Build Asteroids using Python and Pygame

Lesson 4 of 7

Asteroids

A ship with nothing to dodge isn't much of a game. This lesson adds the asteroids themselves, and a screen-wrapping trick that both the ship and asteroids will share.

An Asteroid class

import random
import pygame

class Asteroid:
    def __init__(self, x, y, size=40):
        self.x = x
        self.y = y
        self.size = size
        self.dx = random.uniform(-2, 2)
        self.dy = random.uniform(-2, 2)

    def update(self, width, height):
        self.x = (self.x + self.dx) % width   # wrap around the left/right edges
        self.y = (self.y + self.dy) % height  # wrap around the top/bottom edges

    def draw(self, screen):
        pygame.draw.circle(screen, (160, 160, 160), (int(self.x), int(self.y)), self.size, width=2)

self.x % width is the key trick: Python's modulo operator wraps a value that goes past width back around to 0, and a negative value back around to just under width. Applying it to both x and y every frame gives you the classic Asteroids screen-wrap for free, drift off the right edge, and you reappear on the left.

Spawning a field of them

def spawn_asteroids(count, width, height):
    asteroids = []
    for _ in range(count):
        x = random.uniform(0, width)
        y = random.uniform(0, height)
        asteroids.append(Asteroid(x, y))
    return asteroids

asteroids = spawn_asteroids(5, 800, 600)

Updating and drawing all of them

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # ... player input handling from the previous lesson ...
    player.update()

    for asteroid in asteroids:
        asteroid.update(800, 600)

    screen.fill((10, 10, 30))
    player.draw(screen)
    for asteroid in asteroids:
        asteroid.draw(screen)
    pygame.display.flip()
    clock.tick(60)

Looping over asteroids twice, once to update() and once to draw(), is deliberate: every asteroid's position should be fully updated before any of them are drawn, otherwise you'd occasionally draw a half-updated frame (some asteroids moved, some not yet), causing visible stutter.

NOTE

Right now the ship can fly straight through an asteroid with nothing happening, that's intentional for this lesson. Collision detection comes in a later lesson, once there's also something (a bullet) to collide with.

📝 Asteroids Quiz

Passing score: 70%
  1. 1.What does self.x % width accomplish in Asteroid.update?

  2. 2.The example loop updates every asteroid's position before drawing any of them, rather than updating and drawing one at a time.

  3. 3.random.____(-2, 2) returns a random floating-point number between -2 and 2, used for each asteroid's drift speed.