Dojo Defender: Sessie 6
Vandaag voeg je een eindbaas (boss) toe aan Dojo Defender. De boss verschijnt om de 5 waves en heeft 3 fases met eigen gedrag. Je leert werken met een eigen state machine, HP-balken en schermeffecten.
Wat je vandaag leert
- State machine voor een baas: PHASE_1, PHASE_2, PHASE_3 en EXPLODING
- HP-balk tekenen met kleurverloop (groen → geel → rood)
- Doelzoekende kogels: een kogel die naar de speler beweegt
- Screen shake: willekeurige offset op teken-coördinaten
- Fase-overgangen met visuele effecten
Stap 0: Download de starter
De starter is de oplossing van sessie 5 — Dojo Defender met parallax sterren, particles en splitsende asteroïden.
- Download de starter voor vandaag, pak uit en open
main.py. - Was je er niet bij? Download de oplossing van sessie 5.
- Klik op Run. Het spel werkt zoals voorheen. Dood 10 vijanden om wave 5 te halen — dan verschijnt de boss!
Stap 1: ✅ Basic — Boss-variabelen
Een boss heeft eigen variabelen. Voeg deze toe bij de andere globale variabelen:
boss = None
boss_active = False
boss_hp = 0
boss_hp_max = 50
boss_phase = "PHASE_1"
boss_timer = 0
boss_phase_transition_timer = 0
boss_defeated_timer = 0
boss_shoot_timer = 0
boss_minion_spawn_timer = 0
boss_bullets = []
boss_minions = []
screen_shake_x = 0
screen_shake_y = 0
boss_direction = 1Voeg ook leegmaken van boss_bullets en boss_minions toe in reset_game().
Waarom zoveel variabelen? Een baas is complexer dan gewone vijanden. Hij moet bewegen, schieten, minions spawnen, fases hebben en een dood-animatie. Elke variabele is één aspect van dat gedrag.
Check
Start het spel. Er verandert nog niets — maar de variabelen staan klaar.
Stap 2: ⭐ Stretch — Boss spawnen en tekenen
Spawn-functie
Schrijf een functie die de boss activeert:
def start_boss():
global boss_active, boss, boss_hp, boss_phase, boss_timer
global boss_bullets, boss_minions, boss_shoot_timer
global boss_minion_spawn_timer, boss_direction, screen_shake_x, screen_shake_y
boss_active = True
boss = GameObject('boss.png', WIDTH / 2, -80)
boss_hp = boss_hp_max
boss_phase = "PHASE_1"
boss_timer = 0
boss_shoot_timer = 0
boss_minion_spawn_timer = 0
boss_direction = 1
screen_shake_x = 0
screen_shake_y = 0
boss_bullets.clear()
boss_minions.clear()
roar_path = os.path.join(_DIR, 'sounds/boss_roar.wav')
if os.path.exists(roar_path):
pygame.mixer.Sound(roar_path).play()Boss-wave detectie
Elke 5e wave (vanaf wave 5) verschijnt een boss. Voeg dit toe waar de wave omhoog gaat:
def check_boss_wave():
if wave >= 5 and wave % 5 == 0 and not boss_active:
start_boss()Roep check_boss_wave() aan na wave += 1.
Vijanden stoppen tijdens boss
Zorg dat gewone vijanden niet spawnen tijdens een boss fight. Omcirkel de spawn-logic:
if not boss_active:
spawn_counter += 1
if spawn_counter >= spawn_interval:
spawn_counter = 0
spawn_enemy()Boss tekenen
Teken de boss als hij actief is, en teken ook de HP-balk bovenaan het scherm:
if boss_active and boss is not None:
boss.draw(screen)
# HP-balk
bar_width = 300
bar_height = 20
bar_x = (WIDTH - bar_width) // 2
bar_y = 10
bar_bg = pygame.Rect(bar_x, bar_y, bar_width, bar_height)
pygame.draw.rect(screen, (100, 20, 20), bar_bg)
hp_ratio = max(0, boss_hp / boss_hp_max)
fill_width = int(bar_width * hp_ratio)
if hp_ratio > 0.5:
color = (0, 255, 0)
elif hp_ratio > 0.25:
color = (255, 255, 0)
else:
color = (255, 0, 0)
fill_rect = pygame.Rect(bar_x, bar_y, fill_width, bar_height)
pygame.draw.rect(screen, color, fill_rect)
pygame.draw.rect(screen, (255, 255, 255), bar_bg, 2)
label = font.render(f"BOSS {boss_hp}/{boss_hp_max}", True, (255, 255, 255))
screen.blit(label, (bar_x + bar_width // 2 - label.get_width() // 2, bar_y - 5))Check
Speel tot wave 5. Zie je de boss binnenkomen met een HP-balk? Gewone vijanden spawnen niet meer.
Stap 3: 🔥 Expert — Boss AI (3 fases)
De boss heeft een state machine met 3 fases. Elke fase is anders gedrag.
Fase 1 (HP 34-50, 100-66%)
De boss drijft langzaam naar links en rechts en vuurt af en toe een doelzoekende kogel.
def fire_boss_bullet():
dx = ship.rect.centerx - boss.rect.centerx
dy = ship.rect.centery - boss.rect.centery
dist = math.sqrt(dx * dx + dy * dy)
if dist == 0:
dist = 1
speed = 3
bullet = {
'x': boss.rect.centerx,
'y': boss.rect.centery + 30,
'vx': dx / dist * speed,
'vy': dy / dist * speed,
}
boss_bullets.append(bullet)Wiskunde: Door dx en dy te delen door de afstand (dist) krijg je een eenheidsvector — een richting met lengte 1. Vermenigvuldig met speed voor de gewenste snelheid. Dit werkt in élke richting, niet alleen horizontaal/verticaal.
Voeg de AI-update toe in de game loop (if boss_active and boss is not None):
if boss.rect.centery < 80:
boss.rect.centery += 2 # komt binnen
else:
if boss_phase == "PHASE_1":
boss.rect.centerx += boss_direction * 1.5
if boss.rect.centerx > WIDTH - 60:
boss_direction = -1
if boss.rect.centerx < 60:
boss_direction = 1
boss_shoot_timer += 1
if boss_shoot_timer >= 60:
boss_shoot_timer = 0
fire_boss_bullet()Fase-overgang (HP ≤ 66%)
Check of de baas van fase moet wisselen. Roep dit aan na de boss update:
hp_ratio = boss_hp / boss_hp_max
if hp_ratio <= 0.66 and boss_phase == "PHASE_1":
boss_phase = "PHASE_2"
boss_phase_transition_timer = 20Tijdens een fase-overgang laat je de boss knipperen:
if boss_phase_transition_timer > 0 and boss_phase_transition_timer % 4 < 2:
flash_surf = pygame.Surface(boss.image.get_size(), pygame.SRCALPHA)
flash_surf.fill((255, 255, 255, 180))
screen.blit(flash_surf, (boss.rect.x, boss.rect.y))Fase 2 (HP 17-33, 66-33%)
De boss beweegt sneller, vuurt vaker en spawnt minions:
elif boss_phase == "PHASE_2":
boss.rect.centerx += boss_direction * 3
...
boss_shoot_timer += 1
if boss_shoot_timer >= 40:
boss_shoot_timer = 0
fire_boss_bullet()
boss_minion_spawn_timer += 1
if boss_minion_spawn_timer >= 180:
boss_minion_spawn_timer = 0
spawn_boss_minion()Een minion spawnen:
def spawn_boss_minion():
minion = GameObject('boss_minion.png', random.randint(30, WIDTH - 30), -30)
minion.speed = random.uniform(1, 2)
boss_minions.append(minion)Fase 3 (HP 0-16, 33-0%)
De boss is enraged (woedend): vuurt twee kogels tegelijk, spawn sneller minions, en het scherm schudt:
elif boss_phase == "PHASE_3":
boss.rect.centerx += boss_direction * 4
...
boss_shoot_timer += 1
if boss_shoot_timer >= 20:
boss_shoot_timer = 0
fire_boss_bullet()
if boss_hp < boss_hp_max * 0.15:
fire_boss_bullet()
...Screen shake: voeg een willekeurige offset toe aan ALLE teken-coördinaten:
if boss_phase == "PHASE_3":
screen_shake_x = random.randint(-3, 3)
screen_shake_y = random.randint(-3, 3)
else:
screen_shake_x = 0
screen_shake_y = 0Pas alle draw functies aan om offset_x en offset_y parameters te accepteren. Bijvoorbeeld draw_stars(screen, screen_shake_x, screen_shake_y).
Check
Speel tot wave 5. Zie je de boss door 3 fases gaan? Fase 1: langzaam, af en toe een kogel. Fase 2: sneller + minions. Fase 3: scherm schudt, regen van kogels.
Stap 4: 💀 Expert — Boss verslaan
Als de boss 0 HP bereikt, start de exploding sequence:
if boss_hp <= 0:
boss_phase = "EXPLODING"
boss_defeated_timer = 90
boss_phase_transition_timer = 60
score += 500
explode_sound.play()Tijdens EXPLODING spawn je elke 10 frames een particle-burst:
if boss_phase == "EXPLODING":
if boss_phase_transition_timer > 0 and boss_phase_transition_timer % 10 == 0:
spawn_particles(
boss.rect.centerx + random.randint(-40, 40),
boss.rect.centery + random.randint(-40, 40),
random.randint(10, 20),
[(255, 255, 0), (255, 100, 0), (255, 0, 0), (255, 255, 255)],
(2, 6), (3, 7), (20, 40),
)Toon “BOSS DEFEATED!” bovenaan het scherm:
if boss_defeated_timer > 0:
defeat_msg = big_font.render("BOSS DEFEATED!", True, (255, 255, 0))
screen.blit(defeat_msg, (WIDTH // 2 - defeat_msg.get_width() // 2, HEIGHT // 2 - 100))Let op: zet na de baas de gewone vijand-spawning weer aan. De wave gaat door naar de volgende.
Check
Versla de boss. Zie je de explosie-animatie, de “BOSS DEFEATED!” tekst en +500 score? Gaan de gewone waves daarna verder?
Showcase
Laat aan een coach zien dat:
- Boss verschijnt om de 5 waves met HP-balk
- 3 fases met eigen gedrag (langzaam → minions → enraged)
- Doelzoekende kogels die naar de speler vliegen
- Screen shake in fase 3
- Minions spawnen in fase 2 en 3
- Boss defeated: explosion sequence, “BOSS DEFEATED!”, +500 score
Tot de volgende keer!
“Volgende keer: de gepolijste versie. Geluid, menu-scherm, high scores — en jouw eigen draai aan de game.”
Neem mee naar huis
Probeer thuis één van deze uitbreidingen:
- Makkelijk: verander de boss-kleur of -grootte via de image.
- Middel: voeg een vierde fase toe (HP < 10%) waarin de boss teleporteert.
- Lastig: laat de boss delay-bullets schieten die een spoor achterlaten.
- Erg lastig: voeg een tweede boss-type toe die in een andere wave verschijnt.