What you will learn

This chapter explores two fundamental concepts: the color system (RGB, HSV, transparency) and Pygame's coordinate system. You will learn to generate rainbow colors, understand where the point (0, 0) is located, and display a debug grid to better visualize element positioning.

The concept

The coordinate system

In Pygame, the point (0, 0) is at the top-left corner of the window. The X axis goes right, the Y axis goes down. This is the opposite of what you learned in math class, where Y goes up.

For an 800x600 window:

  • Top-left corner is (0, 0)
  • Top-right corner is (799, 0)
  • Bottom-left corner is (0, 599)
  • Bottom-right corner is (799, 599)

Colors

Pygame supports several ways to define colors:

  • RGB: tuple of 3 integers (red, green, blue) from 0 to 255
  • RGBA: tuple of 4 integers with an alpha channel (transparency) from 0 (invisible) to 255 (opaque)
  • HSV: via pygame.Color, you can define hue, saturation and value

The HSV model is particularly useful for generating gradients: by varying the hue from 0 to 360, you get a complete rainbow.

Step by step

1. Generating rainbow colors

Our rainbow_color function uses Pygame's HSV model. We vary the hue based on the row number to get a uniform color spectrum.

python
def rainbow_color(row, total_rows):
    """Generate a rainbow color based on row position."""
    hue = int(360 * row / total_rows)
    color = pygame.Color(0)
    color.hsva = (hue, 100, 100, 100)
    return (color.r, color.g, color.b)

pygame.Color is a powerful object. Its hsva property accepts a tuple (hue, saturation, value, alpha). Hue ranges from 0 to 360 (color wheel), saturation and value from 0 to 100. We then retrieve the r, g, b components to get a standard RGB tuple.

2. Checkerboard effect on bricks

To add visual depth, we alternate brick brightness based on the parity of (row + col). It is a simple but effective checkerboard effect.

python
for row in range(BRICK_ROWS):
    for col in range(BRICK_COLS):
        x = BRICK_OFFSET_X + col * (BRICK_WIDTH + BRICK_PADDING)
        y = BRICK_OFFSET_Y + row * (BRICK_HEIGHT + BRICK_PADDING)
        color = rainbow_color(row, BRICK_ROWS)
        # Alternate brightness for checkerboard effect
        if (row + col) % 2 == 0:
            color = tuple(min(255, c + 30) for c in color)
        pygame.draw.rect(screen, color, (x, y, BRICK_WIDTH, BRICK_HEIGHT))
        pygame.draw.rect(screen, (255, 255, 255),
                         (x, y, BRICK_WIDTH, BRICK_HEIGHT), 1)

The second draw.rect call with width=1 draws a thin white outline around each brick, visually separating them.

3. Coordinate grid

To debug element placement, we add a grid that toggles with the G key. It draws lines every 50 pixels.

python
show_grid = False

# In the event loop:
if event.key == pygame.K_g:
    show_grid = not show_grid

# In the draw section:
if show_grid:
    for gx in range(0, SCREEN_WIDTH, 50):
        pygame.draw.line(screen, (50, 50, 70), (gx, 0), (gx, SCREEN_HEIGHT))
    for gy in range(0, SCREEN_HEIGHT, 50):
        pygame.draw.line(screen, (50, 50, 70), (0, gy), (SCREEN_WIDTH, gy))

4. Displaying mouse coordinates

To better understand the coordinate system, we continuously display the mouse position. We use pygame.mouse.get_pos() and text rendering (which we will cover in detail in chapter 8).

python
mouse_x, mouse_y = pygame.mouse.get_pos()
font = pygame.font.SysFont("monospace", 14)
coord_text = font.render(f"({mouse_x}, {mouse_y})", True, (150, 150, 150))
screen.blit(coord_text, (10, SCREEN_HEIGHT - 25))

Key takeaways

Tip: pygame.Color also supports English color names: pygame.Color("red"), pygame.Color("dodgerblue"). Handy for quick prototyping.

Common pitfall: the alpha channel (RGBA) does not work directly with pygame.draw on the main surface. To use transparency, you must draw on an intermediate surface with SRCALPHA, then copy it to the screen with blit(). We will cover this in chapter 5.

Pygame functions used:

  • pygame.Color: color object with RGB, RGBA, HSV support
  • color.hsva: set/get color in Hue-Saturation-Value
  • pygame.mouse.get_pos(): current cursor position
  • pygame.font.SysFont(): load a system font
  • font.render(): convert text to a Surface

Complete code

python
"""Brick Breaker - Chapter 3: Colors and Coordinates"""
import pygame
import sys

pygame.init()

SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
FPS = 60
BG_COLOR = (20, 20, 40)

PADDLE_WIDTH = 100
PADDLE_HEIGHT = 15
PADDLE_COLOR = (200, 200, 200)
paddle_x = SCREEN_WIDTH // 2 - PADDLE_WIDTH // 2
paddle_y = SCREEN_HEIGHT - 40

BALL_RADIUS = 8
BALL_COLOR = (255, 255, 255)
ball_x = SCREEN_WIDTH // 2
ball_y = SCREEN_HEIGHT // 2

BRICK_ROWS = 6
BRICK_COLS = 10
BRICK_WIDTH = 70
BRICK_HEIGHT = 20
BRICK_PADDING = 5
BRICK_OFFSET_X = 30
BRICK_OFFSET_Y = 50


def rainbow_color(row, total_rows):
    """Generate a rainbow color based on row position."""
    hue = int(360 * row / total_rows)
    color = pygame.Color(0)
    color.hsva = (hue, 100, 100, 100)
    return (color.r, color.g, color.b)


screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Brick Breaker - Chapter 3")
clock = pygame.time.Clock()
font = pygame.font.SysFont("monospace", 14)
show_grid = False

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                running = False
            if event.key == pygame.K_g:
                show_grid = not show_grid

    mouse_x, mouse_y = pygame.mouse.get_pos()

    screen.fill(BG_COLOR)

    # Draw bricks with rainbow colors and slight alpha variation
    for row in range(BRICK_ROWS):
        for col in range(BRICK_COLS):
            x = BRICK_OFFSET_X + col * (BRICK_WIDTH + BRICK_PADDING)
            y = BRICK_OFFSET_Y + row * (BRICK_HEIGHT + BRICK_PADDING)
            color = rainbow_color(row, BRICK_ROWS)
            # Alternate brightness for checkerboard effect
            if (row + col) % 2 == 0:
                color = tuple(min(255, c + 30) for c in color)
            pygame.draw.rect(screen, color, (x, y, BRICK_WIDTH, BRICK_HEIGHT))
            pygame.draw.rect(screen, (255, 255, 255),
                             (x, y, BRICK_WIDTH, BRICK_HEIGHT), 1)

    # Draw paddle
    pygame.draw.rect(screen, PADDLE_COLOR,
                     (paddle_x, paddle_y, PADDLE_WIDTH, PADDLE_HEIGHT))

    # Draw ball
    pygame.draw.circle(screen, BALL_COLOR, (ball_x, ball_y), BALL_RADIUS)

    # Show grid overlay
    if show_grid:
        for gx in range(0, SCREEN_WIDTH, 50):
            pygame.draw.line(screen, (50, 50, 70),
                             (gx, 0), (gx, SCREEN_HEIGHT))
        for gy in range(0, SCREEN_HEIGHT, 50):
            pygame.draw.line(screen, (50, 50, 70),
                             (0, gy), (SCREEN_WIDTH, gy))

    # Show coordinates
    coord_text = font.render(f"({mouse_x}, {mouse_y})", True, (150, 150, 150))
    screen.blit(coord_text, (10, SCREEN_HEIGHT - 25))

    # Show instructions
    grid_text = font.render("[G] Toggle grid  [ESC] Quit", True, (100, 100, 100))
    screen.blit(grid_text, (SCREEN_WIDTH - 280, SCREEN_HEIGHT - 25))

    pygame.display.flip()
    clock.tick(FPS)

pygame.quit()
sys.exit()

Download

Download the source code for this chapter: brick-breaker-ch03.zip