Dojo Defender: Session 1
Your spaceship has landed in dark space. Time to bring it to life. In this session you add a ship you can steer and you make asteroids fall down. Dodge them or lose a life. How long do you survive?
What you’ll learn today
- Using lists to keep track of multiple asteroids
- Spawning and removing objects
- Detecting collisions with
colliderect - Keeping track of lives and showing an explosion effect
- Forcing game over when your lives run out
Step 0: Setup
- Open Thonny.
- Download the Dojo Defender starter, unpack the ZIP and open
main.py. - Click Run. You see a dark blue screen with your spaceship at the bottom. You can already move it with the arrow keys.
- Problems? Take a look at Installing Thonny.
Step 1: Let asteroids spawn
A game without danger is boring. Add a list in which you keep track of all the asteroids, and a function that creates new asteroids:
# at the top, after WIDTH and HEIGHT:
asteroids = []
def spawn_asteroid():
rock = Actor('asteroid_big')
rock.x = random.randint(30, WIDTH - 30)
rock.y = -30
rock.speed = random.uniform(3, 5)
asteroids.append(rock)You also need import random at the top of the file. Add that line.
Call the function in update() with a counter so that an asteroid does not appear every single frame:
# at the top, outside the functions:
timer = 0
def update():
global timer
timer += 1
if timer % 60 == 0: # every second (60 frames)
spawn_asteroid()Put this before the movement code of the ship.
Step 2: ✅ Basic: dropping and drawing asteroids
Asteroids have to fall down and disappear when they are off the screen:
def update():
global timer
timer += 1
if timer % 60 == 0:
spawn_asteroid()
for rock in asteroids[:]: # [:] = copy so you can remove safely
rock.y += rock.speed
if rock.y > HEIGHT + 30:
asteroids.remove(rock)Don’t forget to draw the asteroids in draw():
def draw():
screen.fill((10, 10, 30))
for rock in asteroids:
rock.draw()
ship.draw()Step 3: ⭐ Stretch: collision + lives
When an asteroid hits your ship, you lose a life. Add lives and game_over at the top:
lives = 3
game_over = FalseAdd collision detection in the for loop:
for rock in asteroids[:]:
rock.y += rock.speed
if rock.y > HEIGHT + 30:
asteroids.remove(rock)
elif rock.colliderect(ship):
asteroids.remove(rock)
global lives, game_over
lives -= 1
if lives <= 0:
game_over = TrueShow the lives and the score in draw():
screen.draw.text(f"Score: {score}", (10, 10), color="white", fontsize=24)
screen.draw.text(f"Lives: {lives}", (10, 40), color="red", fontsize=24)Stop the game when game_over is True:
def update():
if game_over:
return
...Show a game-over screen in draw():
if game_over:
screen.draw.text(
"GAME OVER",
center=(WIDTH / 2, HEIGHT / 2),
color="red",
fontsize=60,
)
screen.draw.text(
f"Final Score: {score}",
center=(WIDTH / 2, HEIGHT / 2 + 60),
color="white",
fontsize=30,
)Step 4: 🔥 Expert: explosions + invisibility
When an asteroid hits you, you want to see an explosion instead of the ship just disappearing. Use the four explosion frames (explosion_1 to explosion_4):
explosion = Actor('explosion_1')
explosion_active = False
explosion_frame = 0
explosion_timer = 0On a collision: hide the ship, start the explosion:
elif rock.colliderect(ship):
asteroids.remove(rock)
global lives, game_over, explosion_active, explosion_frame, explosion_timer
lives -= 1
explosion.pos = (ship.x, ship.y)
explosion.image = 'explosion_1'
explosion_frame = 1
explosion_timer = 5
explosion_active = True
ship.visible = False
if lives <= 0:
game_over = TruePlay the explosion in update():
if explosion_active:
explosion_timer -= 1
if explosion_timer <= 0:
explosion_frame += 1
if explosion_frame > 4:
explosion_active = False
ship.visible = True
ship.pos = (WIDTH / 2, HEIGHT - 50)
else:
explosion.image = f'explosion_{explosion_frame}'
explosion_timer = 5Then add an invisibility period so that you don’t get hit again straight away:
invincible = False
invincible_timer = 0In update():
if invincible:
invincible_timer -= 1
if invincible_timer <= 0:
invincible = False
ship.visible = TrueAdjust the collision check:
elif rock.colliderect(ship) and not invincible and ship.visible:Draw the explosion in draw():
if explosion_active:
explosion.draw()Showcase
Show your Dojo Defender to a coach and a buddy. How long do you survive? Do you have explosions? Does it look cool?
See you next time!
“Next time: your spaceship SHOOTS BACK. Enemies in formation, and you blast them away with lasers. This is going to be epic.”
Take it home
Try one of these extensions at home:
- Sound — Add
sounds/explosion.wavwhen an asteroid explodes (sounds.explosion.play()). - Harder — Make asteroids fall faster as the score rises:
rock.speed = random.uniform(3 + score // 10, 5 + score // 10). - Power-up — Add a rare green power-up that gives an extra life.
- Smaller — Use
asteroid_medorasteroid_smallfor variation in size and speed.