Dojo Defender: Session 2
Your ship has a weapon now. Enemies are coming. Shoot them down before they reach the bottom. Every 10 enemies means a new wave — and it gets harder and harder.
What you’ll learn today
- Shooting with the space bar and a cooldown
- Lists for bullets and enemies
- Collision between bullets and enemies
- Waves that get harder and harder
- Enemies that make sine movements
Step 0: Open the starter
- Open Thonny.
- Download the Dojo Defender starter for today, unpack the ZIP and open
main.py. - Were you not there last time? Download the solution from session 1 and start with that.
- Click Run. You see the same game as last time: ship, asteroids, explosions.
- Problems? Take a look at Installing Thonny.
Step 1: Shooting with SPACE
Your ship has a laser cannon. Add a list for bullets at the top:
bullets = []Use on_key_down — it is called only once when you press a key (unlike update, which keeps repeating):
def on_key_down(key):
if key == keys.SPACE:
bullet = Actor('bullet')
bullet.x = ship.x
bullet.y = ship.y - 20
bullets.append(bullet)Make the bullets move up in update() and remove them when they are off screen:
for bullet in bullets[:]:
bullet.y -= 8
if bullet.y < -10:
bullets.remove(bullet)Don’t forget to draw the bullets in draw():
for bullet in bullets:
bullet.draw()Step 2: ✅ Basic: shooting cooldown
Without a cooldown you fire dozens of bullets per second. Add a timer:
shoot_cooldown = 0In update():
if shoot_cooldown > 0:
shoot_cooldown -= 1Adjust on_key_down so that you can only shoot when the cooldown is 0:
def on_key_down(key):
global shoot_cooldown
if key == keys.SPACE and shoot_cooldown == 0:
bullet = Actor('bullet')
bullet.x = ship.x
bullet.y = ship.y - 20
bullets.append(bullet)
shoot_cooldown = 15Step 3: ⭐ Stretch: adding enemies
Enemies spawn at the top and fall down. Add a list:
enemies = []A spawn function:
def spawn_enemy():
enemy = Actor('enemy_drone')
enemy.x = random.randint(30, WIDTH - 30)
enemy.y = -30
enemy.speed = random.uniform(1, 3)
enemies.append(enemy)Call spawn_enemy() in update() with a spawn interval:
# at the top:
spawn_counter = 0
spawn_interval = 120
# in update():
spawn_counter += 1
if spawn_counter >= spawn_interval:
spawn_counter = 0
spawn_enemy()Draw enemies in draw():
for enemy in enemies:
enemy.draw()Step 4: 🔥 Expert: collision + waves
Bullet hits enemy: remove both, +10 points:
for bullet in bullets[:]:
bullet.y -= 8
if bullet.y < -10:
bullets.remove(bullet)
continue
for enemy in enemies[:]:
if bullet.colliderect(enemy):
bullets.remove(bullet)
enemies.remove(enemy)
global score
score += 10
breakEnemy hits the bottom or the ship: lose a life (same as an asteroid).
Waves: a new wave every 10 enemies. Enemies spawn faster:
# at the top:
wave = 1
enemies_destroyed = 0
# on destroy:
enemies_destroyed += 1
if enemies_destroyed % 10 == 0:
wave += 1
spawn_interval = max(60, spawn_interval - 10)Show the current wave in draw():
screen.draw.text(f"Wave: {wave}", (10, 70), color="yellow", fontsize=24)Show “WAVE N!” in big letters on a new wave (use a timer).
Sine movement: let enemies veer left and right:
import math # at the top
enemy.x = enemy.start_x + math.sin(timer * 0.03 + enemy.strafe_offset) * 50Showcase
Show your Dojo Defender to a coach. How many waves do you reach? Do you have the sine movement working? Compare scores with a buddy.
See you next time!
“Next time: POWER-UPS. Shields, speed boosts and spread shots. Your ship gets stronger the longer you survive.”
Take it home
Try one of these extensions at home:
- Enemy variation — Use
enemy_charger.pngfor a faster enemy that doesn’t do a sine, but comes straight at you. - Sound — Add
sounds/laser.wavandsounds/explosion.wav. - Hard mode — Enemies spawn faster and faster, up to every 30 frames at most.
- Score multiplier — The higher the wave, the more points per kill:
score += 10 * wave.