Dojo Defender: Session 3
Today Dojo Defender moves from pgzero to real pygame. No more pgzrun.go() — you write the game loop yourself. The game stays the same, but the engine underneath is different. And at the end you add SOUND.
What you’ll learn today
- The difference between pgzero and raw pygame
- Writing the game loop yourself:
while running: clock.tick(60) - Converting actors to
Surface+Rect - Loading and playing sound with
pygame.mixer - Looping background music
Step 0: Download the starter
Today’s starter is a full port of Dojo Defender Session 2 to raw pygame. No more pgzero, but it plays the same.
- Download the Dojo Defender starter for today, unpack it and open
main.py. - Were you not there last time? Download the solution from session 2 and try to port it.
- Click Run. The game works the way you’re used to: ship, asteroids, bullets, enemies, waves, explosions.
What is different?
In pgzero you wrote draw() and update() and pgzrun did the rest. In raw pygame you do everything yourself:
pgzero (before):
import pgzrun
def draw():
screen.fill((10, 10, 30))
ship.draw()
def update():
if keyboard.left:
ship.x -= 5
pgzrun.go()raw pygame (now):
import pygame, sys
pygame.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
running = True
while running:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit(); sys.exit()
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
ship.rect.centerx -= 5
screen.fill((10, 10, 30))
ship.draw(screen)
pygame.display.flip()The most important differences:
| pgzero | raw pygame |
|---|---|
Actor('plaatje') | GameObject('plaatje.png', x, y) |
keyboard.left | keys[pygame.K_LEFT] |
on_key_down(key) | event.type == pygame.KEYDOWN |
screen.draw.text(...) | font.render() + screen.blit() |
rock.colliderect(ship) | rock.rect.colliderect(ship.rect) |
pgzrun.go() | while running: ... clock.tick(60) |
Step 1: Explore the code
Open main.py and compare it with the old pgzero code of session 2. Look for these parts:
- Game loop — Look for the
while running:loop. Do you seeclock.tick(60),pygame.display.flip()? - GameObject — Look for the
class GameObject. How is an image loaded? What isrect? - Input — Look for
pygame.key.get_pressed()andpygame.KEYDOWN. How does the space bar work?
Change the WIDTH to 1024. What happens? (Everything gets wider — the game still works.)
Step 2: ✅ Basic — Add restart with the R key
In the pgzero version you could not restart after game over. Add that now:
# in the event loop, on KEYDOWN:
if event.key == pygame.K_r and game_over:
# reset everything
ship.rect.center = (WIDTH / 2, HEIGHT - 50)
ship.visible = True
asteroids.clear()
bullets.clear()
enemies.clear()
lives = 3
score = 0
wave = 1
enemies_destroyed = 0
spawn_counter = 0
spawn_interval = 120
wave_display_timer = 0
game_over = False
invincible = False
ship.visible = TrueAlso show a hint on the game-over screen: press R to play again.
Step 3: ⭐ Stretch — Add sound
The starter has no sound yet. Add sounds!
Make a sounds/ folder next to images/ and copy the sound files from dojo-defender-assets/sounds/ (ask the coach). Or download the solution, which already contains them.
Add at the top:
pygame.mixer.init() # after pygame.init()
shoot_sound = pygame.mixer.Sound('sounds/shoot.wav')
explode_sound = pygame.mixer.Sound('sounds/explode.wav')
hit_sound = pygame.mixer.Sound('sounds/hit.wav')
pygame.mixer.music.load('sounds/bg_music.wav')
pygame.mixer.music.play(-1) # -1 = keep repeatingWatch out: the path has to be right. Use os.path.join(_DIR, 'sounds/shoot.wav') like the starter also does with images.
Play the sounds at the right moments:
shoot_sound.play()— when you shootexplode_sound.play()— when an enemy or asteroid explodeshit_sound.play()— when your ship gets hit
Step 4: 🔥 Expert — Mute button and volume
Add an M key that turns the sound on and off:
muted = False
# in KEYDOWN:
if event.key == pygame.K_m:
muted = not muted
pygame.mixer.music.set_volume(0 if muted else 1)Every sound then has to be checked:
if not muted:
shoot_sound.play()Volume per sound: Make the shooting sound quieter than the explosion:
shoot_sound.set_volume(0.3)
explode_sound.set_volume(0.7)Showcase
Show a coach that:
- Your game works in real pygame without pgzero
- You added sound (shoot, explode, hit, bg music)
- Bonus: restart with R or mute with M
See you next time!
“Next time: POWER-UPS. Shields, speed boosts and spread shots. And your ship gets stronger the longer you survive.”
Take it home
Try one of these extensions at home:
- Health bar — Replace the text “Lives: 3” with a visual bar: draw three small rectangles at the top.
- Pause — Add a P key that pauses the game. Setting
running = Falseis not enough — use a separatepausedvariable. - Particle effect — Let small white dots splatter around on an explosion (use a list of particles that get smaller and smaller).
- Your own skin — Replace
player_ship.pngwith your own design.