42  The Artist’s Tools: Drawing Primitives and Shapes

In our previous lessons, we’ve set up Pyxel and learned about colors and coordinates. Today, we’ll explore the fundamental building blocks of any game’s visuals: drawing primitives and shapes. Much like a painter needs brushes, pens, and various tools to create art, a game developer needs different drawing functions to create game elements.

42.1 What are Drawing Primitives?

Drawing primitives are the basic shapes and elements that can be combined to create complex images. In Pyxel, these include points, lines, rectangles, circles, triangles, and text. These simple shapes are the foundation of everything you’ll draw in your games - from characters and obstacles to user interfaces and backgrounds.

Let’s explore each of these magical drawing tools and learn how to wield them effectively!

42.2 The Point: The Smallest Unit of Art

A point is the simplest drawing primitive - just a single pixel on the screen. In Pyxel, we use the pset() function to draw a point:

pyxel.pset(x, y, col)
  • x and y are the coordinates where you want to draw the point
  • col is the color number (0-15)

Let’s create a simple example that draws a few stars in the night sky:

import pyxel

class PointsExample:
    def __init__(self):
        pyxel.init(160, 120, title="Drawing Points")
        pyxel.run(self.update, self.draw)
    
    def update(self):
        if pyxel.btnp(pyxel.KEY_Q):
            pyxel.quit()
    
    def draw(self):
        pyxel.cls(1)  # Clear screen with dark blue
        
        # Draw some stars as individual points
        pyxel.pset(20, 20, 7)  # White star
        pyxel.pset(40, 15, 7)
        pyxel.pset(60, 25, 7)
        pyxel.pset(100, 10, 7)
        pyxel.pset(120, 30, 7)
        
        pyxel.text(5, 5, "Stars drawn with pset()", 7)

PointsExample()

This simple example creates a dark blue background with five white stars drawn as individual points.

42.3 The Line: Connecting the Dots

Lines allow us to connect two points. In Pyxel, we use the line() function:

pyxel.line(x1, y1, x2, y2, col)
  • x1 and y1 are the coordinates of the starting point
  • x2 and y2 are the coordinates of the ending point
  • col is the color number

Let’s create a simple house outline with lines:

import pyxel

class LineExample:
    def __init__(self):
        pyxel.init(160, 120, title="Drawing Lines")
        pyxel.run(self.update, self.draw)
    
    def update(self):
        if pyxel.btnp(pyxel.KEY_Q):
            pyxel.quit()
    
    def draw(self):
        pyxel.cls(1)  # Clear screen with dark blue
        
        # Draw a house outline with lines
        # Base of the house
        pyxel.line(40, 80, 80, 80, 7)  # Bottom
        pyxel.line(40, 80, 40, 50, 7)  # Left wall
        pyxel.line(80, 80, 80, 50, 7)  # Right wall
        
        # Roof
        pyxel.line(40, 50, 60, 30, 7)  # Left roof
        pyxel.line(60, 30, 80, 50, 7)  # Right roof
        
        # Door
        pyxel.line(55, 80, 55, 65, 7)  # Left of door
        pyxel.line(65, 80, 65, 65, 7)  # Right of door
        pyxel.line(55, 65, 65, 65, 7)  # Top of door
        
        pyxel.text(5, 5, "House drawn with line()", 7)

This code creates a simple house outline using lines to connect various points.

42.4 The Rectangle: Building Blocks of Games

Rectangles are perhaps the most commonly used shape in games. They’re perfect for buildings, platforms, buttons, and more. Pyxel offers two rectangle functions:

  • rect(): Draws a filled rectangle
  • rectb(): Draws just the outline of a rectangle
pyxel.rect(x, y, w, h, col)   # Filled rectangle
pyxel.rectb(x, y, w, h, col)  # Rectangle outline
  • x and y are the coordinates of the top-left corner
  • w and h are the width and height of the rectangle
  • col is the color number

Let’s draw some rectangles:

import pyxel

class RectangleExample:
    def __init__(self):
        pyxel.init(160, 120, title="Drawing Rectangles")
        pyxel.run(self.update, self.draw)
    
    def update(self):
        if pyxel.btnp(pyxel.KEY_Q):
            pyxel.quit()
    
    def draw(self):
        pyxel.cls(1)  # Clear screen with dark blue
        
        # Draw a filled rectangle (building)
        pyxel.rect(40, 40, 80, 70, 5)  # Gray building
        
        # Draw rectangle outlines (windows)
        pyxel.rectb(50, 50, 15, 15, 7)  # White window outline
        pyxel.rectb(95, 50, 15, 15, 7)  # White window outline
        pyxel.rectb(50, 75, 15, 15, 7)  # White window outline
        pyxel.rectb(95, 75, 15, 15, 7)  # White window outline
        
        # Draw a filled rectangle (door)
        pyxel.rect(75, 80, 10, 30, 4)  # Brown door
        
        pyxel.text(5, 5, "Building drawn with rect() and rectb()", 7)

This code creates a building using a filled rectangle, with door and windows drawn using a combination of filled and outlined rectangles.

42.5 The Circle: Perfect Rounds

Circles are ideal for many game elements like balls, planets, and coins. Pyxel offers two circle functions:

  • circ(): Draws a filled circle
  • circb(): Draws just the outline of a circle
pyxel.circ(x, y, r, col)   # Filled circle
pyxel.circb(x, y, r, col)  # Circle outline
  • x and y are the coordinates of the center of the circle
  • r is the radius of the circle
  • col is the color number

Let’s create a simple solar system with circles:

import pyxel

class CircleExample:
    def __init__(self):
        pyxel.init(160, 120, title="Drawing Circles")
        pyxel.run(self.update, self.draw)
    
    def update(self):
        if pyxel.btnp(pyxel.KEY_Q):
            pyxel.quit()
    
    def draw(self):
        pyxel.cls(1)  # Clear screen with dark blue
        
        # Draw the sun (filled circle)
        pyxel.circ(80, 60, 15, 10)  # Yellow sun
        
        # Draw planet orbits (circle outlines)
        pyxel.circb(80, 60, 30, 13)  # Light blue orbit
        pyxel.circb(80, 60, 50, 13)  # Light blue orbit
        
        # Draw planets (filled circles)
        pyxel.circ(80, 30, 5, 11)  # Green planet
        pyxel.circ(130, 60, 8, 8)  # Red planet
        
        pyxel.text(5, 5, "Solar system drawn with circ() and circb()", 7)

This code creates a simple solar system with a sun, planets, and orbits, all using circles.

42.6 The Triangle: Adding Dimension

Triangles are versatile shapes that can be used for a variety of game elements, from mountain ranges to decoration. Pyxel offers two triangle functions:

  • tri(): Draws a filled triangle
  • trib(): Draws just the outline of a triangle
pyxel.tri(x1, y1, x2, y2, x3, y3, col)   # Filled triangle
pyxel.trib(x1, y1, x2, y2, x3, y3, col)  # Triangle outline
  • x1, y1, x2, y2, x3, y3 are the coordinates of the three corners of the triangle
  • col is the color number

Let’s draw some mountains with triangles:

import pyxel

class TriangleExample:
    def __init__(self):
        pyxel.init(160, 120, title="Drawing Triangles")
        pyxel.run(self.update, self.draw)
    
    def update(self):
        if pyxel.btnp(pyxel.KEY_Q):
            pyxel.quit()
    
    def draw(self):
        pyxel.cls(1)  # Clear screen with dark blue
        
        # Draw a sky
        pyxel.rect(0, 0, 160, 80, 12)  # Light blue sky
        
        # Draw mountains with filled triangles
        pyxel.tri(0, 80, 50, 30, 100, 80, 13)  # Gray mountain
        pyxel.tri(80, 80, 130, 25, 160, 80, 13)  # Gray mountain
        
        # Draw a simple pine tree with a triangle and rectangle
        pyxel.rect(70, 80, 6, 10, 4)  # Brown trunk
        pyxel.tri(63, 80, 73, 60, 83, 80, 3)  # Green triangle for leaves
        
        pyxel.text(5, 5, "Mountains drawn with tri()", 7)

This code creates a simple mountain landscape using triangles, with a sky drawn as a rectangle and a small pine tree made from a rectangle (trunk) and a triangle (foliage).

42.7 Text: The Power of Words

Text is essential for displaying information to the player, such as scores, instructions, or dialog. In Pyxel, we use the text() function:

pyxel.text(x, y, text, col)
  • x and y are the coordinates of the top-left corner of the text
  • text is the string to display
  • col is the color number

Let’s explore different ways to use text:

import pyxel

class TextExample:
    def __init__(self):
        pyxel.init(160, 120, title="Drawing Text")
        self.score = 12550
        pyxel.run(self.update, self.draw)
    
    def update(self):
        if pyxel.btnp(pyxel.KEY_Q):
            pyxel.quit()
    
    def draw(self):
        pyxel.cls(1)  # Clear screen with dark blue
        
        # Draw a simple title
        pyxel.text(40, 10, "SPACE ADVENTURE", 7)
        
        # Draw game stats with different colors
        pyxel.text(10, 30, "SCORE:", 7)
        pyxel.text(50, 30, str(self.score), 10)  # Yellow for the score
        
        pyxel.text(10, 40, "LEVEL:", 7)
        pyxel.text(50, 40, "5", 11)  # Green for the level
        
        pyxel.text(10, 50, "LIVES:", 7)
        pyxel.text(50, 50, "3", 8)  # Red for lives
        
        # Draw instructions
        pyxel.text(10, 100, "Press Z to shoot", 13)
        pyxel.text(10, 110, "Press Q to quit", 13)

This example shows how to display different types of text on the screen with various colors.

42.8 Creating a Simple UI with Shapes and Text

Now, let’s combine what we’ve learned to create a simple game UI that shows health, score, and a mini-map:

import pyxel

class GameUI:
    def __init__(self):
        pyxel.init(160, 120, title="Game UI Example")
        self.health = 70  # Percentage
        self.score = 3500
        pyxel.run(self.update, self.draw)
    
    def update(self):
        if pyxel.btnp(pyxel.KEY_Q):
            pyxel.quit()
    
    def draw(self):
        pyxel.cls(1)  # Clear screen with dark blue
        
        # Draw game title
        pyxel.text(60, 5, "DUNGEON QUEST", 7)
        
        # Draw health bar
        pyxel.text(10, 20, "HEALTH:", 7)
        pyxel.rectb(60, 20, 52, 7, 7)  # Health bar outline
        pyxel.rect(61, 21, int(self.health / 2), 5, 8)  # Red health bar
        
        # Draw score
        pyxel.text(10, 35, "SCORE:", 7)
        pyxel.text(60, 35, str(self.score), 10)
        
        # Draw a mini-map in the corner
        pyxel.rectb(116, 10, 40, 40, 7)  # Mini-map border
        
        # Draw some elements on the mini-map
        pyxel.rect(125, 25, 4, 4, 11)  # Player position (green)
        pyxel.circ(140, 20, 2, 8)  # Enemy position (red)
        pyxel.pset(120, 30, 10)  # Item position (yellow)
        
        # Draw instructions
        pyxel.text(10, 100, "Use arrow keys to play", 13)
        pyxel.text(10, 110, "Q: Quit", 13)

This example demonstrates how to create a simple game UI using various drawing primitives together.

42.9 Practice Time: Your Drawing Primitive Quest

Now it’s your turn to create a Pyxel application using the drawing primitives you’ve learned. Complete these challenges:

  1. Create a scene that uses at least one of each drawing primitive: point, line, rectangle, circle, triangle, and text.

  2. Include at least three different colors in your scene.

  3. Use at least one filled shape and one outline shape.

Here’s a starting point for your quest:

import pyxel

class MyDrawingApp:
    def __init__(self):
        pyxel.init(160, 120, title="My Drawing App")
        pyxel.run(self.update, self.draw)
    
    def update(self):
        if pyxel.btnp(pyxel.KEY_Q):
            pyxel.quit()
    
    def draw(self):
        pyxel.cls(0)  # Clear screen with black
        
        # Draw your scene using different primitives
        # Use at least one point (pset)
        # Use at least one line
        # Use at least one rectangle (rect or rectb)
        # Use at least one circle (circ or circb)
        # Use at least one triangle (tri or trib)
        # Use at least one text element
        
        # Don't forget to use different colors!

42.10 Common Bugs to Watch Out For

As you experiment with drawing primitives in Pyxel, watch out for these common issues:

  1. Coordinate System: Remember that the origin (0,0) is at the top-left corner, with y-values increasing downward. This can be confusing if you’re used to other coordinate systems.

  2. Coordinate Order: For shapes with multiple points (lines, triangles), make sure you’re providing the coordinates in the correct order.

  3. Off-by-one Errors: When drawing shapes, remember that the specified coordinates are for the top-left corner (for rectangles) or exact points (for lines and triangles), not the center.

  4. Missing Parameters: Each drawing function requires a specific number of parameters. Be sure to provide all of them, including the color.

  5. Drawing Order: Elements are drawn in the order your code executes them. If something appears “behind” another element when it should be in front, try changing the order of your drawing commands.

  6. Text Positioning: Text is drawn from the top-left corner. If text appears cut off, make sure it has enough space to be displayed.

  7. Color Numbers: Pyxel uses color numbers 0-15. If you provide a color number outside this range, it will be wrapped around (e.g., color 16 becomes color 0).

42.11 Conclusion and Resources for Further Mastery

You’ve now mastered the fundamental drawing primitives in Pyxel. With these tools, you can create virtually any 2D visual you might need for your games, from simple shapes to complex scenes.

To further enhance your artistic skills in game development, check out these resources:

  1. Pyxel Drawing Functions Documentation - Detailed information about all drawing functions in Pyxel.

  2. Pixel Art Techniques - Learn techniques for creating effective pixel art, which pairs perfectly with Pyxel’s retro aesthetic.

  3. Game Art Tips for Beginners - Tips for creating effective game art, even if you’re not an artist.

  4. Retro Game Graphics Guide - A guide to creating retro-style game graphics.

In our next lesson, we’ll explore loading and using sprites/images in Pyxel, which will allow us to create even more sophisticated and detailed visuals for our games. Keep practicing with drawing primitives – they’ll form the foundation of your game development toolkit!