CoderDojo: Python & Pygame

Welkom! Dit is het curriculum voor ninja’s tussen 10 en 17 jaar die Python en Pygame willen leren door games te maken.

Hoe werkt het?

  • 10 sessies van 3 uur, ongeveer eenmaal per maand (juli en augustus uitgezonderd).
  • Elke sessie maak je een complete kleine game in 3 uur.
  • Mis je een sessie? Geen probleem: je kunt altijd instappen.
  • Geniet, experimenteer, en vraag aan een coach als je vastloopt.

Dojo Defender, voor de regelmatige ninja’s

Voor wie 4 sessies of meer volgt: we bouwen samen een huisgame, Dojo Defender, die elke maand groeit. Kom je maar af en toe? Doe gewoon de sessie-game van die dag. Die is altijd compleet op zich.

Voor coaches

Klik op een sessie voor coach-notities, de afdrukbare cheatsheet, en de startcode.

13 juni 2026, 13:30

Subsecties van CoderDojo: Python & Pygame

Installeren

Om een Python-game te maken, heb je twee dingen nodig:

  1. Python: de programmeertaal die jouw code uitvoert. De computer begrijpt Python, niet Nederlands of Engels.
  2. Een editor: het programma waar je jouw code intypt. Zoals Word, maar dan voor code.

Heb je nog nooit geprogrammeerd? Begin met Thonny, want dat installeert Python automatisch mee en is speciaal gemaakt voor beginners.

Kies je editor

  • Thonny: ★☆☆ Makkelijk. Voor beginners. Installeert Python automatisch. Dit is het startpunt voor alle CoderDojo-sessies.
  • VS Code: ★★☆ Gemiddeld. De editor die professionele programmeurs gebruiken. Goed als Thonny te klein voelt en je meerdere bestanden wil beheren.
  • PyCharm: ★★★ Gevorderd. Een volledige IDE met ingebouwde debugger en slimme code-hulp. Geschikt als je al een tijdje programmeert.

Daarna: virtuele omgeving

Heb je een editor gekozen? Lees dan ook hoe virtuele omgevingen werken. Dat is de “rugzak” van elk Python-project — een aparte map met precies de juiste pakketten voor jouw spel.

Lukt het niet? Kijk bij Problemen oplossen. Daar staan de meest voorkomende installatiefouten met oplossingen.

26 juni 2026, 13:43

Subsecties van Installeren

Thonny + Pgzero Installeren

Snel installeren

Download het script voor jouw besturingssysteem. Eén dubbelklik en het installeert Python, uv, Thonny, pgzero, pygame en alle startercode voor je. Klaar in 5-10 minuten.

Je besturingssysteem wordt gedetecteerd...

Toon alle downloads

Tip

Het script is veilig: het installeert alleen dezelfde programma’s als de handmatige stappen hieronder. Je kunt de broncode bekijken door het bestand te openen in Kladblok of een teksteditor.


Handmatig installeren

Thonny is een gratis Python-editor die speciaal gemaakt is voor beginners. Het installeert Python automatisch mee. Controleer na de installatie wel welke Python-versie je hebt, want pygame en pgzero werken niet met Python 3.14 of hoger.

Thonny installeren

Ga naar thonny.org en download de versie voor jouw besturingssysteem.

Windows

Download het .exe-bestand en dubbelklik erop. Volg de installatiestappen (klik gewoon op “Next”). Daarna vind je Thonny terug in het Startmenu.

macOS

Download het .pkg-bestand en open het. Sleep Thonny naar je Programma’s-map. De eerste keer dat je het opent, klik je met de rechtermuisknop en kies je “Open” (zodat macOS het vertrouwt).

Linux

Open een terminal en typ:

bash <(wget -qO - https://thonny.org/installer-for-linux)

Of gebruik je pakketbeheerder als Thonny beschikbaar is (bijv. sudo apt install thonny op Ubuntu).

Notitie

Gebruik de versie van thonny.org, niet de versie uit de app store. Die is soms verouderd.

Python-versie controleren

Pygame en pgzero hebben wheels nodig (voorgemaakte binaries) en die bestaan nog niet voor Python 3.14. Gebruik Python 3.13 (of 3.12).

  1. Open Thonny.
  2. Kijk rechtsonder in het venster. Daar staat de actieve Python-versie, bijv. Python 3.13.9.
  3. Staat er 3.14 of hoger? Installeer dan Python 3.13 via de stappen hieronder.
Tip

Staat er Python 3.12 of 3.13? Dan is alles goed. Sla de rest van dit onderdeel over en ga door naar Pgzero installeren.

Python 3.13 installeren en Thonny erop laten wijzen

  1. Ga naar python.org/downloads en klik op “All releases”. Zoek Python 3.13 in de lijst en download de installer voor jouw besturingssysteem.
    • Windows: kies de Windows installer (64-bit) en laat het vinkje “Add Python to PATH” aanstaan.
    • macOS: kies de macOS 64-bit universal2 installer.
    • Linux: gebruik je pakketbeheerder, bijv. sudo apt install python3.13.
  2. Doorloop de installatie en sluit hem af.
  3. Open Thonny en klik rechtsonder op de Python-versienaam (of ga naar Tools → Options → Interpreter).
  4. Kies “Alternatieve Python 3-interpreter of virtuele omgeving”.
  5. Klik op het mapicoon en blader naar de Python 3.13-executable:
    • Windows: C:\Users\<jounaam>\AppData\Local\Programs\Python\Python313\python.exe
    • macOS: /Library/Frameworks/Python.framework/Versions/3.13/bin/python3
    • Linux: /usr/bin/python3.13
  6. Klik op OK en herstart Thonny.

Rechtsonder staat nu Python 3.13. Ga door met het installeren van Pgzero.

Pgzero installeren

Pgzero is de bibliotheek waarmee je spelletjes maakt. Zo installeer je het:

  1. Open Thonny.
  2. Klik bovenaan op Tools → Manage packages…
  3. Typ in het zoekvak: pgzero
  4. Klik op het zoekresultaat en daarna op Install.
  5. Wacht tot de installatie klaar is (je ziet “Done” onderaan).

Controleer daarna dat de versie 1.2 of hoger is. Je ziet de versie rechts naast de naam in de pakketlijst.

Pygame installeren

Pgzero heeft Pygame nodig om te werken. Installeer het op dezelfde manier:

  1. Ga weer naar Tools → Manage packages…
  2. Zoek op pygame
  3. Klik op Install.

Pygame wordt automatisch gevonden door Pgzero, je hoeft er verder niets mee te doen.

Test of alles werkt

Maak een nieuw bestand aan in Thonny en plak deze code erin:

# test_setup.py
import pgzrun

WIDTH = 400
HEIGHT = 300

def draw():
    screen.fill((30, 30, 60))
    screen.draw.text("Pgzero werkt!", center=(200, 150), color="white")

pgzrun.go()

Sla het op als test_setup.py en klik op de groene Run-knop in Thonny.

Als alles goed is, opent er een venster met een donkerblauwe achtergrond en de tekst “Pgzero werkt!”. Sluit het venster door het kruisje te klikken.

Werkt het niet?

Geen paniek! Kijk op de probleemoplossingspagina voor de meest voorkomende fouten en hoe je ze oplost.

26 juni 2026, 15:04

VS Code (graduation track)

Heb je al een paar sessies Python gedaan en wil je meer controle over je code? Dan is VS Code een goede volgende stap. Het is de editor die echte programmeurs ook gebruiken.

Wanneer overstappen?

Pas wanneer Thonny te beperkt aanvoelt, bijvoorbeeld als je meerdere bestanden tegelijk wil beheren, Git wil gebruiken, of gewoon nieuwsgierig bent naar hoe professionele developers werken.

Overstappen is niet verplicht. Thonny blijft prima voor beginners en korte sessies.

Tip: probeer VS Code eens als je al minstens 3 sessies hebt meegedaan en je comfortabel bent met de basis van Python.

VS Code installeren

Download VS Code via code.visualstudio.com. Kies de installer voor jouw besturingssysteem (Windows, macOS of Linux) en volg de installatiestappen.

Python-extensie installeren

VS Code werkt met extensies. Voor Python heb je de officiële Microsoft-extensie nodig:

  1. Open VS Code.
  2. Klik op het Extensions-tabblad in de zijbalk (of druk op Ctrl+Shift+X).
  3. Zoek naar ms-python.python.
  4. Klik op Install.

Na de installatie herkent VS Code automatisch Python-bestanden en krijg je autocomplete, foutmeldingen en meer.

uv installeren

uv is een snelle Python-pakketbeheerder die veel makkelijker werkt dan de standaard pip. Installeer het via astral.sh/uv.

Windows (PowerShell):

powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

macOS / Linux:

curl -LsSf https://astral.sh/uv/install.sh | sh

Herstart je terminal na de installatie zodat het uv-commando beschikbaar is.

Project Installeren

Open een terminal in VS Code (`Ctrl+`` of via Terminal → New Terminal) en voer deze commando’s uit in je projectmap:

# Installeer een python versie groter dan 3.13 en kleiner dan 3.14
uv init --python "python<3.14,>3.13"
uv add pgzero pygame pygbag
Notitie

Gebruik --python 3.13 (of 3.12). Pygame en pgzero hebben nog geen wheels voor Python 3.14.

Daarna moet je VS Code vertellen welke Python-versie het project gebruikt:

  1. Druk op Ctrl+Shift+P en typ Python: Select Interpreter.
  2. Kies de interpreter die in je .venv-map staat (die ziet er uit als .venv/bin/python of .venv\Scripts\python.exe).

VS Code gebruikt nu de juiste omgeving met alle packages die je net hebt geïnstalleerd.

Pgzero runnen

Open een terminal in VS Code en gebruik:

uv run pgzrun main.py

Of maak een launch.json aan in de map .vscode/ zodat je met F5 kunt starten:

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Pgzero: run current file",
      "type": "debugpy",
      "request": "launch",
      "module": "pgzero",
      "args": ["${file}"],
      "console": "integratedTerminal"
    },
    {
      "name": "Pygame: run current file",
      "type": "debugpy",
      "request": "launch",
      "program": "${file}",
      "console": "integratedTerminal"
    }
  ]
}

Maak de map .vscode/ aan in je projectmap als die er nog niet is, en sla het bestand op als launch.json. Daarna zie je de configuraties terug in het Run and Debug-paneel (Ctrl+Shift+D).

Pygame runnen

Voor gewone Pygame-projecten (zonder pgzrun) gebruik je:

uv run python main.py

Of kies in het Run and Debug-paneel de configuratie “Pygame: run current file” als je de launch.json hebt aangemaakt.

14 juni 2026, 13:39

PyCharm (graduation track)

PyCharm is een volledig uitgeruste IDE van JetBrains. Je hebt het niet nodig om met Pgzero of Pygame aan de slag te gaan, maar als je verder wilt groeien als programmeur is het een handige stap. Denk aan een ingebouwde debugger, slimme code completion en krachtige refactoring-tools.

Wanneer overstappen?

Blijf gewoon met Thonny werken zolang dat prima voelt. Overweeg PyCharm pas als je:

  • variabelen wilt inspecteren terwijl je code pauzeert (debugger),
  • snel functies of variabelen wil hernoemen door heel je project heen (refactoring), of
  • je autocomplete wilt die ook weet wat de types van je variabelen zijn.

Voor beginners en de meeste dojo-sessies is Thonny meer dan genoeg.

PyCharm Community installeren

Download de gratis Community-editie op jetbrains.com/pycharm. Kies Community, niet Professional. Professional is betaald en heb je niet nodig.

  1. Ga naar de downloadpagina en klik op Community-Free.
  2. Voer het installatieprogramma uit en laat alle standaardinstellingen staan.
  3. Start PyCharm op. Je hoeft geen JetBrains-account aan te maken.

Nieuw project aanmaken en interpreter instellen

Wanneer je PyCharm voor het eerst opstart, kies je New Project.

PyCharm 2024.1 of nieuwer met uv:

Als je uv al op je systeem hebt staan (zie de Thonny-gids), herkent PyCharm dit automatisch. Kies bij Python version de optie 3.13 (of 3.12) en laat PyCharm een uv-omgeving aanmaken. Kies geen 3.14 of hoger, want pygame en pgzero hebben daarvoor nog geen wheels.

Oudere versie of zonder uv met venv:

  1. Kies als Interpreter type de optie Virtualenv.
  2. Selecteer bij Base interpreter de Python-installatie op je systeem (meestal python3 of python3.x).
  3. Laat het vinkje Create a main.py welcome script staan of verwijder het. Het maakt niet uit.

Klik daarna op Create. PyCharm maakt de omgeving aan en opent het project.

Packages installeren

Je installeert pgzero, pygame en pygbag via de instellingen:

  1. Open File → Settings (op macOS: PyCharm → Preferences).
  2. Navigeer naar Project: <naam> → Python Interpreter.
  3. Klik rechts onderaan op het +-icoon.
  4. Zoek in het zoekvak naar pgzero en klik op Install Package. Doe hetzelfde voor pygame en pygbag.
  5. Sluit het venster wanneer alle drie klaar zijn.

Controleer of alles gelukt is: rechts in de lijst zie je nu pgzero, pygame en pygbag met hun versienummers.

Run config voor Pgzero

Pgzero werkt anders dan een gewoon Python-script: je start het als een module en geeft je bestandsnaam mee als argument.

  1. Klik bovenaan op Add Configuration… (of open Run → Edit Configurations…).
  2. Klik op het +-icoon en kies Python.
  3. Vul in:
    • Name: Pgzero
    • Run: zet de keuze op Module name (niet Script path)
    • Module name: pgzero
    • Parameters: main.py
    • Working directory: de map van je project (als die nog leeg is, klik op het mapicoon en kies je projectmap)
  4. Klik op OK.

Selecteer daarna de Pgzero-configuratie in het dropdown-menu bovenaan en druk op de groene afspeelknop om je spel te starten.

Run config voor Pygame

Pygame start je wél als gewoon script:

  1. Open opnieuw Run → Edit Configurations… en klik op +Python.
  2. Vul in:
    • Name: Pygame
    • Run: laat de keuze op Script path staan
    • Script path: blader naar main.py in je projectmap
    • Working directory: je projectmap
  3. Klik op OK.

Kies de Pygame-configuratie in het dropdown-menu en druk op de afspeelknop.


Dat is alles. Je hebt nu twee kant-en-klare run-configuraties: één voor Pgzero-spellen en één voor Pygame-spellen. Wissel gewoon van configuratie in het dropdown-menu bovenaan naargelang wat je aan het bouwen bent.

Handige tips

  • Debugger gebruiken: Zet een breekpunt door links van een regelnummer te klikken (rode stip). Start daarna je run-configuratie via de bug-knop in plaats van de afspeelknop. PyCharm pauzeert op die regel zodat je variabelen kunt bekijken.
  • Automatisch importeren: Als PyCharm een rode onderstreping zet onder een naam die je net schreef, druk dan op Alt+Enter om automatisch het juiste import-statement bovenaan toe te voegen.
  • Terminal: Onderin kun je een ingebouwde terminal openen (View → Tool Windows → Terminal). Handig voor uv add of het uitvoeren van pygbag voor publiceren.
13 juni 2026, 13:30

Virtuele omgevingen (venv)

Stel je voor: je gaat op kamp en je moet je rugzak pakken. Voor een sportkamp stop je er andere dingen in dan voor een muziekkamp. Als je alles door elkaar gooit, vind je nooit iets terug.

Zo werkt het ook met Python-projecten. Elk project heeft zijn eigen “rugzak” nodig: een virtuele omgeving met precies de juiste spullen erin.

Wat is een virtuele omgeving?

Een virtuele omgeving (kortweg venv, spreek uit: “ven-vee”) is een aparte map op je computer waar Python én alle extra pakketten voor één project in zitten.

Stel dat je twee projecten hebt:

  • Project A gebruikt Pygame versie 2.5
  • Project B gebruikt Pygame versie 2.6

Zonder virtuele omgevingen zou je maar één versie van Pygame op je computer kunnen installeren. Het ene project zou dan niet meer werken. Met virtuele omgevingen heeft elk project zijn eigen afgesloten doos met de juiste versie erin. Ze staan elkaar niet in de weg.

Metafoor: Een virtuele omgeving is als een eigen rugzak per project. Je stopt er precies in wat dat project nodig heeft, en de rugzakken staan los van elkaar in de kast.

Waarom gebruiken we ze?

  1. Geen ruzie tussen projecten. Versie 2.5 en 2.6 kunnen allebei bestaan, elk in hun eigen omgeving.
  2. Delen is makkelijk. Als jij je project deelt met een vriend(in), hoef je niet alle pakketten mee te sturen. Je vriend(in) maakt gewoon een eigen virtuele omgeving en installeert dezelfde pakketten.
  3. Je computer blijft opgeruimd. Je installeert niets “zomaar” op je hele computer. Alles blijft netjes in de rugzak van het juiste project.

Hoe maak je een virtuele omgeving met uv

uv is de Python-pakketbeheerder die we op CoderDojo gebruiken. Hij kan virtuele omgevingen maken én beheren.

Stap 1: Open een terminal

  • Windows: Druk op ⊞ Windows, typ cmd en open “Command Prompt” (of gebruik PowerShell).
  • macOS / Linux: Open “Terminal”.

Stap 2: Ga naar de map van je project

cd pad/naar/jouw/projectmap
Tip

Vervang pad/naar/jouw/projectmap door het echte pad, bijvoorbeeld cd Bureaublad/mijn-spel.

Stap 3: Maak de virtuele omgeving aan

uv venv

uv maakt nu een mapje .venv aan in je projectmap. Daarin zit een aparte Python-installatie.

Wil je een specifieke Python-versie gebruiken? Dat kan ook:

uv venv --python 3.13

Waarom zou je dat doen? Omdat Pygame en Pgzero nog niet werken met Python 3.14 of hoger. Door --python 3.13 te gebruiken, ben je zeker dat alles werkt.

Stap 4: Activeer de virtuele omgeving

De omgeving bestaat, maar je moet hem nog “aanzetten”. Dat noemen we activeren.

Windows (Command Prompt of PowerShell):

.venv\Scripts\activate

macOS / Linux:

source .venv/bin/activate

Na activeren zie je links in je terminal (.venv) verschijnen. Dat is het teken dat je nú in je virtuele omgeving werkt.

(.venv) seppe@computer:~/project$

Stap 5: Installeer pakketten

Nu je omgeving actief is, kun je pakketten installeren. Ze komen alleen in deze ene rugzak terecht:

uv add pgzero pygame

Stap 6: Uitschakelen (deactiveren)

Klaar met programmeren? Typ gewoon:

deactivate

Het (.venv)-teken verdwijnt en je werkt weer met de gewone Python van je computer.

Waar moet je op letten?

⚠️ Vergeet de omgeving niet te activeren

De meest gemaakte fout: je opent een nieuwe terminal en vergeet de virtuele omgeving te activeren. Python klaagt dan dat het pakketten niet vindt, ook al heb je ze wél geïnstalleerd.

Oplossing: activeer altijd eerst met .venv\Scripts\activate (Windows) of source .venv/bin/activate (macOS/Linux) voor je je programma start.

⚠️ Zet .venv in .gitignore

De map .venv is groot (soms honderden MB’s). Die moet je niet delen via Git of GitHub. Zet .venv in je .gitignore-bestand, zodat Git hem overslaat:

.venv/

Jouw vriend(in) maakt gewoon een eigen .venv aan met uv venv en installeert dezelfde pakketten via het pyproject.toml-bestand dat wél in Git zit.

⚠️ Gebruik uv run als shortcut

Geen zin om telkens te activeren en deactiveren? uv heeft een handige snelkoppeling:

uv run python main.py

Dit voert python main.py uit binnen de virtuele omgeving, ook al is die niet geactiveerd. uv snapt zelf welke omgeving bij het project hoort.

⚠️ Verschillende terminals, verschillende omgevingen

Activeer je de omgeving in de ene terminal, dan is hij niet automatisch actief in een andere terminal. Je moet elke terminal apart activeren waar je de omgeving wil gebruiken.

Samengevat

ActieCommando (Windows)Commando (macOS/Linux)
Aanmakenuv venvuv venv
Activeren.venv\Scripts\activatesource .venv/bin/activate
Deactiverendeactivatedeactivate
Shortcutuv run python main.pyuv run python main.py

De gulden regel: zie je (.venv) in je terminal? Dan zit je goed. Zie je het niet? Activeer eerst, programmeer daarna.


Wat is het verschil met virtualenv en pip? Voor uv bestonden er al andere tools zoals virtualenv (voor omgevingen) en pip (voor pakketten). Daarmee kon je hetzelfde doen, maar je moest twee aparte commando’s gebruiken en het werkte een stuk trager. uv doet alles in één keer, is veel sneller, en is een stuk makkelijker voor beginners. Op CoderDojo gebruiken we daarom het liefst uv.

Heb je problemen met je virtuele omgeving? Kijk bij Problemen oplossen voor veelvoorkomende fouten.

13 juni 2026, 14:46

Problemen oplossen

Je code werkt niet en je weet niet waarom? Geen paniek: bijna elk probleem heeft een eenvoudige oplossing. Blader door de lijst hieronder en kijk of jouw foutmelding ertussen staat.

Pgzero of pygame kan niet geïnstalleerd worden

Je ziet een fout als No matching distribution found of Package not found bij het installeren van pgzero of pygame.

Oorzaak: Python 3.14 of hoger heeft nog geen kant-en-klare binaries voor pygame of pgzero. Thonny of je IDE gebruikt een te nieuwe Python-versie.

Ubuntu 26.04 heeft Python 3.14 als standaard en biedt Python 3.13 niet via apt. Het één-klik installatiescript lost dit automatisch op:

  • Python 3.13 wordt geïnstalleerd via de deadsnakes PPA (ppa:deadsnakes/ppa)
  • Thonny wordt gedetecteerd via PATH, snap, pip of flatpak
  • Als Thonny als snap geïnstalleerd is, wordt de configuratie naar de juiste map geschreven (~/snap/thonny/current/.config/Thonny/)
  • Lukt apt install thonny niet, dan probeert het script pip install --user thonny

Oplossing: Installeer Python 3.13 (of 3.12) en stel die in als actieve interpreter:

  • Thonny: zie Thonny + Pgzero InstallerenPython-versie controleren.
  • VS Code: maak je omgeving opnieuw aan met uv venv --python 3.12.
  • PyCharm: maak een nieuwe interpreter aan en selecteer Python 3.12 bij Base interpreter.

pgzrun: command not found

Dit betekent dat Pgzero niet geïnstalleerd is, of dat je een verkeerde Python-versie gebruikt.

Oplossing: Open Thonny, ga naar Tools → Manage Packages, zoek naar pgzero en klik op Install. Herstart Thonny daarna en probeer opnieuw.

ModuleNotFoundError: No module named 'pygame'

Python kan de pygame-bibliotheek niet vinden. Dit komt bijna altijd doordat Thonny de verkeerde Python-interpreter gebruikt.

Oplossing: Kijk rechtsonder in de statusbalk van Thonny welke interpreter actief is. Kies dezelfde interpreter die je bij de installatie hebt gebruikt. Installeer pygame dan opnieuw via Tools → Manage Packages.

Venster opent en sluit meteen

Het programmavenster flitst even op en verdwijnt dan weer.

  • Pgzero: Je hebt WIDTH en HEIGHT nodig als losse variabelen bovenaan je bestand, buiten elke functie. Zonder die variabelen weet Pgzero niet hoe groot het venster moet zijn en stopt het direct.
  • Pygame: Je hebt een event-loop nodig die elke frame pygame.event.get() aanroept. Zonder die aanroep bevriest het venster en sluit het besturingssysteem het.

Geluiden doen niets

Je roept sounds.jump.play() aan maar er is niets te horen.

Controleer het volgende:

  1. Staat er een map sounds/ naast je main.py? De map moet op exact dezelfde plek staan.
  2. Staan de bestandsnamen in kleine letters en gebruik je underscores in plaats van spaties? Bijvoorbeeld jump.wav, niet Jump.wav of jump sound.wav.
  3. Pgzero accepteert alleen .wav-bestanden in de sounds/-map. Pgzero laadt ze automatisch, dus je hoeft zelf niets te importeren.

Geluid werkt niet in pygame (sessie 5 en later)

Je gebruikt pygame.mixer.Sound(...) maar er is niets te horen, geen foutmelding; het spel start gewoon zonder geluid.

Oorzaak: Python zoekt het geluidsbestand relatief aan de map waar je het script vanuit start, niet aan de map waar main.py staat. Open je VS Code vanuit de projectroot en staat je script in een submap, dan zoekt Python sounds/hit.wav in de projectroot en vindt het niet. De try/except in de starter vangt die fout stil op.

Oplossing: Gebruik __file__ om het pad absoluut te maken:

import os
_DIR = os.path.dirname(os.path.abspath(__file__))
snd_hit = pygame.mixer.Sound(os.path.join(_DIR, 'sounds/hit.wav'))

__file__ is altijd het pad naar main.py zelf, ongeacht vanwaar je het script start. De starter-bestanden vanaf sessie 5 doen dit al. Voeg de twee regels toe net boven het laden van geluiden als je eigen game dit nog niet heeft.

Afbeeldingen worden niet weergegeven

Je actor of achtergrond is niet zichtbaar.

Controleer het volgende:

  1. Staat er een map images/ naast je main.py?
  2. Zijn alle bestandsnamen in kleine letters? player.png werkt; Player.png niet.
  3. Gebruik bij voorkeur .png-bestanden, want die werken het betrouwbaarst met Pgzero.

KeyError bij een afbeelding

Je krijgt een KeyError wanneer je een Actor aanmaakt of een afbeelding laadt.

Oorzaak: De naam die je meegeeft moet exact overeenkomen met de bestandsnaam, zonder extensie. Actor('basket') zoekt naar images/basket.png. Een typfout of een hoofdletter te veel is genoeg om de fout te veroorzaken.

Oplossing: Controleer de bestandsnaam in de images/-map en zorg dat die exact overeenkomt met wat je in de code hebt geschreven.

Chromebook of geen installatierechten

Je kunt Thonny of Pgzero niet installeren omdat je geen beheerdersrechten hebt op de computer.

Oplossing: Werk samen met iemand die Thonny wél heeft staan, want pair-programming is sowieso een goed idee! Thuis kun je verder via de pygbag-versie: die draait je Pygame-project rechtstreeks in de browser, zonder installatie.

Antivirus blokkeert de installatie

Je installatiepoging wordt onderbroken of geblokkeerd door antivirussoftware.

Oplossing: Schakel de antivirussoftware tijdelijk uit tijdens de installatie, of vraag een coach om een portable versie van Thonny op een USB-stick te zetten. Die versie heeft geen installatie nodig en werkt zo van de stick.


Nog steeds vast?

Geen probleem. Vraag gewoon een coach tijdens de sessie, of post je vraag in de CoderDojo-chat. Geef daarbij de exacte foutmelding mee (kopieer en plak de tekst uit Thonny), dan kunnen we je sneller helpen.

2 juli 2026, 15:35

Sessies

Kies een reeks hieronder. De Python-sessies bouwen games met Pygame. De Git-sessies leren je versiebeheer: van je eerste commit tot pull requests.

13 juni 2026, 13:30

Subsecties van Sessies

Python sessies

Tien sessies waarin je elke maand een compleet Pygame-spel bouwt. Elke sessie bevat een worksheet, een cheatsheet en startcode om te downloaden.

  • Sessie 1: Catch the Stars: Een ster valt, jij vangt hem. Je schrijft je eerste draw()/update() loop en beweegt een sprite met pijltjestoetsen.
  • Sessie 2: Dodge Meteors: Meteoren spawnen en vallen. Je houdt ze bij in een lijst, verwijdert ze als ze van het scherm af zijn en trekt een leven af bij elke botsing.
  • Sessie 3: Space Blaster: Vijanden zakken naar beneden, jij schiet ze neer. Je maakt kogels aan met SPATIE en detecteert collision met een loop over een lijst.
  • Sessie 4: Pong: Twee paddles, één bal. Je leert pygame.Rect, laat de bal stuiteren door de snelheid om te keren en houdt de score bij voor twee spelers.
  • Sessie 5: Breakout: Stenen kapotschieten met een stuiterende bal. Je voegt geluidseffecten toe met pygame.mixer en bouwt een win- en verlies-scherm.
  • Sessie 6: Platformer: Springen en vallen op platforms. Je simuleert zwaartekracht met een vel_y variabele en detecteert platformbotsingen met pygame.Rect.
  • Sessie 7: Bug Zapper: Bugs kruipen, jij schiet ze neer. Je schrijft je eerste klasse met __init__ en maakt een subklasse via overerving.
  • Sessie 8: Sky Highway: Een vliegend schip op een scrollende weg. Je spawnt obstakels van rechts en verhoogt de snelheid naarmate de score stijgt.
  • Sessie 9: Boss Battle: Een baas met HP-balk en aanvalsfases. Je bouwt een state machine die het schiet- en bewegingsgedrag per fase aanpast.
  • Sessie 10: Mijn Spel: Geen vaste opdracht. Jij kiest het genre en combineert alles wat je in negen sessies hebt geleerd in je eigen spel.
13 juni 2026, 13:30

Subsecties van Python sessies

Sessie 1: Catch the Stars

In deze sessie bouw je een mini-spel: een ster valt naar beneden en jij vangt hem op met een basket. Aan het einde van de sessie beweegt alles, houdt het spel bij hoeveel sterren je vangt, en heb je je eerste eigen game.

Wat je vandaag leert

  • Variabelen gebruiken om objecten te positioneren
  • De draw() functie: wat je elke frame tekent
  • De update() functie: wat er elke frame verandert
  • Toetsenbord-input: bewegen met pijltjestoetsen

Stap 0: Installeren

  1. Open Thonny.
  2. Download de starter via deze link, pak de ZIP uit en open main.py in Thonny.
  3. Klik op de groene Run-knop. Je ziet een donkerblauw venster met een basket onderaan en een ster bovenaan, maar niks beweegt nog.
  4. Problemen? Kijk bij Thonny instellen.

Stap 1: Laat de ster vallen

De update() functie draait 60 keer per seconde. Alles wat je daarin zet, wordt dus 60 keer per seconde uitgevoerd. Dat is hoe beweging werkt in een game.

Op dit moment staat er alleen pass in update(). Dat betekent “doe niks”. Verwijder pass en voeg dit toe:

def update():
    star.y = star.y + 3

Klik op Run. De ster valt nu van boven naar beneden. Als hij het scherm uit valt, verdwijnt hij. Dat lossen we zo op.

Wat gebeurt hier? star.y is de verticale positie van de ster. Hoe groter y, hoe lager op het scherm. Door elke frame + 3 toe te voegen, zakt de ster steeds een beetje.

Het coördinatenstelsel: (0, 0) zit linksboven, niet linksonder zoals in wiskunde. x loopt naar rechts, y loopt naar beneden. De ster begint op y = 30 (bijna helemaal boven), de basket staat op y = 370 (bijna helemaal onder).

Coördinatenstelsel Pygame Zero: oorsprong linksboven, y loopt naar beneden

Beweeg je muis over het scherm hieronder om de coördinaten te zien:

Beweeg je muis over het scherm om de coördinaten te zien.

Wil je meer weten over hoe het scherm werkt? → Coördinaten uitgelegd

Pas de schuifbalken aan en kijk hoe actor.x en actor.y veranderen als de actor beweegt:

Stap 2: ✅ Basic: laat de basket bewegen

Tijd om de basket te besturen. Pygame Zero heeft een handige keyboard-variabele die bijhoudt welke toetsen ingedrukt zijn.

Voeg dit toe aan update(), na de regel over de ster:

def update():
    star.y = star.y + 3

    if keyboard.left:
        basket.x = basket.x - 5
    if keyboard.right:
        basket.x = basket.x + 5

Klik op Run en gebruik de pijltjestoetsen. De basket beweegt nu mee!

Tip (optioneel, telt als Stretch): Als je de basket te ver naar links of rechts beweegt, verdwijnt hij van het scherm. Kun jij een check toevoegen die dat voorkomt? Hint: gebruik WIDTH en vergelijk met basket.x.

Stap 3: ⭐ Stretch: vang de ster

Nu wil je dat er iets gebeurt als de basket de ster raakt. Daarvoor gebruik je colliderect. Die controleert of twee rechthoeken elkaar overlappen.

Voeg dit toe aan update():

    if star.colliderect(basket):
        star.y = 0

De ster springt terug naar boven als je hem vangt. Maar wat als je hem mist? Voeg ook dit toe:

    if star.y > HEIGHT:
        star.y = 0

Bonus: Laat de ster elke keer op een willekeurige plek terug verschijnen. Voeg bovenaan het bestand toe:

import random

Vervang dan star.y = 0 (op beide plekken) door:

        star.x = random.randint(20, WIDTH - 20)
        star.y = 0

Nu valt de ster steeds op een andere plek. Veel moeilijker te vangen!

Stap 4: 🔥 Expert: voeg een score toe

Een echte game houdt bij wat je scoort. Daarvoor heb je een variabele nodig die buiten de functies leeft: een globale variabele.

Hoe een variabele werkt: een doosje met een naam en een waarde

Klik op de knop hieronder om te zien hoe score verandert elke keer dat je een ster vangt:

Voeg dit toe bovenaan, buiten alle functies (na de regels over basket en star):

score = 0

In draw(), voeg toe:

    screen.draw.text(f"Score: {score}", topleft=(10, 10), color="white")

In update(), pas de catch-code aan zodat de score omhoog gaat:

    if star.colliderect(basket):
        global score
        score = score + 1
        star.x = random.randint(20, WIDTH - 20)
        star.y = 0

global score vertelt Python: “ik bedoel de score van buiten deze functie, niet een nieuwe lokale variabele.” Zonder die regel krijg je een foutmelding.

Klik op Run. De score staat linksboven en telt op elke keer dat je een ster vangt!

Showcase

Laat je spel zien aan een coach en een buddy. Vertel wat je gemaakt hebt: wat werkt, en wat je nog extra hebt toegevoegd.

Tot de volgende keer!

“Volgende keer: dingen die TERUGSLAAN. En ze gaan BOEM, met echt geluid.”

Neem mee naar huis

Probeer thuis één van deze uitbreidingen. Bij elke staat een code-hint om je op weg te helpen.

  1. Beperk levens — Voeg levens = 3 toe. Bij een misser (star.y > HEIGHT): levens -= 1. Stop het spel als levens == 0 met game_over = True en toon “Game Over” in draw().

  2. Willekeurige ster-kleur — Bij elke vangst: geef de ster een nieuwe kleur met random.randint(50, 255) voor rood, groen en blauw. Begin bij 50, niet bij 0 — anders is de ster soms zwart en onzichtbaar.

  3. Achtergrond — Teken vaste sterren op de achtergrond in draw() voor de score. Gebruik screen.draw.filled_circle(x, y, 2, "white") op 20 willekeurige posities. Zaai random.seed(42) zodat ze niet flikkeren.

  4. Tweede ster — Maak sterren = [star, Actor("star")]. Geef elke ster een eigen x. Werk ze allebei bij in update() en teken ze in draw().

  5. Hoge score — Houd high_score bij. Als score > high_score: high_score = score. Toon f"Beste: {high_score}" naast de score.

  6. Basket kleiner — Na elke 5 punten: basket.width = max(30, basket.width - 5). Zet basket.width terug bij herstart.

  7. Bom — Voeg een rode cirkel toe die af en toe valt. Gebruik een timer bom_timer en random.randint om te bepalen wanneer hij verschijnt. Als de basket de bom raakt: score -= 2.

  8. Moeilijkheidsgraad — Na elke 5 sterren: verhoog de valsnelheid met 1. Bouw een maximum in: max(3, star_snelheid).

Vastgelopen? Vraag het volgende dojo aan een coach, of probeer gewoon iets anders. Programmeren is doen.

Bekijk de cheatsheet voor deze sessie

20 juni 2026, 10:54

Sessie 2: Dodge Meteors

Je ruimteschip zweeft in de donkere ruimte terwijl meteoren van boven naar beneden komen. Ontwijk ze, of verlies een leven. Hoe langer je overleeft, hoe sneller ze gaan. En deze keer bouw je ook Dojo Defender: het grotere project dat elke sessie groeit.

Wat je vandaag leert

  • Lijsten gebruiken om meerdere objecten bij te houden
  • Objecten spawnen (aanmaken) en verwijderen
  • Levens bijhouden en een game-over scherm tonen
  • Dojo Defender: je eigen ruimteschip toevoegen aan het grotere project

Stap 0: Installeren

  1. Open Thonny.
  2. Download de starter via deze link, pak de ZIP uit en open main.py.
  3. Klik op Run. Je ziet je ruimteschip onderaan, maar er vallen nog geen meteoren.
  4. Problemen? Kijk bij Thonny instellen.

Stap 1: Laat meteoren spawnen

Vul de functie spawn_meteor() in. Een meteoor is een Actor die bovenaan het scherm begint op een willekeurige x-positie:

import random

def spawn_meteor():
    rock = Actor('meteor')
    rock.x = random.randint(30, WIDTH - 30)
    rock.y = 0
    meteors.append(rock)

Roep de functie aan in update(), maar niet élke frame, anders wordt het scherm meteen volgestort. De update() functie draait 60 keer per seconde. Gebruik dat om een teller bij te houden:

Zie hieronder hoe snel een meteoor beweegt bij verschillende snelheden en hoeveel pixels dat per seconde is:

Gebruik een teller:

# bovenaan, buiten de functies:
timer = 0

def update():
    global timer
    timer += 1
    if timer % 60 == 0:   # elke seconde (60 frames)
        spawn_meteor()

Klik op Run. Na een seconde verschijnt de eerste meteoor, maar hij beweegt nog niet.

Stap 2: ✅ Basic: meteoren laten vallen en verdwijnen

Laat alle meteoren in de lijst zakken, en verwijder ze als ze het scherm uit vallen:

def update():
    global timer
    timer += 1
    if timer % 60 == 0:
        spawn_meteor()

    for rock in meteors[:]:      # [:] = kopie zodat je veilig kunt verwijderen
        rock.y += 4
        if rock.y > HEIGHT + 30:
            meteors.remove(rock)

Collision detecteren: Als een meteoor het schip raakt, verlies je een leven. Voeg dit toe in de loop:

        if rock.colliderect(ship):
            meteors.remove(rock)
            global lives
            lives -= 1
            if lives <= 0:
                # TODO: game over (stap 3)
                pass

Stap 3: ⭐ Stretch: game over en snelheid opvoeren

Game over: Voeg een variabele game_over = False toe bovenaan. Als lives == 0, zet game_over = True. In draw(), toon een tekst als game_over True is:

    if game_over:
        screen.draw.text(
            "GAME OVER",
            center=(WIDTH / 2, HEIGHT / 2),
            color="red",
            fontsize=60,
        )

Stop het bewegen in update() als game_over True is:

def update():
    if game_over:
        return
    ...

Snelheid verhogen: Laat meteoren sneller gaan naarmate de score stijgt:

snelheid = 3 + score // 5   # elke 5 punten +1 snelheid
rock.y += snelheid

Stap 4: 🔥 Expert: score en meerdere meteoorgroottes

Score: Elke seconde dat je overleeft, +1 punt. Voeg global score toe en verhoog in update():

    if timer % 60 == 0:
        global score
        score += 1
        spawn_meteor()

Meerdere groottes: Maak kleine, middel en grote meteoren. Gebruik een schaal-variabele en pas de hitbox aan:

def spawn_meteor():
    rock = Actor('meteor')
    rock.x = random.randint(30, WIDTH - 30)
    rock.y = 0
    rock.schaal = random.choice([0.5, 1.0, 1.5])
    meteors.append(rock)

Kleinere meteoren bewegen sneller; grotere zijn moeilijker te ontwijken maar geven meer punten.

Dojo Defender

Download de Dojo Defender starter voor vandaag. Dit is de basis van het project dat elke sessie groeit. Voeg vandaag je ruimteschip toe en laat het bewegen met de pijltjestoetsen.

Was je er vorige keer niet bij? Geen probleem — download de oplossing van vorige keer en je kan meteen mee.

Het volledige Dojo Defender werkblad vind je op de Dojo Defender pagina.

Showcase

Laat je spel zien aan een coach en een buddy. Hoe lang overleef jij? Wie haalt de hoogste score?

Tot de volgende keer!

“Volgende keer: je eigen ruimteschip SCHIET TERUG. Vijanden in formatie, en jij blast ze weg.”

Neem mee naar huis

  1. Makkelijk: verander de achtergrondkleur naar dieppaars of donkergroen.
  2. Middel: voeg een geluid toe als een meteoor het schip raakt (sounds/hit.wav).
  3. Lastig: maak meteoren sneller naarmate er meer op het scherm zijn (niet alleen de score).
  4. Erg lastig: voeg power-ups toe: een gouden ster die je een extra leven geeft als je hem aanraakt.

Bekijk de cheatsheet voor deze sessie

1 juli 2026, 22:43

Sessie 3: Space Blaster

Vijanden staan in formatie bovenaan het scherm. Jij staat onderaan met een ruimteschip en één doel: ze allemaal uitschakelen voordat ze de bodem bereiken. Druk op SPATIE om te schieten, en mis niet.

Wat je vandaag leert

  • Kogels bijhouden in een lijst
  • Een kogel aanmaken als je op SPATIE drukt
  • Collision tussen kogels en vijanden
  • Game over als een vijand de bodem raakt

Stap 0: Installeren

  1. Open Thonny.
  2. Download de starter via deze link, pak de ZIP uit en open main.py.
  3. Klik op Run. Je ziet het schip onderaan en 12 vijanden in een raster, maar niks beweegt en je kunt nog niet schieten.

Stap 1: Laat het schip bewegen

Het schip beweegt al met de pijltjestoetsen. Controleer de code in update(). Zorg dat het schip niet buiten het scherm gaat:

if keyboard.left and ship.x > 30:
    ship.x -= 5
if keyboard.right and ship.x < WIDTH - 30:
    ship.x += 5

Stap 2: ✅ Basic: schieten met SPATIE

Vul de functie shoot() in. Een kogel is een Actor die begint op de positie van het schip:

def shoot():
    kogel = Actor('bullet')
    kogel.x = ship.x
    kogel.y = ship.y - 20
    bullets.append(kogel)

Roep shoot() aan als de spatiebalk wordt ingedrukt. Gebruik on_key_down (Pgzero-stijl):

def on_key_down(key):
    if key == keys.SPACE:
        shoot()

Laat de kogels omhoog bewegen in update() en verwijder ze als ze het scherm uit gaan:

for kogel in bullets[:]:
    kogel.y -= 8
    if kogel.y < -10:
        bullets.remove(kogel)

Stap 3: ⭐ Stretch: vijanden raken

colliderect controleert of de rechthoekige hitbox van twee actors overlapt. Beweeg je muis over het scherm hieronder om te zien wanneer dat gebeurt:

Controleer in update() of een kogel een vijand raakt:

for kogel in bullets[:]:
    kogel.y -= 8
    if kogel.y < -10:
        bullets.remove(kogel)
        continue
    for vijand in enemies[:]:
        if kogel.colliderect(vijand):
            bullets.remove(kogel)
            enemies.remove(vijand)
            global score
            score += 10
            break

Win-conditie: Als enemies leeg is, heb je gewonnen:

    if not enemies:
        screen.draw.text("JE WINT!", center=(WIDTH/2, HEIGHT/2), color="yellow", fontsize=60)

Stap 4: 🔥 Expert: vijanden bewegen en game over

Laat de vijanden langzaam naar beneden komen. Gebruik een timer:

vijand_timer = 0

def update():
    global vijand_timer
    vijand_timer += 1
    if vijand_timer % 90 == 0:   # elke 1,5 seconde
        for vijand in enemies:
            vijand.y += 20

Game over als een vijand de bodem bereikt:

        for vijand in enemies:
            if vijand.y > HEIGHT - 30:
                global game_over
                game_over = True

Snellere schietfrequentie: Bouw een cooldown in zodat de ninja niet oneindig snel kan schieten:

shoot_cooldown = 0

def update():
    global shoot_cooldown
    if shoot_cooldown > 0:
        shoot_cooldown -= 1

def on_key_down(key):
    global shoot_cooldown
    if key == keys.SPACE and shoot_cooldown == 0:
        shoot()
        shoot_cooldown = 15

Showcase

Laat je spel zien aan een coach. Hoe snel maak jij alle vijanden weg? Vergelijk scores met een buddy.

Tot de volgende keer!

“Volgende keer: twee spelers, één bal: PONG. En je leert echte pygame zonder pgzrun.”

Neem mee naar huis

  1. Makkelijk: verander de kleur van de kogels of de vijanden.
  2. Middel: laat vijanden links en rechts bewegen (één richting per stap, keer om aan de rand).
  3. Lastig: voeg een second wave toe: als alle vijanden weg zijn, spawn een nieuwe rij die sneller daalt.
  4. Erg lastig: laat vijanden ook terugschieten: willekeurig een kogel omlaag sturen elke N frames.

Bekijk de cheatsheet voor deze sessie

Dojo Defender

Download de Dojo Defender starter voor vandaag.

Was je er vorige keer niet bij? Download de oplossing van sessie 1 en begin daarmee.

Het volledige werkblad: Dojo Defender Sessie 2.

1 juli 2026, 22:43

Sessie 4: Pong

Twee paddles, één bal: het klassieke Pong-spel. Maar er is een verrassing: deze sessie schrijf je het spel in echte pygame, zonder pgzrun. Je leert de game loop zelf Installeren. Dat is hoe de meeste echte games werken.

Wat je vandaag leert

  • De pygame game loop: events, update, draw
  • pygame.Rect voor rechthoeken en botsingen
  • Bal laten stuiteren (snelheid omkeren)
  • Score bijhouden voor twee spelers

Stap 0: Installeren

  1. Open Thonny.
  2. Download de starter via deze link, pak de ZIP uit en open main.py.
  3. Klik op Run. Je ziet twee paddles en een gele bal in het midden, maar de bal beweegt nog niet.

Verschil met pgzrun: Er zijn geen draw() en update() functies meer. Alles zit in één while running: loop.

Stap 1: Laat de bal bewegen

Zoek de regels:

# bal.x += ball_dx  # zet dit aan als je klaar bent
# ball.y += ball_dy

Verwijder de # voor beide regels. Maar stel eerst een startsnelheid in. Zoek ball_dx, ball_dy = 0, 0 en verander naar:

ball_dx, ball_dy = 4, 4

Klik op Run. De bal beweegt nu, maar verdwijnt door de rand. Dat lossen we op in stap 2.

Stap 2: ✅ Basic: bal stuitert op muren en paddles

Voeg na ball.x += ball_dx en ball.y += ball_dy het volgende toe:

# Stuiteren op boven- en onderrand
if ball.top <= 0 or ball.bottom >= HEIGHT:
    ball_dy = -ball_dy

# Stuiteren op paddles
if ball.colliderect(left_paddle) or ball.colliderect(right_paddle):
    ball_dx = -ball_dx

De bal stuitert nu oneindig. Voeg ook grenzen toe voor de paddles zodat ze niet buiten het scherm gaan. Kijk naar de code die al in de starter staat.

Stap 3: ⭐ Stretch: score bijhouden

Als de bal links het scherm uitvalt, scoort de rechter speler. Als hij rechts uitvalt, scoort de linker:

# Bal uit links → rechts scoort
if ball.left <= 0:
    score_right += 1
    ball.center = (WIDTH // 2, HEIGHT // 2)
    ball_dx = 4

# Bal uit rechts → links scoort
if ball.right >= WIDTH:
    score_left += 1
    ball.center = (WIDTH // 2, HEIGHT // 2)
    ball_dx = -4

De score staat al in draw(). Controleer dat de variabelen kloppen.

Stap 4: 🔥 Expert: win-conditie en snelheid verhogen

Win bij 5 punten: Voeg een variabele won toe. Als een speler 5 bereikt, stop dan het spel:

if score_left >= 5:
    won = "Links wint!"
if score_right >= 5:
    won = "Rechts wint!"

Toon de winnaar op het scherm en stop de beweging.

Snelheid verhogen: Na elke rally wordt de bal een beetje sneller:

if ball.colliderect(left_paddle) or ball.colliderect(right_paddle):
    ball_dx = -ball_dx * 1.05   # 5% sneller
    ball_dy = ball_dy * 1.05

Bouw een maximum in: ball_dx = max(-12, min(12, ball_dx)).

Showcase

Speel een potje tegen een buddy. Eerste naar 5 punten wint. Laat het zien aan een coach.

Tot de volgende keer!

“Volgende keer: Breakout: de bal beukt stenen kapot. En er is GELUID.”

Neem mee naar huis

  1. Makkelijk: verander de kleur van de bal of de paddles.
  2. Middel: maak één paddle groter naarmate die speler verliest (handicap).
  3. Lastig: voeg een AI-tegenstander toe: de rechter paddle volgt automatisch de bal.
  4. Erg lastig: voeg power-ups toe: een item dat de bal verdubbelt of de paddle groter maakt.

Dojo Defender

Vandaag verhuist Dojo Defender naar echte Pygame! Download de starter. Was je er niet bij? Download de oplossing van sessie 2. Werkblad: Dojo Defender Sessie 3.


Bekijk de cheatsheet voor deze sessie

1 juli 2026, 22:43

Sessie 5: Breakout

Een bal kaatst heen en weer en beukt rijen gekleurde stenen kapot. Jij bestuurt de paddle om de bal in de lucht te houden. En deze keer hoor je het ook: er zijn geluidseffecten bij elke botsing.

Wat je vandaag leert

  • Geluid laden en afspelen met pygame.mixer
  • Botsingen met meerdere objecten (lijst van stenen)
  • De bal laten stuiteren in de juiste richting
  • Win- en verlies-scherm bouwen

Stap 0: Installeren

  1. Open Thonny.
  2. Download de starter via deze link, pak de ZIP uit en open main.py.
  3. Klik op Run. Je ziet de paddle, de bal en de stenen. De bal beweegt al horizontaal maar niet verticaal. Dat ga jij aanpassen.

Stap 1: Laat de bal verticaal bewegen

Zoek deze regel in de starter:

ball_dx, ball_dy = 4, 0
# ball_dy = -4  ← verwijder de # om de bal te laten bewegen!

Verwijder de # voor ball_dy = -4. Nu heeft de bal een verticale snelheid. Voeg beweging toe in de game loop (zoek de update-sectie):

ball.x += ball_dx
ball.y += ball_dy

Klik op Run. De bal beweegt diagonaal maar verdwijnt door de randen.

Stap 2: ✅ Basic: bal stuitert op muren en paddle

Voeg stuiteren toe:

# Boven-, links- en rechtsrand
if ball.left <= 0 or ball.right >= WIDTH:
    ball_dx = -ball_dx
if ball.top <= 0:
    ball_dy = -ball_dy

# Paddle
if ball.colliderect(paddle):
    ball_dy = -ball_dy
    if snd_hit:
        snd_hit.play()

Levens: Als de bal de onderkant bereikt, verlies je een leven:

if ball.top > HEIGHT:
    levens -= 1
    # Reset bal bovenop de paddle
    ball.center = (paddle.centerx, paddle.top - BALL_SIZE)
    ball_dy = -4

Stap 3: ⭐ Stretch: stenen breken

Controleer collision met elke steen in de loop:

for steen in stenen[:]:
    if ball.colliderect(steen['rect']):
        stenen.remove(steen)
        ball_dy = -ball_dy
        score += 10
        if snd_hit:
            snd_hit.play()
        break  # slechts één steen per frame breken

Win-conditie: Als alle stenen weg zijn, heeft de ninja gewonnen. Toon een bericht:

if not stenen:
    # toon win-scherm

Stap 4: 🔥 Expert: levens, explosiegeluid en snelheid

Explosiegeluid als een steen breekt (gebruik snd_explode uit de starter):

if snd_explode:
    snd_explode.play()

Game over bij 0 levens: toon een scherm en stop de bal.

Snelheid verhogen naarmate er minder stenen overblijven:

snelheid_factor = 1 + (1 - len(stenen) / totaal_stenen) * 0.5
ball_dx_huidig = ball_dx * snelheid_factor

Nieuwe rij stenen als alles weg is: roep stenen = maak_stenen() opnieuw aan en verhoog de moeilijkheidsgraad.

Showcase

Wie haalt de meeste stenen weg? Vergelijk scores en laat je coach de hoogste streak zien.

Tot de volgende keer!

“Volgende keer: zwaartekracht. Jouw karakter valt, en jij moet hem laten springen op platforms.”

Neem mee naar huis

  1. Makkelijk: verander de kleuren van de stenen naar jouw favoriete kleuren.
  2. Middel: maak sommige stenen twee keer zo sterk (ze moeten tweemaal geraakt worden).
  3. Lastig: voeg een power-up toe die de paddle tijdelijk breder maakt.
  4. Erg lastig: voeg een tweede bal toe die spawnt als score > 50.

Bekijk de cheatsheet voor deze sessie

Dojo Defender

Dojo Defender krijgt een menu en nieuwe vijandtypes! Download de starter. Was je er niet bij? Download de oplossing van sessie 3. Werkblad: Dojo Defender Sessie 4.

1 juli 2026, 22:43

Sessie 6: Platformer

In deze sessie bouw je een micro-platformer. Je speler heeft zwaartekracht, kan springen, en verzamelt sterren op platforms. Je gebruikt voor het eerst pygame direct, zonder pgzrun, en voegt geluidseffecten toe met pygame.mixer.

Wat je vandaag leert

  • Zwaartekracht simuleren met een vel_y variabele
  • Springen door vel_y negatief te maken
  • Platformbotsingen detecteren met pygame.Rect
  • Items verzamelen (collision met sterren)
  • Geluiden afspelen met pygame.mixer

Stap 0: Installeren

  1. Open Thonny.
  2. Download de starter via deze link, pak de ZIP uit en open main.py in Thonny.
  3. Klik op de groene Run-knop. Je ziet een venster met een speler, platforms en sterren, maar de speler valt nog niet.
  4. Problemen? Vraag een coach.

Stap 1: Zwaartekracht

In een platformer valt de speler continu naar beneden, tenzij hij op een platform staat. We simuleren dat met vel_y: een verticale snelheid die elke frame groter wordt door zwaartekracht.

Zoek de # STAP 1 comment in main.py en vervang de pass door:

# Zwaartekracht toepassen
vel_y += GRAVITY
player_rect.y += vel_y

# Controleer of speler op een platform staat
on_ground = False
for platform in platforms:
    if (player_rect.colliderect(platform) and vel_y > 0):
        player_rect.bottom = platform.top
        vel_y = 0
        on_ground = True

GRAVITY = 0.5 staat al bovenaan het bestand. Klik Run. De speler valt nu naar het dichtstbijzijnde platform en blijft daar staan.

Wat gebeurt hier? Elke frame wordt vel_y iets groter (de speler versnelt naar beneden). Als de speler een platform raakt terwijl hij naar beneden valt (vel_y > 0), zet je zijn onderkant (bottom) precies op de bovenkant van het platform en stop je de val (vel_y = 0).

Stap 2: ✅ Basic: springen

Een platformer zonder springen is maar half af. Druk op SPATIE om te springen: dat is niets anders dan vel_y plotseling negatief maken.

Zoek de # STAP 2 comment en voeg toe:

keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
    player_rect.x -= SPEED
if keys[pygame.K_RIGHT]:
    player_rect.x += SPEED

# Springen — alleen als je op de grond staat
if keys[pygame.K_SPACE] and on_ground:
    vel_y = -12

Klik Run en test:

  • Pijltjestoetsen: beweeg links/rechts
  • SPATIE: spring omhoog

Tip: Als de speling te hoog of te laag voelt, verander dan -12 in -10 (lager springen) of -15 (hoger springen).

Stap 3: ⭐ Stretch: sterren verzamelen

Laten we iets te verzamelen toevoegen. De starter heeft al een lijst stars met pygame.Rect-objecten. Jij schrijft de botsingsdetectie.

Zoek de # STAP 3 comment:

for star in stars[:]:  # [:] zodat je veilig kunt verwijderen tijdens het loopen
    if player_rect.colliderect(star):
        stars.remove(star)
        score += 1

En in de tekenfunctie (zoek # TEKEN STERREN):

for star in stars:
    pygame.draw.polygon(screen, YELLOW, star_points(star.centerx, star.centery, 12, 5))

star_points() is al gedefinieerd in de starter. Die berekent de punten van een 5-puntige ster.

Bovenaan het spel staat score = 0. Teken de score op het scherm (zoek # TEKEN SCORE):

score_surf = font.render(f"Sterren: {score}", True, WHITE)
screen.blit(score_surf, (10, 10))

Stap 4: 🔥 Expert: geluid en win-scherm

Nu voeg je een springgeluid toe en een win-scherm als alle sterren verzameld zijn.

Geluid laden (dit staat al klaar in de starter; controleer dat jump.wav in de sounds/ map zit):

pygame.mixer.init()
jump_snd = pygame.mixer.Sound("sounds/jump.wav")

Geluid afspelen bij springen: voeg toe aan de sprong-code:

if keys[pygame.K_SPACE] and on_ground:
    vel_y = -12
    jump_snd.play()

Win-scherm: voeg toe na de ster-loop:

if len(stars) == 0:
    win_surf = font.render("Gewonnen! Alle sterren verzameld!", True, YELLOW)
    screen.blit(win_surf, (WIDTH // 2 - win_surf.get_width() // 2, HEIGHT // 2))
    pygame.display.flip()
    pygame.time.wait(3000)
    running = False

Showcase

Laat je spel zien aan een coach en een buddy. Kun je alle sterren verzamelen voor de tijd om is?

Dojo Defender

De Dojo Defender snapshot van vandaag bouwt verder op sessie 5:

  • Parallax sterrenachtergrond — drie lagen sterren met eigen snelheid
  • Particle effects — uitlaatgassen, explosiepuin, vonkjes
  • Vernietigbare asteroïden — big → med → small → weg

Download de Dojo Defender starter of oplossing.

Tot de volgende keer!

“Volgende keer: klik op bugs en ZAP ze weg, met je eigen klassen.”

Neem mee naar huis

Probeer thuis één van deze uitbreidingen:

  1. Makkelijk: verander de kleur van de speler als hij in de lucht is.
  2. Middel: voeg een timer toe. De speler heeft 30 seconden om alle sterren te verzamelen.
  3. Lastig: voeg een vijand toe die heen en weer loopt op een platform; als de speler hem raakt, herstart het spel.
  4. Erg lastig: laat de speler dubbel springen (twee keer SPATIE voor een tweede sprong in de lucht).

Vastgelopen? Vraag het volgende dojo aan een coach, of probeer gewoon iets anders. Programmeren is doen.

Bekijk de cheatsheet voor deze sessie

1 juli 2026, 22:43

Sessie 7: Bug Zapper

Bugs vallen van boven naar beneden. Jij schiet omhoog om ze te raken. In deze sessie leer je OOP: je schrijft je eerste eigen klasse (Bug), en daarna twee subklassen: een die slingert en een die op je duikt. Klassen zijn de bouwstenen van bijna alle grote programma’s.

Wat je vandaag leert

  • Een klasse schrijven met __init__ en eigen methoden
  • Overerving: een subklasse die een bestaande klasse uitbreidt
  • Bullets (kogels) bijhouden in een lijst
  • Botsingsdetectie tussen kogels en bugs

Stap 0: Installeren

  1. Open Thonny.
  2. Download de starter via deze link, pak de ZIP uit en open main.py in Thonny.
  3. Klik op Run. Je ziet een scherm met een speler onderaan, maar nog geen bugs en geen kogels.
  4. Problemen? Vraag een coach.

Stap 1: Bugs spawnen en schieten

De Bug-klasse

Zoek de # STAP 1 — BUG KLASSE comment en schrijf:

class Bug:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.alive = True

    def update(self):
        self.y += 2  # valt naar beneden

    def draw(self, screen):
        pygame.draw.circle(screen, GREEN, (int(self.x), int(self.y)), 15)
        # Ogen
        pygame.draw.circle(screen, BLACK, (int(self.x) - 5, int(self.y) - 4), 3)
        pygame.draw.circle(screen, BLACK, (int(self.x) + 5, int(self.y) - 4), 3)

Bugs laten spawnen: de starter heeft al een timer. Zoek # SPAWN BUG:

bugs.append(Bug(random.randint(30, WIDTH - 30), -20))

Schieten: zoek # SCHIET (dit staat in de event-loop bij K_SPACE):

bullets.append(pygame.Rect(player_x - 3, player_y - 20, 6, 12))

Update bullets elke frame (zoek # UPDATE BULLETS):

for b in bullets[:]:
    b.y -= 10
    if b.y < 0:
        bullets.remove(b)
pygame.draw.rect(screen, YELLOW, b)  # dit staat al klaar

Stap 2: ✅ Basic: bugs raken

Nu schrijf je de botsingsdetectie: als een kogel een bug raakt, sterft de bug.

Zoek # STAP 2 — BOTSING:

for bug in bugs[:]:
    bug.update()
    bug.draw(screen)
    if bug.y > HEIGHT + 20:
        bugs.remove(bug)
        continue
    bug_rect = pygame.Rect(bug.x - 15, bug.y - 15, 30, 30)
    for bullet in bullets[:]:
        if bullet.colliderect(bug_rect):
            bug.alive = False
            bullets.remove(bullet)
            score += 1
            break
    if not bug.alive:
        bugs.remove(bug)

Klik Run. Schiet op de bugs: ze verdwijnen bij een treffer!

Stap 3: ⭐ Stretch: SlingerBug

Nu maak je een subklasse: een Bug die heen en weer slingert.

import math

class SlingerBug(Bug):
    def __init__(self, x, y):
        super().__init__(x, y)
        self.start_x = x
        self.timer = 0

    def update(self):
        self.timer += 0.05
        self.x = self.start_x + math.sin(self.timer) * 60
        self.y += 1.5  # iets langzamer, maar moeilijker te raken

    def draw(self, screen):
        super().draw(screen)
        # Oranje cirkel eromheen als visuele aanwijzing
        pygame.draw.circle(screen, ORANGE, (int(self.x), int(self.y)), 18, 2)

Spawnen van SlingerBugs: voeg toe aan de spawn-code (bijv. elke 3e bug):

if random.random() < 0.3:
    bugs.append(SlingerBug(random.randint(30, WIDTH - 30), -20))
else:
    bugs.append(Bug(random.randint(30, WIDTH - 30), -20))

super().__init__(x, y) roept de __init__ van de ouderklas (Bug) aan, dus je hoeft self.x, self.y en self.alive niet opnieuw te schrijven.

Stap 4: 🔥 Expert: DuikBug

De DuikBug detecteert de positie van de speler en duikt er recht op af.

class DuikBug(Bug):
    def __init__(self, x, y, player_x_ref):
        super().__init__(x, y)
        self.player_x_ref = player_x_ref  # functie die huidige speler-x geeft
        self.speed_x = 0
        self.speed_y = 1

    def update(self):
        target_x = self.player_x_ref()
        dx = target_x - self.x
        dist = max(abs(dx), 1)
        self.x += dx / dist * 2
        self.y += 2.5

    def draw(self, screen):
        super().draw(screen)
        pygame.draw.circle(screen, RED, (int(self.x), int(self.y)), 18, 2)

Spawnen: gebruik een lambda om de speler-x door te geven:

bugs.append(DuikBug(random.randint(30, WIDTH - 30), -20, lambda: player_x))

Bonus: Teken een HP-balk. Start met lives = 3 en trek er één af als een bug de onderkant haalt.

Showcase

Laat je spel zien aan een coach en een buddy. Hoeveel bugs kun je neerzappen voor je levens op zijn?

Tot de volgende keer!

“Volgende keer: een oneindige weg in de lucht, en alles SCROLT voorbij.”

Neem mee naar huis

Probeer thuis één van deze uitbreidingen:

  1. Makkelijk: maak bugs groter of sneller naarmate de score oploopt.
  2. Middel: voeg een lives-systeem toe met 3 levens, en als een bug de onderkant haalt, verlies je er één.
  3. Lastig: schrijf een PantserbBug die 3 treffers nodig heeft voor hij sterft (gebruik een hp-attribuut).
  4. Erg lastig: voeg muisbesturing toe: de speler beweegt naar de muispositie en schiet automatisch elke 0,5 seconde.

Vastgelopen? Vraag het volgende dojo aan een coach, of probeer gewoon iets anders. Programmeren is doen.

Dojo Defender: Eindbaas

Deze sessie bouw je ook verder aan Dojo Defender. Je voegt een eindbaas (boss) toe die om de 5 waves verschijnt met 3 fases.

Bekijk de Dojo Defender sessie 6 voor de instructies.

De starter en oplossing staan klaar.


Bekijk de cheatsheet voor deze sessie

1 juli 2026, 22:43

Sessie 8: Sky Highway

Je schip vliegt door een eindeloze luchtweg. De achtergrond scrolt langs je heen, rode obstakels komen van rechts, en sterren geven punten. Hoe langer je overleeft, hoe sneller het wordt!

Wat je vandaag leert

  • Een scrollende achtergrond maken met een scroll_x variabele
  • Objecten spawnen die van rechts naar links bewegen
  • Botsingsdetectie met levens
  • Moeilijkheidsscaling: snelheid verhogen naarmate de score stijgt

Stap 0: Installeren

  1. Open Thonny.
  2. Download de starter via deze link, pak de ZIP uit en open main.py in Thonny.
  3. Klik op Run. Je ziet een statisch scherm met een schip en een luchtachtergrond, maar niks beweegt nog.
  4. Problemen? Vraag een coach.

Stap 1: Achtergrond laten scrollen

Een scrollende achtergrond geeft de illusie van beweging. We tekenen de achtergrond twee keer naast elkaar en schuiven de positie elke frame op.

Zoek # STAP 1 en voeg toe:

# Beweeg achtergrond naar links
scroll_x -= speed
if scroll_x <= -WIDTH:
    scroll_x = 0

# Teken achtergrond twee keer
screen.blit(bg_image, (int(scroll_x), 0))
screen.blit(bg_image, (int(scroll_x) + WIDTH, 0))

scroll_x begint op 0 en wordt elke frame kleiner. Als het -WIDTH bereikt, springen we terug naar 0: een naadloze loop.

Klik Run. De lucht scrolt nu voorbij je schip!

Stap 2: ✅ Basic: obstakels en levens

Obstakels komen van rechts en bewegen naar links. Als je ze raakt, verlies je een leven.

Obstakels updaten (zoek # STAP 2):

for obs in obstacles[:]:
    obs.x -= speed
    if obs.x < -obs.width:
        obstacles.remove(obs)
    pygame.draw.rect(screen, RED, obs)
    # Botsing
    if ship_rect.colliderect(obs):
        obstacles.remove(obs)
        lives -= 1
        if lives <= 0:
            running = False

Levens tekenen:

for i in range(lives):
    pygame.draw.rect(screen, RED, (10 + i * 25, 10, 18, 18))

Obstakels spawnen (in de spawn-timer-event):

h = random.randint(40, 120)
obstacles.append(pygame.Rect(WIDTH, random.randint(50, HEIGHT - h - 50), 30, h))

Stap 3: ⭐ Stretch: sterren verzamelen

Sterren geven punten en verdwijnen als je ze raakt.

Zoek # STAP 3:

for star in stars[:]:
    star.x -= speed
    if star.x < -20:
        stars.remove(star)
        continue
    pygame.draw.circle(screen, YELLOW, star.center, 10)
    if ship_rect.colliderect(star):
        stars.remove(star)
        score += 1

# Score tekenen:
score_surf = font.render(f"Score: {score}", True, WHITE)
screen.blit(score_surf, (WIDTH - 120, 10))

Spawnen van sterren (in dezelfde spawn-timer, maar minder vaak):

if random.random() < 0.4:
    stars.append(pygame.Rect(WIDTH, random.randint(40, HEIGHT - 40), 20, 20))

Stap 4: 🔥 Expert: spawnen en snelheid schalen

Nu maak je de game steeds moeilijker naarmate de score stijgt.

Snelheid schalen (zoek # STAP 4):

speed = BASE_SPEED + score * 0.1

BASE_SPEED = 4 staat al bovenaan. Bij score 20 gaat alles dubbel zo snel!

Spawn-interval aanpassen: hoe hoger de score, hoe vaker nieuwe obstakels komen:

spawn_interval = max(500, 1800 - score * 30)  # min. 500ms
pygame.time.set_timer(SPAWN_EVENT, spawn_interval)

Game over scherm:

if not running:
    go_surf = font.render("GAME OVER", True, RED)
    screen.blit(go_surf, (WIDTH // 2 - go_surf.get_width() // 2, HEIGHT // 2 - 30))
    sc_surf = font.render(f"Score: {score}", True, WHITE)
    screen.blit(sc_surf, (WIDTH // 2 - sc_surf.get_width() // 2, HEIGHT // 2 + 10))
    pygame.display.flip()
    pygame.time.wait(3000)

Showcase

Laat je spel zien aan een coach en een buddy. Wie haalt de hoogste score?

Tot de volgende keer!

“Volgende keer: een baas die terugvecht, in DRIE fases.”

Neem mee naar huis

Probeer thuis één van deze uitbreidingen:

  1. Makkelijk: voeg een tweede soort obstakel toe (bijv. een blauw obstakel dat hoger of lager vliegt).
  2. Middel: voeg power-ups toe. Vang een groene capsule voor een tijdelijk schild (het schip kan 1 keer geraakt worden).
  3. Lastig: laat het schip omhoog en omlaag bewegen met de pijltjestoetsen én begrens het scherm zodat het schip er niet uit vliegt.
  4. Erg lastig: voeg parallax-scrolling toe: een tweede laag achtergrond (wolken) die langzamer scrolt dan de eerste laag.

Vastgelopen? Vraag het volgende dojo aan een coach, of probeer gewoon iets anders. Programmeren is doen.


Dojo Defender: Jouw eigen versie

Vandaag werk je ook verder aan Dojo Defender — maar niet met stap-voor-stap instructies. Jij kiest wat je bouwt.

Download de Dojo Defender starter — dit is de complete game met alles erop en eraan (schip, vijanden, asteroïden, waves, boss). Kies een feature uit de lijst, plan hem kort met een coach, en ga aan de slag.

👉 Alles over Dojo Defender vind je op de Dojo Defender pagina.


Bekijk de cheatsheet voor deze sessie

1 juli 2026, 22:43

Sessie 9: Boss Battle

De eindbaas wacht op je. Hij beweegt heen en weer, schiet kogels en wordt steeds gevaarlijker naarmate zijn HP daalt. Jij schiet terug, houdt zijn HP bij op een balk, en overleeft drie fasen. Dit is de moeilijkste sessie, en de meest bevredigende als je hem haalt.

Wat je vandaag leert

  • Een HP-balk tekenen op het scherm
  • Een state machine: de baas gedraagt zich anders per fase
  • De baas laten schieten met een kogeltimer
  • Fasen activeren op HP-drempelwaarden

Stap 0: Installeren

  1. Open Thonny.
  2. Download de starter via deze link, pak de ZIP uit en open main.py in Thonny.
  3. Klik op Run. Je ziet de baas bovenaan, jouw schip onderaan. Geen movement, geen kogels nog.
  4. Problemen? Vraag een coach.

Stap 1: Baas laten bewegen

De baas beweegt heen en weer over het scherm. Als hij de rand raakt, keert hij om.

Zoek # STAP 1 en voeg toe:

boss_rect.x += boss_speed

# Omdraaien bij de rand
if boss_rect.right >= WIDTH or boss_rect.left <= 0:
    boss_speed = -boss_speed

boss_speed = 3 staat al bovenaan. Klik Run: de baas dendert van links naar rechts!

Speler bewegen en schieten (zoek # SPELER):

keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and player_rect.left > 0:
    player_rect.x -= PLAYER_SPEED
if keys[pygame.K_RIGHT] and player_rect.right < WIDTH:
    player_rect.x += PLAYER_SPEED

# Schiet (zoek # SCHIET in event-loop):
player_bullets.append(pygame.Rect(player_rect.centerx - 3, player_rect.top - 15, 6, 15))

Stap 2: ✅ Basic: baas raken en HP-balk

Botsing speler-kogel met baas:

Zoek # STAP 2:

for b in player_bullets[:]:
    b.y -= 8
    if b.y < 0:
        player_bullets.remove(b)
        continue
    pygame.draw.rect(screen, YELLOW, b)
    if b.colliderect(boss_rect):
        player_bullets.remove(b)
        boss_hp -= 1
        if boss_hp <= 0:
            running = False  # speler wint!

HP-balk tekenen (zoek # HP BALK):

# Achtergrond van de balk
pygame.draw.rect(screen, DARK_RED, (10, 10, 300, 20))
# Gevulde balk op basis van huidige HP
breedte = int(300 * boss_hp / BOSS_MAX_HP)
pygame.draw.rect(screen, RED, (10, 10, breedte, 20))
# Label
hp_surf = font.render(f"Baas HP: {boss_hp}", True, WHITE)
screen.blit(hp_surf, (320, 10))

Klik Run. Schiet op de baas: de HP-balk slinkt!

Stap 3: ⭐ Stretch: baas schiet terug

De baas schiet elke N frames een kogel naar beneden.

Zoek # STAP 3:

boss_shoot_timer += 1
if boss_shoot_timer >= boss_shoot_interval:
    boss_shoot_timer = 0
    boss_bullets.append(pygame.Rect(boss_rect.centerx - 4, boss_rect.bottom, 8, 16))

Baas-kogels updaten:

for b in boss_bullets[:]:
    b.y += 6
    if b.y > HEIGHT:
        boss_bullets.remove(b)
        continue
    pygame.draw.rect(screen, ORANGE, b)
    if b.colliderect(player_rect):
        boss_bullets.remove(b)
        player_lives -= 1
        if player_lives <= 0:
            running = False

Teken player_lives als hartjes of rode blokjes onderaan het scherm.

Stap 4: 🔥 Expert: drie fases

Nu reageert de baas op zijn HP:

  • Fase 1 (HP 100 tot 50): normaal gedrag
  • Fase 2 (HP 50 tot 25): sneller bewegen, vaker schieten
  • Fase 3 (HP < 25): maximale snelheid, dubbele kogels

Zoek # STAP 4:

if boss_hp > 50:
    boss_phase = 1
    boss_speed_abs = 3
    boss_shoot_interval = 60
elif boss_hp > 25:
    boss_phase = 2
    boss_speed_abs = 5
    boss_shoot_interval = 35
else:
    boss_phase = 3
    boss_speed_abs = 7
    boss_shoot_interval = 20

# Pas de snelheid aan zonder de richting te wisselen:
boss_speed = boss_speed_abs * (1 if boss_speed > 0 else -1)

Fase 3: dubbele kogels (voeg toe in de shoot-code):

if boss_phase == 3:
    boss_bullets.append(pygame.Rect(boss_rect.left + 10, boss_rect.bottom, 8, 16))
    boss_bullets.append(pygame.Rect(boss_rect.right - 18, boss_rect.bottom, 8, 16))
else:
    boss_bullets.append(pygame.Rect(boss_rect.centerx - 4, boss_rect.bottom, 8, 16))

Win-scherm bij boss_hp <= 0:

screen.fill(BLACK)
win = font.render("BAAS VERSLAGEN!", True, YELLOW)
screen.blit(win, (WIDTH // 2 - win.get_width() // 2, HEIGHT // 2))
pygame.display.flip()
pygame.time.wait(3000)

Showcase

Laat je spel zien aan een coach en een buddy. Wie haalt fase 3 en overleeft?

Tot de volgende keer!

“Volgende keer: jouw eigen spel, van nul, helemaal jouw idee.”

Neem mee naar huis

Probeer thuis één van deze uitbreidingen:

  1. Makkelijk: verander de kleur van de baas per fase (groen → oranje → rood).
  2. Middel: voeg een score toe: hoe sneller je de baas verslaat, hoe meer punten.
  3. Lastig: laat de baas in fase 2 ook diagonale kogels schieten (schuin links en rechts).
  4. Erg lastig: voeg een tweede speler toe die mee kan doen met WASD-toetsen.

Vastgelopen? Vraag het volgende dojo aan een coach, of probeer gewoon iets anders. Programmeren is doen.

Bekijk de cheatsheet voor deze sessie

14 juni 2026, 13:39

Sessie 10: Mijn Spel

Dit is jouw sessie. Geen vaste stappen, geen verplichte mechanics. Jij beslist wat je bouwt, en vandaag maak je het.

Jouw spel, jouw regels

In negen sessies heb je geleerd:

  • Variabelen, functies en game loops
  • Toetsenbord- en muisinput
  • Sprites, collision detection en score
  • Geluid en animatie
  • Klassen en overerving
  • Scrollende achtergronden, spawnen en moeilijkheidsscaling
  • HP-balken, state machines en baas-AI

Al die bouwstenen liggen klaar. Vandaag combineer je ze in je eigen spel.

Kies je concept

Nog geen idee? Hier zijn wat genres om van te starten:

Dodge-game: wijk objecten uit die steeds sneller komen. Simpel maar verslavend.

Platformer: spring van platform naar platform, verzamel items, vermijd vijanden.

Shooter: schiet vijanden neer die van boven of van de zijkant komen.

Puzzelgame: schuif blokjes, verbind punten, of los een patroon op.

Racegame: ontwerp een parcours en beweeg zo snel mogelijk naar de finish.

Vertel een verhaal: geen score, geen levens. Laat de speler klikken en keuzes maken.

Je mag ook iets combineren: een shooter met puzzelelementen, een platformer met een baas aan het einde. Alles mag.

Plan je spel

Vóór je code schrijft, beantwoord deze drie vragen:

  1. Wat doet de speler? (bewegen, schieten, springen, klikken, …)
  2. Wat is het doel? (score halen, de baas verslaan, het einde bereiken, …)
  3. Wat maakt het moeilijk? (vijanden, obstakels, een timer, …)

Schrijf je antwoorden op een papiertje of in een comment bovenaan je code. Dit helpt je gefocust te blijven als je vastloopt.

Animatie: sprite wisselen per frame

Wil je een loopanimatie, een explosie of een knipperend object? Maak een lijst met afbeeldingsnamen en wissel actor.image elke N milliseconden. De demo hieronder laat zien hoe dat werkt: gebruik de schuifbalk om de snelheid aan te passen en de knop om te pauzeren:

In Python gebruik je dan een lijst met afbeeldingsnamen en een timer:

frames = ['lopen_1', 'lopen_2', 'lopen_3', 'lopen_4']
anim_timer = 0
FRAME_DUUR = 10   # frames (bij 60 fps = ~167 ms)

def update():
    global anim_timer
    anim_timer += 1
    speler.image = frames[(anim_timer // FRAME_DUUR) % len(frames)]

Aan de slag

Download de starter via deze link. De starter heeft:

  • Een werkende pygame game loop
  • Een lege draw()- en update()-sectie
  • Kleur-constanten en een lettertypevariabele klaar
  • Comments die aangeven waar je wat kunt plaatsen

Open main.py en begin met Stap 1 van je eigen plan. De cheatsheet op deze pagina heeft alle patronen van de vorige sessies bij de hand.

Vastgelopen? Vraag een coach. Beschrijf wat je wilt bereiken, niet alleen wat niet werkt. Dan kunnen we samen de beste aanpak bedenken.

Te snel klaar? Voeg geluid toe, maak een start-scherm, of voeg een tweede speler toe.

Showcase

Aan het einde van de sessie presenteert iedereen zijn spel. Je hoeft het niet af te hebben. Laat zien wat je gemaakt hebt en vertel:

  • Wat is het concept van je spel?
  • Welke techniek uit een vorige sessie heb je gebruikt?
  • Wat zou je nog willen toevoegen als je meer tijd had?

Wat nu?

CoderDojo stopt na tien sessies. Zelf bouwen stopt niet.

Blijf bouwen. Kleine projectjes leren je meer dan grote cursussen. Maak een spel voor je broer of zus, je huisdier, of gewoon voor jezelf.

Inspiratie nodig?

CoderDojo komt terug. Jij ook?

Blijf bouwen!

Je hebt in tien sessies een platformer, een shooter, een boss battle en meer gebouwd. Neem die code mee naar huis, open hem volgende week opnieuw, en voeg gewoon één ding toe. Dat is hoe programmeurs werken.

Bekijk de cheatsheet voor deze sessie

13 juni 2026, 13:30

Git sessies

Vijf sessies over Git en GitHub, van je eerste commit tot pull requests. Plus een bonus-sessie over Markdown, de taal waarin je notities en documentatie schrijft.

13 juni 2026, 13:30

Subsecties van Git sessies

Sessie 11: Git: Jouw code-geschiedenisboek

Stel je voor: je werkt drie uur aan je game. Je voegt een cool nieuw wapen toe. Maar… nu doet de rest van je game het niet meer. En je weet niet meer wat je precies veranderd hebt. Weg drie uur werk.

Git is de oplossing. Git onthoudt élke versie van je bestanden. Als een oneindige “undo”-knop die nooit vergeet wat je gedaan hebt.

In deze sessie installeer je Git, maak je je eerste repository en leg je voor altijd vast wat je doet: stap voor stap, versie voor versie. En het leuke: Git werkt niet alleen voor code. Ook voor notities, schoolverslagen en verhalen.

Wat is Git?

Git is een versiebeheersysteem. Het houdt bij wat jij verandert in je bestanden, wanneer je dat deed, en waarom. Elke keer dat je een stukje werk af hebt, maak je een commit: een foto van al je bestanden op dat moment.

Wat Git bijzonder maakt:

  • Elke versie is bewaard. Je kunt altijd terug naar een vorige commit als iets kapot gaat.
  • Je werkt lokaal. Git draait op jouw computer. Geen internet nodig.
  • Git is snel. Zelfs projecten met duizenden bestanden controleren in milliseconden.

Git is gemaakt door Linus Torvalds, dezelfde persoon die Linux bouwde. Hij had iets nodig om met duizenden programmeurs tegelijk aan de Linux-kernel te werken. In 2005 bouwde hij Git in een weekend. Vandaag gebruikt bijna elke ontwikkelaar ter wereld het.

Git is niet alleen voor code

Git is gebouwd voor broncode, maar het werkt met alle tekstbestanden. Een tekstbestand is elk bestand dat je met Kladblok of een simpele editor kunt openen en lezen: .py, .html, .css, .json, .txt, .csv, .md (Markdown), .yml, .toml, noem maar op.

Wat Git níet goed kan: binaire bestanden. Dat zijn bestanden die niet uit leesbare tekst bestaan: afbeeldingen (.png, .jpg), video’s (.mp4), gecompileerde programma’s (.exe), Word-documenten (.docx). Git kan ze wél bijhouden, maar het is trager en je kunt niet zien wát er precies veranderd is. Bij tekstbestanden toont git diff elke letter die je toevoegde of weghaalde. Bij een .png zegt Git alleen “dit bestand is gewijzigd”, maar niet wát er aan de afbeelding veranderde.

Markdown en notities

Veel notitie-apps van tegenwoordig gebruiken Markdown (.md-bestanden) om je documenten op te slaan. Denk aan:

  • Obsidian: je notities zijn gewoon .md-bestanden in een map op je computer
  • Notion: exporteert naar Markdown
  • Joplin: slaat alles op als Markdown
  • Typora, Zettlr, Logseq: allemaal Markdown-gebaseerd

Omdat Markdown-bestanden gewone tekstbestanden zijn, kun je er Git op loslaten. Je houdt de geschiedenis van je notities bij, je kunt terug naar een vorige versie van een document, en je kunt je notities delen via GitHub, precies zoals je met code doet.

Een map met Markdown-notities onder Git zetten is even simpel als een Python-project:

mkdir mijn-notities
cd mijn-notities
git init
echo "# Mijn Notities" > start.md
git add start.md
git commit -m "Eerste notitie: startpagina"

Vanaf dat moment bewaart Git elke wijziging aan je notities. Schrijf je een verslag voor school in Markdown? Commit elke paragraaf. Werk je aan wereldbouw voor een verhaal? Git onthoudt elke versie van je plot.

Wanneer Git wél en níet voor notities gebruiken

Wél Git gebruikenBeter iets anders
Notities in Markdown (.md)Notities in Word (.docx)
Configuratiebestanden (.json, .yml)Grote afbeeldingen of video’s bij je notities
Schrijfwerk: verslagen, verhalen, blogsRealtime samenwerking zoals Google Docs (daar is Git te traag voor)
Je CoderDojo-cheatsheetsWachtwoorden of persoonlijke dagboeken (tenzij private repo)

Kortom: alles wat tekst is, kun je in Git stoppen. Alles wat binair is, beter niet.

Waarom versiebeheer?

Zonder Git ziet je workflow er zo uit:

game.py
game_backup.py
game_backup2.py
game_final.py
game_final_echt.py
game_final_echt_nieuwe_wapens.py

Herkenbaar? Met Git heb je maar één bestand nodig: game.py. Git onthoudt de rest.

Wat versiebeheer je oplevert:

  • Nooit meer werk kwijt. Alles wat je commit, blijft bestaan. Altijd.
  • Begrijpen wat er gebeurd is. Je ziet exact welke regels je wanneer veranderde.
  • Experimenteren zonder risico. Maak een aparte kopie van je code (een branch), probeer iets geks, en gooi het weg als het niet werkt. Je originele code blijft intact.
  • Samenwerken. Meerdere mensen kunnen tegelijk aan hetzelfde project werken, zonder elkaars code te overschrijven.

Wat stop je NIET in Git?

Git is gemaakt voor tekstbestanden: bestanden die jij zelf schrijft: code, notities, configuratie. Er zijn dingen die je beter buiten Git houdt:

Stop wél in GitStop NIET in Git
Je Python-bestanden (.py)Wachtwoorden, API-sleutels, tokens
Afbeeldingen die je game gebruiktBestanden die je programma zelf maakt (.exe, .pyc)
Geluidsbestanden (.wav, .mp3)De __pycache__ map
Tekstbestanden, configuratieGrote bestanden (>50 MB) zoals video’s
Je README.mdInstellingen van jouw specifieke computer

Waarom geen wachtwoorden in Git? Omdat Git álles onthoudt, voor altijd. Zelfs als je later een wachtwoord verwijdert, zit het nog in de geschiedenis. Iedereen die jouw code ooit ziet, kan terugscrollen naar die commit. En als je code ooit op GitHub komt, staat je wachtwoord op het internet. Voor eeuwig.

Gebruik een .gitignore-bestand om Git te vertellen welke bestanden het moet negeren. Daar kom je zo meteen achter.

Stap 0: Git installeren

Windows

  1. Ga naar git-scm.com en download de Windows-installer.
  2. Dubbelklik het gedownloade bestand.
  3. Klik door de installatie. De standaardopties zijn prima.
  4. Belangrijk: kies bij het kiezen van de teksteditor Nano of Notepad++ (niet Vim, tenzij je Vim al kent).
  5. Open na de installatie Git Bash (zoek ernaar in het startmenu).

Mac

Open de Terminal (zoek naar “Terminal” in Spotlight) en typ:

git --version

Als Git niet geïnstalleerd is, vraagt macOS je om de developer tools te installeren. Klik op “Install”. Klaar.

Linux (Ubuntu/Debian)

sudo apt install git

Controleren of het werkt

Open een terminal (Git Bash op Windows) en typ:

git --version

Je ziet iets als git version 2.47.0. Dat betekent: Git staat klaar.

Stap 1: Vertel Git wie je bent

Git wil weten wie jij bent, zodat het jouw naam bij elke commit kan zetten. Dit doe je één keer, daarna onthoudt Git het.

git config --global user.name "Jouw Naam"
git config --global user.email "jouw@email.com"

Gebruik hetzelfde e-mailadres dat je straks voor GitHub gebruikt. Dat maakt koppelen makkelijker.

Tip: --global betekent: deze instelling geldt voor ál je Git-projecten. Je hoeft dit maar één keer te doen.

Stap 2: Je eerste repository maken

Een repository (of “repo”) is een map waar Git de versies bijhoudt. Laten we er één maken.

Maak eerst een nieuwe map voor je project:

mkdir mijn-eerste-git-project
cd mijn-eerste-git-project

Nu vertel je Git: “Deze map is een repository.”

git init

Je ziet: Initialized empty Git repository in .../mijn-eerste-git-project/.git/

Git heeft een verborgen map .git aangemaakt. Daar bewaart het álles: de geschiedenis, de commits, de configuratie. Raak die map nooit met de hand aan, want Git beheert hem zelf.

Stap 3: Je eerste bestand committen

Maak een bestand aan. Bijvoorbeeld een Python-bestand met één regel:

echo 'print("Hallo Git!")' > hallo.py

Nu vertel je Git: “Let op dit bestand.”

git add hallo.py

Met git add zet je bestanden in de staging area: een tussenstation. Je zegt tegen Git: “Deze wijzigingen wil ik in mijn volgende commit.”

Controleer wat er klaarstaat:

git status

Git toont:

On branch master
Changes to be committed:
  new file:   hallo.py

Nu komt de commit zelf:

git commit -m "Mijn eerste commit: hallo.py toegevoegd"

Wat gebeurt hier? -m staat voor “message”. De tekst tussen aanhalingstekens is je commit message: een korte uitleg van wat je veranderd hebt. Git heeft nu een permanente foto van je project gemaakt.

Goede commit messages: Schrijf in het Engels (dat is de afspraak in bijna alle projecten). Begin met een werkwoord: “Add”, “Fix”, “Update”. Hou het kort: maximaal 72 karakters.

Stap 4: Veranderingen bijhouden

Open hallo.py in een teksteditor. Verander de regel naar:

print("Hallo Git! Jij onthoudt mijn code.")

Sla het bestand op. Kijk nu wat Git ziet:

git status

Git weet dat hallo.py gewijzigd is. Maar wát is er precies veranderd?

git diff

git diff toont exact welke regels je toegevoegd (groen met +) en verwijderd (rood met -) hebt. Dit is één van de krachtigste dingen van Git: je ziet letterlijk wat er veranderd is, karakter voor karakter, sinds je laatste commit.

Commit de wijziging:

git add hallo.py
git commit -m "Update: begroeting uitgebreid"

Stap 5: Terug in de tijd

Dit is waar Git magisch wordt. Stel: je hebt spijt van je laatste wijziging. Je wilt terug naar de eerste versie.

Toon eerst alle commits:

git log --oneline

Je ziet zoiets:

a1b2c3d Update: begroeting uitgebreid
e4f5g6h Mijn eerste commit: hallo.py toegevoegd

Elke commit heeft een unieke code, een hash. Die lange code (verkort tot 7 karakters) identificeert exact die ene versie van je project.

Om te zien hoe hallo.py eruitzag in de eerste commit:

git show e4f5g6h:hallo.py

Daar is je originele print("Hallo Git!") weer.

Wil je écht terug in de tijd? Dat kan:

git checkout e4f5g6h -- hallo.py

Nu staat hallo.py weer zoals in je eerste commit. Controleer met cat hallo.py. Je ziet de oude versie. Maar maak je geen zorgen: je tweede commit is niet weg. Met git checkout master -- hallo.py ga je weer naar de nieuwste versie.

Stap 6: Wat níet in Git: .gitignore

Maak een bestand aan dat Git moet negeren:

echo "GEHEIM_WACHTWOORD=12345" > geheimen.txt

git status toont dit bestand nu als “untracked”. Git heeft het gezien, maar weet niet of je het wilt bijhouden. Je kunt het negeren met een .gitignore-bestand:

echo "geheimen.txt" > .gitignore

Nu git status opnieuw en geheimen.txt is verdwenen uit de lijst. Git negeert het. (Voeg .gitignore zelf wél toe aan Git met git add .gitignore en git commit, zodat anderen ook weten wat er genegeerd moet worden.)

Een goed .gitignore voor een Python-project:

# Python
__pycache__/
*.pyc
*.pyo

# Geheimen
*.env
secrets.txt

# Jouw editor
.vscode/
.idea/

Showcase

Laat aan een coach en een buddy zien:

  1. Je repository met minstens 3 commits.
  2. Het resultaat van git log --oneline: jouw commit-geschiedenis.
  3. Een git diff die toont wat je veranderd hebt.
  4. Je .gitignore-bestand.

Tot de volgende keer!

“Volgende keer: twee versies van je code tegelijk. Je werkt aan een nieuwe feature terwijl de oude versie gewoon blijft werken. Dat heet branches, en het is wat Git écht krachtig maakt.”

Neem mee naar huis

  1. Makkelijk: Maak een repository voor je notities. Zet een Markdown-bestand met wat je vandaag geleerd hebt in Git, en commit het.
  2. Middel: Verander iets in je code of notities, gebruik git diff om de wijziging te bekijken, en commit hem met een goede message.
  3. Lastig: Maak drie commits met verschillende wijzigingen. Gebruik git show om elke versie te bekijken. Gebruik git checkout om terug te gaan naar de eerste commit, en daarna weer naar de laatste.
  4. Erg lastig: Maak een Python-project met een goede .gitignore. Zorg dat __pycache__/ en .pyc-bestanden écht genegeerd worden (check met git status).

Vastgelopen? Vraag het volgende dojo aan een coach, of probeer gewoon iets anders. Programmeren is doen. Git leren is doen.

13 juni 2026, 13:30

Sessie 12: Git Branches: Parallelle universums voor je code

Vorige sessie leerde je commits maken: foto’s van je code op één tijdlijn. Alles netjes achter elkaar.

Maar wat als je een nieuw idee wil uitproberen? Een lasergun toevoegen aan je game, bijvoorbeeld. Je weet niet of het gaat werken. Je wil je werkende code niet kapot maken.

Daar zijn branches voor.

Een branch is een kopie van je code waar je vrij kunt experimenteren. Je hoofdcode (de master-branch) blijft veilig. Als je experiment werkt, plak je het terug in master. Werkt het niet? Je gooit de branch weg. Je master-branch heeft er nooit iets van gemerkt.

Noot: Op GitHub en in veel nieuwe projecten heet de hoofdbranch main in plaats van master. Dat is exact hetzelfde, alleen de naam is anders. In deze cursus gebruiken we master, maar als je online main tegenkomt, weet je dat het hetzelfde betekent.

Wat je vandaag leert

  • Wat een branch is en hoe Git branches intern opslaat
  • Branches aanmaken, wisselen en samenvoegen
  • Wat een merge conflict is en hoe je het oplost
  • Wanneer je wél en níet een branch moet gebruiken

Stap 0: Herhaling: waar sta je nu?

Open je terminal in het project van vorige sessie. Check waar je bent:

git status
git log --oneline

Je hebt een paar commits op master. Dit is je stabiele basis: hier ga je straks vanaf splitsen.

Stap 1: Je eerste branch

Je gaat een nieuwe feature toevoegen: een highscore.txt-bestand dat de hoogste score bijhoudt. Maar eerst maak je een branch:

git branch highscore-feature

Git heeft nu een nieuwe branch aangemaakt. Maar je staat nog steeds op master. Check welke branches er zijn:

git branch

Je ziet:

  highscore-feature
* master

Het sterretje bij master betekent: “hier sta je nu.” Wissel naar je nieuwe branch:

git switch highscore-feature

(Oudere Git-versies gebruiken git checkout highscore-feature. Beide werken.)

Nog een keer git branch: het sterretje staat nu bij highscore-feature. Alles wat je nu commit, gebeurt op deze branch. master blijft onaangeroerd.

Tip: Je kunt een branch aanmaken en er meteen naartoe switchen in één commando:

git switch -c mijn-nieuwe-branch

-c staat voor “create”. Dit is sneller en je vergeet nooit te switchen.

Stap 2: Werk doen op een branch

Maak een nieuw bestand aan:

echo "HIGHSCORE: 0" > highscore.txt

Commit je werk:

git add highscore.txt
git commit -m "Add highscore.txt met beginwaarde 0"

Voeg nog een bestand toe dat de highscore leest:

echo 'def lees_highscore():' > highscore_manager.py
echo '    with open("highscore.txt") as f:' >> highscore_manager.py
echo '        return int(f.read().split(": ")[1])' >> highscore_manager.py

Commit opnieuw:

git add highscore_manager.py
git commit -m "Add highscore_manager: functie om highscore te lezen"

Kijk nu naar je log:

git log --oneline

Je ziet al je commits van master, plus de twee nieuwe. Git onthoudt de hele keten.

Stap 3: Twee werelden tegelijk

Switch terug naar master:

git switch master

Kijk wat er hier is:

ls

highscore.txt en highscore_manager.py zijn… weg. Ze bestaan alleen op de highscore-feature-branch. Op master is alles nog precies zoals je het achterliet.

Dit is de magie van branches: twee (of meer) versies van je project, tegelijk op je computer, zonder dat ze elkaar in de weg zitten.

Switch nog eens heen en weer om het te voelen:

git switch highscore-feature   # bestanden zijn terug
git switch master                # bestanden zijn weer weg

Stap 4: Samenvoegen met git merge

Je highscore-feature werkt. Tijd om hem terug in master te zetten. Dit heet mergen.

Zorg dat je op master staat:

git switch master

Voeg de branch samen:

git merge highscore-feature

Git toont iets als Updating a1b2c3d..e4f5g6h en Fast-forward. Je highscore-feature-commits zijn nu onderdeel van master. Controleer:

ls
git log --oneline

highscore.txt en highscore_manager.py staan nu op master. Alle commits van de feature-branch zijn opgenomen in de geschiedenis.

Stap 5: Wat zijn merge conflicts?

Stel: je hebt op master iets veranderd in highscore.txt, bijvoorbeeld “HIGHSCORE: 100” in plaats van “HIGHSCORE: 0”. En tegelijkertijd heb je op een andere branch highscore.txt ook veranderd, naar “HIGHSCORE: 500”.

Als je nu probeert te mergen, weet Git niet welke versie de juiste is. Dat is een merge conflict. Git vraagt jóu om te beslissen.

Laten we het expres uitlokken.

Op master:

echo "HIGHSCORE: 100" > highscore.txt
git add highscore.txt
git commit -m "Update highscore naar 100 op master"

Maak een nieuwe branch en verander daar hetzelfde bestand:

git switch -c andere-highscore
echo "HIGHSCORE: 500" > highscore.txt
git add highscore.txt
git commit -m "Update highscore naar 500"

Switch terug naar master en probeer te mergen:

git switch master
git merge andere-highscore

Git zegt: CONFLICT (content): Merge conflict in highscore.txt. Open highscore.txt in je editor. Je ziet:

<<<<<<< HEAD
HIGHSCORE: 100
=======
HIGHSCORE: 500
>>>>>>> andere-highscore

Dit zijn de twee versies. HEAD is jouw master-versie. Daaronder staat de versie van andere-highscore. Jij kiest welke je houdt, of je combineert ze.

Verwijder de markers en kies één versie:

HIGHSCORE: 500

Sla het bestand op. Vertel Git dat het conflict is opgelost:

git add highscore.txt
git commit -m "Merge: highscore van andere-highscore gekozen"

Conflict opgelost. Git kan weer verder.

Stap 6: Branches opruimen

Je feature is gemerged. De branch heb je niet meer nodig:

git branch -d highscore-feature
git branch -d andere-highscore

-d staat voor “delete”. Git weigert een branch te verwijderen die nog niet gemerged is: dat is een veiligheidsklep. Als je écht weg wil gooien zonder mergen, gebruik je -D (hoofdletter). Maar wees daar voorzichtig mee.

Wanneer wél een branch, wanneer niet?

Wél een branchGéén branch
Nieuwe feature die tijd kostTypo in een comment fixen
Experiment waarvan je niet weet of het werktKleine bugfix van één regel
Iets dat je werkende code kan brekenJe werkt alleen en je wijziging is duidelijk
Je werkt samen met anderen(bij solo-werk is master vaak genoeg)

Vuistregel: Als je twijfelt, maak een branch. Een branch kost niks. Code kwijtraken wel.

Tips om vlot te werken met branches

  1. Houd branches kort. Een branch van 3 uur is prima. Een branch van 3 weken wordt een merge-nachtmerrie: hoe langer je wacht met mergen, hoe meer er intussen op master verandert, en hoe groter de kans op conflicts.
  2. Eén ding per branch. “Highscore toevoegen” is een goede branch. “Highscore, menu, en nieuwe levels toevoegen” is te veel: split het op.
  3. Merge vaak. Werk je feature af, merge hem meteen terug naar master. Kleine, frequente merges zijn makkelijker dan één groot monster-merge.
  4. Geef branches duidelijke namen. highscore-feature is goed. test123 of branch2 zijn slecht. Over een week weet je niet meer wat er in test123 zat.
  5. Check waar je bent voor je begint. git branch of git status vertelt je op welke branch je staat. Maak hier een gewoonte van, zodat je voorkomt dat je per ongeluk op master code schrijft die eigenlijk op een feature-branch hoort.

Showcase

Laat aan een coach en een buddy zien:

  1. git branch met minstens twee branches (waaronder master).
  2. Op elke branch een ander bestand of andere code.
  3. Switch heen en weer: de bestanden verschijnen en verdwijnen.
  4. Een succesvolle merge van een feature-branch in master.

Tot de volgende keer!

“Tot nu toe leeft al je code alleen op jouw laptop. Volgende keer zetten we hem online. Je maakt een GitHub-account, pusht je repository naar het internet, en je game is overal ter wereld te bekijken. En: je kunt samenwerken met anderen. GitHub: je code de wereld in.”

Neem mee naar huis

  1. Makkelijk: Maak een branch voor een kleine feature in een bestaand project. Commit je werk en merge hem terug.
  2. Middel: Creëer expres een merge conflict (twee branches die hetzelfde bestand wijzigen) en los het op.
  3. Lastig: Werk met drie branches tegelijk op hetzelfde project. Merge ze één voor één terug in master. Check na elke merge of alles nog werkt.
  4. Erg lastig: Maak een branch, commit drie wijzigingen, switch terug naar master, maak daar óók een wijziging in hetzelfde bestand, en merge de branch. Los het conflict zorgvuldig op en check met git log --graph hoe de geschiedenis eruitziet.
13 juni 2026, 13:30

Sessie 13: GitHub: Je code online, voor iedereen

Tot nu toe leeft al je code op jouw laptop. Handig voor jou, maar niemand anders kan erbij. En als je laptop crasht, ben je alles kwijt.

GitHub is een website waar je Git-repositories online zet. Het is een back-up, een portfolio, en een samenwerkingsplatform in één. Vandaag zet je jouw code op GitHub. Aan het einde van deze sessie kan iedereen met een internetverbinding jouw projecten bekijken, en jij die van hen.

Wat is GitHub?

GitHub is niet hetzelfde als Git. Git is het versiebeheersysteem dat op jouw computer draait. GitHub is een website (github.com) die Git-repositories host: een soort Google Drive voor code, maar dan met Git eronder.

Wat GitHub toevoegt aan Git:

  • Back-up in de cloud. Je code staat niet alleen lokaal. Laptop kapot? Je code overleeft.
  • Samenwerken. Meerdere mensen kunnen aan dezelfde repo werken. GitHub regelt wie wat mag.
  • Pull requests. Je vraagt toestemming om jouw wijzigingen in iemands project te stoppen, met discussie en review.
  • Issues. Bugs melden, features voorstellen, taken bijhouden: allemaal in je repo.
  • Portfolio. Je GitHub-profiel is je visitekaartje. Werkgevers kijken ernaar.

GitHub is niet de enige optie; er zijn alternatieven zoals GitLab en Bitbucket. Maar GitHub is de grootste, met meer dan 100 miljoen gebruikers. In deze sessies gebruiken we GitHub.

Stap 0: Een GitHub-account maken

  1. Open github.com in je browser.
  2. Klik op Sign up.
  3. Vul je e-mailadres in. Gebruik hetzelfde adres dat je in sessie 11 bij git config hebt ingesteld: dat scheelt gedoe.
  4. Kies een username. Dit wordt jouw identiteit op GitHub. Kies iets dat je over 5 jaar nog steeds leuk vindt. Je kunt je username later veranderen, maar oude links naar jouw profiel werken dan niet meer.
  5. Verifieer je account via de e-mail die GitHub stuurt.
  6. Kies het Free-abonnement. Alles wat je nodig hebt is gratis: onbeperkt publieke en private repositories.

Tip voor coaches: check of de ninjas toegang hebben tot hun e-mail tijdens de sessie. Sommige laptops staan niet ingelogd op webmail. Zorg voor een plan B: als e-mailverificatie niet lukt, kunnen ninjas GitHub later thuis verifiëren. De rest van de sessie werkt ook zonder verificatie.

Stap 1: Je eerste repository op GitHub

Je hebt een lokaal project op je computer (van sessie 11). Die ga je nu online zetten.

Op GitHub, klik rechtsboven op het + icoontje → New repository.

Vul in:

VeldWat je invult
Repository namemijn-eerste-git-project (zelfde als je lokale map)
Description“Mijn eerste Git-project (CoderDojo sessie 11)”
Public / PrivateKies Public: anderen mogen je code zien
Initialize with READMENIET aanvinken: je hebt al een lokaal project

Klik Create repository.

GitHub toont nu instructies. Kies het blok onder "…or push an existing repository from the command line". Kopieer die drie commando’s: die heb je zo nodig.

Stap 2: Je lokale repo koppelen aan GitHub

In je terminal (in je projectmap):

git remote add origin https://github.com/JOUW_USERNAME/mijn-eerste-git-project.git

Wat gebeurt hier? remote is een externe locatie waar jouw repo ook staat. origin is de naam die je die locatie geeft. “origin” is de standaardnaam voor je belangrijkste remote. Je kunt meerdere remotes hebben (bijvoorbeeld origin voor GitHub, backup voor GitLab), maar één is meestal genoeg.

Controleer of het gelukt is:

git remote -v

Je ziet:

origin  https://github.com/JOUW_USERNAME/mijn-eerste-git-project.git (fetch)
origin  https://github.com/JOUW_USERNAME/mijn-eerste-git-project.git (push)

fetch = hier haal je code vandaan. push = hier stuur je code naartoe. Meestal zijn die hetzelfde.

Stap 3: Je code online zetten met git push

Nu komt het moment: je lokale commits naar GitHub sturen.

git push -u origin master

push = stuur mijn commits naar de remote. -u origin master = “onthoud dat master op mijn computer hoort bij master op origin.” De volgende keer kun je gewoon git push typen, want Git onthoudt de koppeling.

GitHub vraagt om in te loggen. Dat kan op twee manieren:

  1. Via de browser: GitHub opent een browservenster, je klikt op “Authorize”. Klaar.
  2. Met een Personal Access Token: maak je aan via GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic). Geef de token een naam, vink repo aan, kopieer de token, en plak hem als wachtwoord in de terminal.

Voor ninjas: methode 1 (browser) is het makkelijkst. Als dat niet werkt, help dan met een token.

Na een succesvolle push, ververs je GitHub-pagina. Je bestanden staan er! Je hallo.py, je commit-geschiedenis: alles.

Stap 4: Wijzigingen pushen

Maak een wijziging lokaal:

echo 'print("GitHub is cool!")' >> hallo.py
git add hallo.py
git commit -m "Add GitHub enthusiasm"

Push naar GitHub:

git push

Je hoeft nu geen -u origin master meer, want Git weet de weg. Ververs GitHub: je nieuwe commit staat erbij.

Stap 5: Een nieuw project starten via GitHub

Dit is de omgekeerde route: je begint op GitHub en haalt het naar je computer.

Op GitHub: New repository. Noem hem python-spelletje. Vink dit keer wél Add a README file aan; GitHub maakt dan een repo met één bestand erin. Klik Create repository.

Nu haal je deze repo naar je computer. Dit heet clonen:

cd ~   # ga naar je home-map
git clone https://github.com/JOUW_USERNAME/python-spelletje.git

GitHub maakt een map python-spelletje aan met de README erin. En het is meteen een volwaardige Git-repository: de .git-map zit er al in. Je hoeft geen git init te doen.

cd python-spelletje
git log --oneline   # je ziet de eerste commit die GitHub voor je maakte

Nu kun je hier lokaal verder werken en je commits pushen naar GitHub, precies zoals in Stap 3 tot 4.

Stap 6: Wat als iemand anders ook wijzigingen maakt?

Stel: je hebt thuis aan python-spelletje gewerkt en gepusht. Nu ben je op de dojo-laptop en wil je verder. Je hebt de nieuwste versie nog niet.

git pull

git pull haalt de nieuwste commits van GitHub naar jouw computer. Het is twee acties in één: git fetch (ophalen) + git merge (samenvoegen met jouw lokale code).

Altijd git pull doen voor je begint met werken, anders loop je achter en krijg je merge conflicts.

Samenvatting van de workflow:

CommandoWat het doet
git pullHaal nieuwste versie op van GitHub
Werk aan je code, maak commits (git add, git commit)
git pushStuur jouw commits naar GitHub

Onthoud: pull voor je begint, push als je klaar bent.

Showcase

Laat aan een coach en een buddy zien:

  1. Je GitHub-profiel met minstens één repository.
  2. De commit-geschiedenis van je repo op GitHub (klik op “X commits” boven de bestandslijst).
  3. Een verse git push die net op GitHub is aangekomen.

Tot de volgende keer!

“Nu staat je code online. Maar wat zet je in een commit message? Hoe schrijf je een bericht dat over drie maanden nog steeds duidelijk is? En hoe werk je met een team zonder de boel te breken? Volgende keer: de professionele kant van Git: best practices die elke developer gebruikt.”

Neem mee naar huis

  1. Makkelijk: Push een bestaand lokaal project naar een nieuwe GitHub-repository.
  2. Middel: Maak 3 commits lokaal, push ze, en check op GitHub of ze allemaal zichtbaar zijn.
  3. Lastig: Clone een repo van GitHub, maak een wijziging, commit, en push terug.
  4. Erg lastig: Simuleer werken op twee computers: clone een repo, maak een commit en push. Ga naar een andere map, clone dezelfde repo opnieuw (alsof het een andere computer is), maak een andere commit, en probeer te pushen. Wat gebeurt er? Los het op met git pull.
13 juni 2026, 13:30

Sessie 14: Git Best Practices: Schrijf commits die je later nog snapt

Je kunt Git-commando’s typen. Je code staat op GitHub. Maar er is een verschil tussen “Git gebruiken” en “Git goed gebruiken.”

Deze sessie gaat over de gewoontes die ervaren developers elke dag toepassen. Geen nieuwe commando’s, wel scherpere regels. Aan het einde schrijf je commit messages waar je over een jaar nog iets aan hebt, en werk je met een branching-strategie die je project overzichtelijk houdt.

Wat je vandaag leert

  • Hoe je een commit message schrijft die iets zegt
  • Drie simpele regels voor branching
  • Wat git stash is en wanneer je het gebruikt
  • De pull-voor-je-pusht regel

Deel 1: Commit messages die iets zeggen

Een slechte commit message ziet er zo uit:

git commit -m "fix"
git commit -m "update"
git commit -m "dingen veranderd"

Over drie maanden kijk je terug. Wat was “fix”? Welke “dingen”? Je weet het niet meer. Je moet door de code spitten om te achterhalen wat er gebeurd is. Dat is tijdverspilling.

De goede commit message: 3 delen

Een professionele commit message heeft drie onderdelen:

1. Een korte samenvatting (max 72 karakters)

Schrijf in het Engels. Begin met een werkwoord in de tegenwoordige tijd. Dit heet “imperative mood”: alsof je een bevel geeft aan de codebase. Denk: “Add”, “Fix”, “Remove”, “Update”, “Refactor”.

Add highscore tracking
Fix star spawning off-screen
Remove unused collision code

2. Een lege regel

3. Een langere uitleg (optioneel, als het nodig is)

Add highscore tracking

Slaat de hoogste score op in highscore.txt. De score wordt
bijgewerkt na elke game-over. Het bestand wordt aangemaakt als
het nog niet bestaat.

Dit vervangt de oude methode waarbij de score alleen in het
geheugen stond en verloren ging bij afsluiten.

De uitgebreide uitleg vertelt waarom je iets veranderd hebt, niet alleen wát. De code zelf laat het “wat” al zien. Het “waarom” is wat je over drie maanden nodig hebt.

Commit message regels op een rij

Doe ditNiet dit
“Add player health bar”“added health bar thing”
“Fix collision detection on left wall”“fix”
“Remove debug print statements”“cleanup”
Max 72 karakters voor de titelEen heel verhaal in de titel
Tegenwoordige tijd, EngelsVerleden tijd (“added”, “fixed”)

Waarom Engels? Omdat bijna alle open source-projecten Engels gebruiken. Code is internationaal. Ook je Python-code schrijf je in het Engels (def calculate_score). Je commit messages volgen diezelfde taal.

Waarom tegenwoordige tijd? Een commit beschrijft wat deze wijziging dóet, niet wat jij gedaan hébt. “Add feature” (deze commit voegt een feature toe), niet “Added feature” (ik heb ooit een feature toegevoegd). Git zelf gebruikt ook tegenwoordige tijd: Merge branch 'feature', niet Merged.

Multi-line commits in de terminal

Je kunt een commit message over meerdere regels schrijven door de aanhalingstekens niet te sluiten:

git commit -m "Add highscore tracking

Slaat de hoogste score op in highscore.txt. Het bestand wordt
automatisch aangemaakt bij de eerste game-over."

Let op: je typt het sluitende aanhalingsteken pas op de laatste regel. Alles ertussen is onderdeel van je message.

Wanneer commit je?

Commit vaak. Commit klein.

Niet: drie uur werken, één enorme commit met 47 gewijzigde bestanden. Wel: elke 15 tot 30 minuten een commit, telkens als één logisch stukje af is.

Een vuistregel: als je commit message “en” bevat, is je commit te groot. “Add highscore tracking en fix menu bug en update sprites” → dit moeten drie aparte commits zijn.

Kleine commits maken het makkelijker om:

  • Te begrijpen wat er wanneer veranderd is
  • Eén specifieke wijziging terug te draaien zonder andere dingen kapot te maken
  • Merge conflicts op te lossen (kleine brokjes = minder conflicten)

Deel 2: Branching: drie simpele regels

Je weet al hóe je branches maakt (sessie 12). Nu: wannéér en waaróm.

Regel 1: master is altijd werkend

De master-branch is je etalage. Iemand die je repo cloned, moet je code kunnen runnen zonder foutmeldingen. master bevat nooit half-af werk.

Niet oké op master: code met # TODO: dit moet nog af, uitgecommentarieerde functies, experimenten die je “later nog fixet.”

Wel oké op master: werkende code. Punt.

Regel 2: Eén feature, één branch

Een branch doet precies één ding. Niet meer.

  • Goed: feature/player-health-bar
  • Slecht: feature/health-bar-and-menu-and-sound-effects

Als je tijdens het werken aan een feature denkt: “Oh, ik kan ook even die menu-bug fixen”: stop. Maak een nieuwe branch voor de bugfix. Merge die eerst. Ga dan verder met je feature.

Regel 3: Branches hebben een korte levensduur

Een branch die langer dan een paar dagen bestaat, wordt een probleem. master verandert intussen, en jouw branch raakt achterop. Hoe langer je wacht met mergen, hoe groter de kans op merge conflicts.

Merge je branch zodra de feature klaar is. Voor kleine dingen (een bugfix van 10 minuten): misschien heb je niet eens een aparte branch nodig.

Branch-naamgeving

Geef branches namen die je over een week nog begrijpt:

feature/highscore-tracking
bugfix/star-spawn-offscreen
experiment/particle-effects

De prefix (feature/, bugfix/, experiment/) vertelt meteen wat voor soort werk er in deze branch zit. Dit is een conventie, geen technische vereiste, maar het helpt.

Deel 3: Handige workflow-commando’s

git stash: werk opzij zetten

Stel: je bent bezig op een feature-branch. Je code is half af. Je wil even switchen naar master voor een snelle bugfix, maar je wil je half-affe werk niet committen. Daar is git stash voor:

git stash

Git bewaart al je wijzigingen tijdelijk en zet je working directory terug naar de laatste commit. Je kunt nu switchen, de bugfix doen, committen, en terugkomen:

git switch master
# ... bugfix doen, committen ...
git switch feature-branch
git stash pop

git stash pop haalt je opgeslagen wijzigingen terug. Alles staat weer zoals je het achterliet: half af, maar veilig bewaard.

git pull --rebase: een schonere geschiedenis

Normaal doet git pull een merge. Dat maakt een extra merge-commit aan. Soms wil je dat niet; je wil een rechte lijn van commits zonder vertakkingen.

git pull --rebase

Dit haalt de nieuwste commits op en zet jouw lokale commits erbovenop, alsof je ze ná de remote-commits gemaakt hebt. Het resultaat is een lineaire geschiedenis: geen merge-commits die alleen maar “Merge branch ‘master’” zeggen.

Let op: gebruik --rebase alleen op branches die jij alleen gebruikt. Bij gedeelde branches is gewoon git pull (merge) veiliger.

De pull-voor-je-pusht regel

Voordat je git push doet:

git pull

Altijd. Ook al denk je dat niemand anders gepusht heeft. Het kost één seconde en voorkomt dat GitHub je push weigert met “Updates were rejected because the remote contains work that you do not have locally.”

Deze regel geldt vooral als je samenwerkt. Als jij de enige bent die aan een repo werkt, is het risico kleiner, maar de gewoonte is goud waard.

Deel 4: De .gitignore die je altijd moet hebben

Elk project heeft bestanden die niet in Git horen. Je .gitignore is je eerste verdedigingslinie tegen per ongeluk wachtwoorden committen.

Minimale .gitignore voor een Python-project:

# Python
__pycache__/
*.pyc
*.pyo

# Virtual environment
venv/
.venv/

# Environment variables (bevat vaak wachtwoorden/keys)
.env

# Jouw editor
.vscode/
.idea/
*.swp
*.swo

# OS-bestanden
.DS_Store
Thumbs.db

Waarom __pycache__/ negeren? Dit zijn gecompileerde Python-bestanden die je programma automatisch aanmaakt. Ze zijn verschillend per Python-versie en per computer. Ze in Git stoppen geeft alleen maar ruis.

Waarom .env negeren? .env-bestanden bevatten vaak geheime sleutels, API-keys, database-wachtwoorden. Die mogen nooit, maar dan ook nooit in Git.

Showcase

Laat aan een coach en een buddy zien:

  1. Je laatste 5 commits, met goeie commit messages.
  2. Je .gitignore-bestand.
  3. Een branch-naam die de feature/prefix-conventie volgt.

Tot de volgende keer!

“Nu werk je professioneel met Git. Maar er is nog één concept dat élk team gebruikt: de pull request. Je code is klaar, maar voor hij in master komt, moet iemand anders hem goedkeuren. Hoe werkt dat? Volgende keer: pull requests, de review-muur waar élke wijziging doorheen moet.”

Neem mee naar huis

  1. Makkelijk: Herschrijf de commit messages van je laatste 5 commits. Gebruik de regels uit deze sessie.
  2. Middel: Maak een branch met de feature/-prefix, werk erin, merge hem terug, en verwijder hem. Doe hetzelfde voor een bugfix/-branch.
  3. Lastig: Gebruik git stash om half werk op te slaan, switch naar master, maak een snelle fix, push, en kom terug naar je feature met git stash pop.
  4. Erg lastig: Maak een situatie waar git pull faalt (clone je repo in twee mappen, maak in beide een andere commit, push de eerste, en probeer de tweede te pushen). Los het op: git pull, merge conflict fixen, en dan pas pushen.
13 juni 2026, 13:30

Sessie 15: Pull Requests: De voordeur van je project

Je hebt een feature gebouwd op een branch. Goeie commit messages, netjes gewerkt. Nu wil je je code in master zetten.

Je zou git merge kunnen doen en klaar. Maar in een team werkt dat niet. Iemand anders moet je code eerst bekijken: checken op fouten, op stijl, op dingen die je gemist hebt. Dát is een pull request.

Een pull request (PR) is een verzoek: “Haal mijn wijzigingen binnen.” Je opent hem op GitHub. Je teamgenoten bekijken je code, geven feedback. Jij past dingen aan. Pas als iedereen tevreden is, gaat de code naar master.

In deze sessie maak je je eerste pull request. Je reviewt die van een buddy. En je ervaart hoe écht teamwerk met Git voelt.

Wat je vandaag leert

  • Wat een pull request is en waarom teams het gebruiken
  • Een PR openen op GitHub
  • Je PR updaten na feedback
  • Een PR van iemand anders reviewen
  • Een PR mergen en de branch opruimen

Stap 0: Voorbereiding: werken in duo’s

Vandaag werk je samen met een buddy. Spreek af wie de auteur is (die de feature bouwt) en wie de reviewer is (die de PR bekijkt). Halverwege wissel je om.

De auteur heeft een GitHub-repo nodig waar de reviewer toegang toe heeft. De makkelijkste manier:

  1. De auteur maakt een repo aan op GitHub (public).
  2. De auteur gaat naar SettingsCollaboratorsAdd people.
  3. De auteur nodigt de reviewer uit met diens GitHub-username.
  4. De reviewer accepteert de uitnodiging (via e-mail of GitHub-notificaties).

Nu kunnen jullie allebei pushen naar dezelfde repo.

Alternatief (zonder collaborator-toegang): de reviewer forkt de repo van de auteur en opent een PR vanaf de fork naar het origineel. Maar vandaag houden we het simpel: directe collaborator-toegang.

Stap 1: Een feature-branch maken en pushen (auteur)

De auteur cloned de repo (als dat nog niet gebeurd is) en maakt een feature-branch:

git clone https://github.com/AUTEUR_USERNAME/ons-samenwerk-project.git
cd ons-samenwerk-project
git switch -c feature/groet-functie

Maak een nieuw bestand:

echo 'def groet(naam):' > groet.py
echo '    return f"Hallo {naam}!"' >> groet.py

Commit en push:

git add groet.py
git commit -m "Add groet functie

Retourneert een begroeting voor de opgegeven naam."
git push -u origin feature/groet-functie

Stap 2: Een pull request openen (auteur)

  1. Ga naar je repo op GitHub.
  2. GitHub toont nu een gele balk: “feature/groet-functie had recent pushes”: klik op Compare & pull request. (Als je de balk niet ziet: klik op het Pull requests-tabblad, dan New pull request, kies base: mastercompare: feature/groet-functie.)
  3. Schrijf een duidelijke titel: “Add groet functie”
  4. Schrijf een beschrijving. Een goede PR-beschrijving heeft drie delen:
## Wat

Een functie `groet(naam)` die een begroeting retourneert.

## Waarom

We hebben een herbruikbare begroeting nodig voor de
gebruikersinterface. Dit voorkomt dat we op meerdere plekken
dezelfde string moeten schrijven.

## Hoe testen

```python
from groet import groet
print(groet("CoderDojo"))  # "Hallo CoderDojo!"
5. Klik **Create pull request**.

Je PR staat nu open. Iedereen met toegang tot de repo kan hem zien. GitHub toont de diff: exact welke regels je hebt toegevoegd (groen) en verwijderd (rood).

## Stap 3: Een PR reviewen (reviewer)

De reviewer opent de PR op GitHub. Dit zijn de stappen van een review:

### 1. Lees de beschrijving

Begrijp je wat deze PR doet? Zo niet, vraag om verduidelijking.

### 2. Bekijk de diff

Klik op het **Files changed**-tabblad. GitHub toont elke gewijzigde regel. Kijk naar:

- **Werkt de code?** Zou je het zelf runnen als je het cloned?
- **Is de stijl consistent?** Dezelfde naamgevingsconventies als de rest van het project?
- **Zijn er fouten?** Typo's, ontbrekende imports, logische fouten?
- **Staan er geen geheimen in?** Wachtwoorden, API-keys, tokens?
- **Is het te begrijpen?** Kan iemand die deze code voor het eerst ziet, volgen wat er gebeurt?

### 3. Geef feedback

Klik op een regel om een comment toe te voegen. Wees specifiek:

**Goeie feedback:**
> In `groet.py` regel 2: wat gebeurt er als `naam` leeg is? Misschien een default-waarde toevoegen?

**Slechte feedback:**
> Dit is niet goed.

### 4. Dien je review in

Klik op **Review changes**. Je hebt drie opties:

- **Comment**: gewoon feedback, geen beslissing.
- **Approve**: de code is goed, mag gemerged worden.
- **Request changes**: er moet iets aangepast worden voor de PR gemerged kan worden.

Bij je eerste review: kies **Approve** als alles er goed uitziet, of **Request changes** als je iets wilt laten aanpassen.

## Stap 4: Feedback verwerken (auteur)

De reviewer heeft een comment achtergelaten. De auteur past de code aan:

```bash
# Zorg dat je op je feature-branch zit
git switch feature/groet-functie

Pas groet.py aan op basis van de feedback. Bijvoorbeeld:

def groet(naam="wereld"):
    return f"Hallo {naam}!"

Commit en push:

git add groet.py
git commit -m "Add default parameter to groet functie"
git push

De nieuwe commit verschijnt automatisch in de PR. De reviewer ziet de update en kan opnieuw kijken.

Stap 5: Mergen en opruimen

Als de reviewer Approved heeft, is het tijd om te mergen.

Op GitHub, in de PR, klik je op Merge pull request. Kies Create a merge commit (de standaardoptie). Klik Confirm merge.

De feature-branch is nu samengevoegd met master. Op GitHub kun je de branch verwijderen met de Delete branch-knop die verschijnt na het mergen.

Lokaal moet je ook opruimen:

git switch master
git pull                      # haal de merge op die net op GitHub gebeurd is
git branch -d feature/groet-functie   # verwijder de lokale branch

Je master is nu up-to-date. Je feature-branch is weg, lokaal én op GitHub. Klaar voor het volgende stuk werk.

De volledige PR-workflow op een rij

1. git switch -c feature/iets        ← Maak een feature-branch
2. Werk, commit, werk, commit
3. git push -u origin feature/iets   ← Push naar GitHub
4. Open een PR op GitHub             ← Vraag om review
5. Reviewer bekijkt, geeft feedback
6. Pas code aan, commit, push        ← Feedback verwerken
7. Reviewer approved
8. Merge de PR op GitHub             ← Code naar master
9. git switch master && git pull       ← Haal master lokaal bij
10. git branch -d feature/iets       ← Ruim de branch op

Een PR van iemand anders reviewen: hoe doe je dat goed?

Een review is niet: “Ziet er goed uit.” Een goeie review is specifiek, constructief en respectvol.

Vragen die je jezelf stelt tijdens een review:

  • Snap ik wat deze code doet? (Zo nee: vraag om betere comments of een duidelijkere beschrijving.)
  • Doet de code wat de PR-beschrijving belooft?
  • Kan dit simpeler?
  • Mist er iets? (Foutafhandeling, edge cases, documentatie)
  • Staan er geheimen, wachtwoorden of persoonlijke data in?
  • Volgt de code de stijl van de rest van het project?

Hoe geef je feedback:

Begin met wat goed is. Wees dan specifiek over wat beter kan. Eindig met een duidelijke conclusie.

Voorbeeld:

De functie werkt precies zoals beschreven. Twee suggesties:

  1. In regel 2: een default-waarde voor naam zou handig zijn.
  2. Overweeg een docstring toe te voegen die uitlegt wat de functie retourneert.

Verder ziet het er goed uit. Als je deze twee dingen aanpast, approve ik hem graag.

Showcase

Wissel van rol met je buddy en doe de hele workflow nog een keer: nu ben jij de reviewer.

Laat aan een coach zien:

  1. Een open pull request op GitHub met een beschrijving.
  2. Minstens één review-comment op een PR van je buddy.
  3. Een succesvol gemergede PR: de feature-code staat nu op master.

Tot de volgende keer!

Dit was de laatste Git-sessie. Wat je nu kunt:

  • Je code versiebeheren met Git (init, add, commit, log, diff)
  • Experimenteren op branches zonder master kapot te maken (branch, switch, merge)
  • Je code online delen via GitHub (push, pull, clone)
  • Professionele commit messages schrijven
  • Samenwerken via pull requests: openen én reviewen

Dit is niet “extra”. Dit is hoe developers élke dag werken. Of je nu games maakt, websites bouwt, of AI traint: Git en GitHub zijn de gereedschappen die je project beheersbaar houden. Gebruik ze.

Neem mee naar huis

  1. Makkelijk: Open een pull request voor een wijziging in een van je eigen projecten.
  2. Middel: Vraag een vriend of familielid om een GitHub-account te maken. Nodig ze uit als collaborator en laat ze een PR reviewen.
  3. Lastig: Draag bij aan een echt open source-project. Zoek op GitHub naar issues met het label good first issue. Fork de repo, maak een fix, en open een PR.
  4. Erg lastig: Bouw een klein project met twee anderen. Gebruik branches, pull requests en reviews. Werk alsof je in een professioneel team zit.
13 juni 2026, 13:30

Sessie 16: Markdown: Schrijf notities die eruitzien als het web

Je kent HTML misschien van het internet: <h1>, <p>, <a>. Maar voor notities en documentatie is HTML veel te veel typwerk. Daar is Markdown voor.

Markdown is een mini-taal die je in gewone tekst schrijft. Het ziet er leesbaar uit zónder dat je het eerst moet omzetten. En met één druk op de knop wordt het nette HTML: koppen, lijsten, links, tabellen.

In deze sessie leer je de hele basis van Markdown. Aan het einde schrijf je notities die er professioneel uitzien, op GitHub, in Obsidian, of in elk ander programma dat Markdown begrijpt.

Wat je vandaag leert

  • Koppen en alinea’s maken
  • Tekst opmaken: vet, cursief, doorstreept
  • Lijsten en opsommingen
  • Links naar andere pagina’s en websites
  • Afbeeldingen invoegen
  • Tabellen maken
  • Horizontale lijnen en codeblokken

Waarom Markdown?

Markdown is overal:

  • GitHub: elke repo heeft een README.md in Markdown. Issues, pull requests, wiki’s: allemaal Markdown.
  • Obsidian, Joplin, Logseq: notitie-apps die je documenten als .md-bestanden opslaan.
  • Discord, Slack, Reddit: ondersteunen Markdown voor snelle opmaak in berichten.
  • Hugo (deze website!): alle sessie-pagina’s die je hier ziet, zijn geschreven in Markdown.
  • Jupyter Notebooks: de tekst-cellen zijn Markdown.

Het grote voordeel: je schrijft gewone tekst. Geen ingewikkelde tags. En het resultaat is altijd leesbaar, ook als platte tekst.

Stap 0: Een Markdown-bestand maken

Markdown-bestanden eindigen op .md. Maak er één:

mkdir mijn-notities
cd mijn-notities
echo "# Mijn Eerste Notitie" > test.md

Je kunt .md-bestanden openen in elke teksteditor: Kladblok, VS Code, Thonny, Obsidian. VS Code heeft een ingebouwde preview: klik rechtsboven op het icoontje met het vergrootglas en het boek.

Stap 1: Koppen en alinea’s

Koppen maak je met #. Hoe meer #, hoe kleiner de kop:

# Dit is een titel (h1)

## Dit is een subtitel (h2)

### Dit is een kleinere kop (h3)

#### Nog kleiner (h4)

Een alinea maak je door gewoon tekst te typen. Een lege regel ertussen start een nieuwe alinea:

Dit is de eerste alinea. Die loopt gewoon door.

Dit is de tweede alinea. Omdat er een lege regel tussen zit.

Tip: één # gebruik je maar één keer per document: voor de titel. De rest begint bij ##.

Stap 2: Tekst opmaken

**Dit is vetgedrukt**

*Dit is cursief*

~~Dit is doorstreept~~

Je kunt ook **vet en *cursief* combineren** in dezelfde zin.

Het resultaat:

  • **vet** wordt vet
  • *cursief* wordt cursief
  • ~~doorstreept~~ wordt doorstreept

Let op: geen spaties tussen de sterretjes en de tekst. ** vet ** werkt niet. **vet** wel.

Sommige editors accepteren ook underscores: __vet__ en _cursief_. Maar sterretjes zijn de standaard: gebruik die.

Stap 3: Horizontale lijnen

Een horizontale lijn maak je met drie (of meer) streepjes:

---

Of met sterretjes:

***

Het resultaat is een lijn over de hele breedte. Gebruik het spaarzaam: één of twee per document is genoeg. Te veel lijnen maken je notities onoverzichtelijk.

Stap 4: Lijsten

Ongenummerde lijst

Gebruik -, * of +:

- Python
- Git
- Markdown
- GitHub

Je kunt lijsten nesten door in te springen (2 of 4 spaties):

- Programmeertalen
  - Python
  - JavaScript
- Versiebeheer
  - Git
  - GitHub

Genummerde lijst

1. Open Thonny
2. Schrijf je code
3. Klik op Run

Het maakt niet uit welke nummers je typt: Markdown nummert automatisch door. Dit:

1. Stap één
1. Stap twee
1. Stap drie

…geeft exact hetzelfde resultaat als 1, 2, 3. Handig als je later een stap wilt toevoegen zonder alles te hernummeren.

Checklist (GitHub-stijl)

- [x] Git installeren
- [x] Eerste commit maken
- [ ] Branches leren
- [ ] Pull request openen

[x] is afgevinkt, [ ] is open. Werkt in GitHub issues, pull requests, en veel notitie-apps.

[klik hier voor de CoderDojo-site](https://python.coderdojohasselt.be)

Het woord tussen [ ] is de klikbare tekst. De URL tussen ( ) is de bestemming.

[Bekijk sessie 11](11-git-intro/)

Dit linkt naar het bestand 11-git-intro/_index.md in dezelfde map.

<https://github.com>

Dit toont de URL als klikbare link, zonder aparte tekst.

Stap 6: Afbeeldingen

Een afbeelding is bijna hetzelfde als een link, maar met een ! ervoor:

![Een ster in Pygame Zero](/sessions/01-catch-the-stars/coordinaten.svg)

De tekst tussen [ ] is de alt-tekst: die verschijnt als de afbeelding niet laadt, of wordt voorgelezen door screenreaders.

Afbeeldingen en Git: Kleine afbeeldingen (SVG, kleine PNG’s) kun je in Git zetten. Grote afbeeldingen (>1 MB) en video’s beter niet, want die maken je repo traag.

Stap 7: Tabellen

| Commando | Wat het doet |
|----------|-------------|
| `git init` | Nieuwe repository maken |
| `git add` | Bestanden klaarzetten voor commit |
| `git commit` | Wijzigingen vastleggen |
| `git push` | Commits naar GitHub sturen |

De | tekens vormen de kolommen. De --- regel scheidt de kop van de data. De : in de scheidingsregel bepaalt uitlijning:

| Links uitgelijnd | Gecentreerd | Rechts uitgelijnd |
|:-----------------|:-----------:|------------------:|
| links            | midden      | rechts            |

Stap 8: Code en codeblokken

Inline code

Gebruik backticks voor korte stukjes code in een zin:

Gebruik `git status` om te zien wat er gewijzigd is.

Codeblokken

Voor meerdere regels code gebruik je drie backticks:

```python
def groet(naam):
    return f"Hallo {naam}!"
```

De taal (python) achter de eerste backticks geeft syntax highlighting. Dit werkt voor tientallen talen: bash, html, css, javascript, json, yaml, markdown.

Codeblok zonder taal

```
Dit is gewoon tekst
zonder syntax highlighting
```

Stap 9: Blockquotes

Gebruik > voor citaten of belangrijke opmerkingen:

> Git is geen back-up. Git is een tijdmachine.
> Iemand op het internet

Meerdere > regels achter elkaar vormen één blok. Een lege > regel start een nieuwe alinea binnen het citaat.

Stap 10: Alles samen: een echte README

Een goed README.md combineert alles wat je nu kent. Hier is een voorbeeld:

# Mijn Project

Een korte beschrijving van wat dit project doet.

## Installatie

```bash
git clone https://github.com/jouw-naam/mijn-project.git
cd mijn-project
```

## Gebruik

1. Open `main.py` in Thonny.
2. Klik op **Run**.
3. Kies een level en start.

## Wat je leert

- [x] Pygame Zero basis
- [x] Collision detection
- [ ] Highscore systeem
- [ ] Geluidseffecten

## Links

- [CoderDojo Hasselt](https://coderdojohasselt.be)
- [Broncode op GitHub](https://github.com/jouw-naam/mijn-project)

Cheatsheet

WatMarkdown
Kop niveau 1# Titel
Kop niveau 2## Subtitel
Kop niveau 3### Kleinere kop
Vet**vet**
Cursief*cursief*
Doorstreept~~doorstreept~~
Horizontale lijn--- of ***
Lijst (ongeordend)- item
Lijst (genummerd)1. item
Checklist- [ ] open item
Link[tekst](url)
Afbeelding![alt tekst](url)
Inline code`code`
Codeblok```taal
Citaat> citaat
Tabel| kolom | kolom |

Showcase

Laat aan een coach en een buddy zien:

  1. Een Markdown-document met minstens één kop, een lijst, een link en vetgedrukte tekst.
  2. Een tabel met minstens twee kolommen en drie rijen.
  3. Een checklist met afgevinkte en open items.

Tot de volgende keer!

Markdown is simpel, maar het is overal. Je README’s op GitHub, je notities in Obsidian, je documentatie: alles wordt leesbaarder met Markdown. Gebruik het. Overdrijf niet met opmaak. Goeie notities zijn helder, niet druk.

Neem mee naar huis

  1. Makkelijk: Schrijf een README.md voor je favoriete Python-project. Gebruik koppen, een lijst en een codeblok.
  2. Middel: Maak een notitie in Obsidian (of een andere Markdown-editor) over wat je deze maand geleerd hebt. Gebruik alle opmaak uit de cheatsheet minstens één keer.
  3. Lastig: Bouw een “cheatsheet”-pagina in Markdown voor een onderwerp naar keuze (Git-commando’s, Python syntax, wiskunde-formules). Gebruik tabellen en geneste lijsten.
  4. Erg lastig: Maak een Markdown-document met een geneste lijst die minstens drie niveaus diep gaat, een tabel met uitlijning, en een codeblok met syntax highlighting.
13 juni 2026, 13:30

Extra uitdagingen

Je hebt de tien sessies afgerond. Hier vind je klassieke games om zelf na te bouwen. Geen stap-voor-stap handleiding, alleen wat houvast en een startbestand. Schrijf de code zelf. Hoe ver kom je?

Uitdagingen

  • Snake: Sessie 5+. Groei zo lang mogelijk. Eet fruit, vermijd de muren en jezelf.
  • Space Invaders: Sessie 7+. Schiet de alien-formatie neer voor ze landen.
13 juni 2026, 13:30

Subsecties van Extra uitdagingen

Snake

Notitie

Vereist: sessie 5 afgerond.

De slang beweegt vanzelf. Jij stuurt met de pijltjestoetsen. Eet een stuk fruit: de slang groeit één vakje. Raak de muur of jezelf: game over.

Opstarten

Download de startcode en pak de ZIP uit. Open main.py in Thonny en klik Run. Je ziet een leeg raster. De besturing en de tijdmeting staan al klaar. Jij vult de functies nieuw_fruit(), zet_stap() en teken() in.

Hoe werkt het?

Snake speelt op een rooster. De slang is een lijst van (kolom, rij) coördinaten. Elke stap voeg je een nieuw hoofd toe aan de voorkant van de lijst en verwijder je het laatste element. Eet de slang fruit, dan laat je het laatste element staan, waardoor de slang groeit.

TILE = 20          # grootte van één vakje in pixels
COLS = WIDTH // TILE
ROWS = HEIGHT // TILE

slang = [(10, 10), (9, 10), (8, 10)]  # lijst van (kolom, rij)
dx, dy = 1, 0                          # richting: 1 stap naar rechts

Afbeeldingen laden en schalen

De fruitplaatjes zijn 64×64 pixels. Dat is te groot voor één vakje van 20×20. Je laadt ze met pygame.image.load() en schaalt ze daarna met pygame.transform.scale(). Dit staat al klaar in de startcode.

surf = pygame.image.load('images/apple.png').convert_alpha()
fruit_images['apple'] = pygame.transform.scale(surf, (TILE, TILE))

convert_alpha() zorgt dat de transparantie van het plaatje goed werkt. pygame.transform.scale() geeft een nieuwe Surface terug op het gevraagde formaat. Het origineel blijft ongewijzigd.

Om een geschaald plaatje op het scherm te zetten, gebruik je screen.blit():

screen.blit(fruit_surf, (fruit_pos[0] * TILE, fruit_pos[1] * TILE))

✅ Basic

  • Vul nieuw_fruit() in: kies een willekeurig fruit, zoek een vrij vakje en sla de positie en Surface op.
  • Vul zet_stap() in: bereken het nieuwe hoofd, voeg het toe aan de voorkant van de lijst, verwijder het laatste element.
  • Vul teken() in: teken het raster (pygame.draw.line), het fruit (screen.blit), en elke slangbol (pygame.draw.circle). Toon de score met font.render() en screen.blit().
  • Spawn fruit als de slang het eet: laat slang.pop() weg en roep nieuw_fruit() aan.

⭐ Stretch

  • Game over als het hoofd buiten het raster valt.
  • Game over als het hoofd een eigen segment raakt (nieuw_hoofd in slang[1:]).
  • Toon een game-over scherm met de score. Druk op R om opnieuw te starten.

🔥 Expert

  • Verhoog de snelheid naarmate de score stijgt: verlaag STAP_MS op basis van de score (max(60, BASISINTERVAL - score * 10)).
  • Sla de hoogste score op en toon hem naast de huidige score, ook na een herstart.
  • Zorg dat fruit nooit spawnt op een vakje dat de slang al bezet.

Downloads

  • Startcode: main.py met de besturing en tijdmeting klaar; jij vult de functies in.
  • Fruitafbeeldingen: als je zonder startcode begint (al inbegrepen in de startcode-ZIP).

Fruit: Kenney Food Kit, CC0 (publiek domein). kenney.nl

20 juni 2026, 10:54

Space Invaders

Notitie

Vereist: sessie 7 afgerond.

Aliens staan in een 4×9 formatie. Ze bewegen heen en weer, zakken omlaag bij elke rand en schieten willekeurig terug. Jij hebt één schip en drie levens. Schiet ze allemaal neer voordat ze landen.

Opstarten

Download de startcode en pak de ZIP uit. Open main.py in Thonny en klik Run. Je ziet het schip en de alien-formatie. De besturing en de afbeeldingen staan al klaar. Jij vult de beweging, het schieten en de botsingen in.

Hoe werkt het?

Alle aliens zijn pygame.Rect-objecten in één lijst. Elk frame beweeg je ze allemaal met hetzelfde aantal pixels. Raakt de formatie een rand, dan keer je de richting om en laat je elk alien ALIEN_VAL pixels zakken.

for alien in aliens:
    alien.x += snelheid * alien_richting

rechts = max(a.right for a in aliens)
links  = min(a.left  for a in aliens)
if rechts >= BREEDTE or links <= 0:
    alien_richting *= -1
    for alien in aliens:
        alien.y += ALIEN_VAL

Kogels zijn ook pygame.Rect-objecten. Speler-kogels gaan omhoog (k.y -= KOGEL_SNL), alien-kogels gaan omlaag (k.y += ALIEN_SNL). Botsingen check je met k.colliderect(alien).

✅ Basic

  • Teken de achtergrond (gebruik random.seed(42) voor vaste sterren), het schip en alle aliens.
  • Beweeg het schip links/rechts met de pijltjestoetsen.
  • Laat de formatie heen en weer bewegen en omlaag zakken bij een rand.
  • Schiet een kogel omhoog als de speler op spatie drukt. Gebruik COOLDOWN_MS om razendsnel schieten te voorkomen.

⭐ Stretch

  • Speler-kogel raakt alien: verwijder beide, score += 10.
  • Elk alien schiet willekeurig terug: if random.random() < SCHIET_KANS.
  • Alien-kogel raakt speler: levens -= 1, bij 0 levens → game over.
  • Toon score en levens linksboven.

🔥 Expert

  • De formatie versnelt naarmate er minder aliens zijn: 1 + 3 * (1 - restant). Van 1 pixel/frame bij het begin tot 4 pixels/frame bij de laatste alien.
  • Game over als een alien de onderkant bereikt (alien.bottom >= HOOGTE - 50).
  • Game over (gewonnen) als de lijst leeg is.
  • Herstart met R: stel alle variabelen opnieuw in via een reset()-functie.

Downloads

  • Startcode: main.py met venster, afbeeldingen en alien-rooster klaar; jij vult de logica in.
  • Afbeeldingen: ship.png, enemy.png en laser.png als je zonder startcode begint.

Afbeeldingen: Kenney Space Shooter Redux, CC0 (publiek domein). kenney.nl

20 juni 2026, 10:54

Pac-Man

Notitie

Vereist: sessie 9 afgerond.

Een doolhof van blauwe muren. Witte stippen overal. Vier gekleurde geesten die op je afkomen. Eet alle stippen om te winnen. Raak een geest en het is game over.

Opstarten

Download de startcode en pak de ZIP uit. Open main.py in Thonny en klik Run. Het doolhof verschijnt en Pac-Man staat klaar. Jij voegt de beweging, de botsingen en de geesten toe.

Hoe werkt het?

Pac-Man beweegt in pixels, niet in vakjes. Elk frame verplaatst hij snelheid × dt pixels in zijn richting. dt is de tijd (in seconden) die het vorige frame duurde. Zo beweegt het spel even snel op trage én snelle computers.

nieuw_x = self.rect.x + self.dx * PACMAN_SNL * dt

Muurbotsing check je door een test-rect te maken op de nieuwe positie en te kijken of die overlapt met een muurblok:

test = self.rect.copy()
test.x = nieuw_x
test.y = nieuw_y
botsing = any(test.colliderect(m.rect) for m in muren)

✅ Basic

  • Laat Pac-Man in zijn huidige richting bewegen (pijltjestoetsen staan al klaar).
  • Voeg muurbotsing toe: stop als de nieuwe positie een muur raakt.
  • Laat de richtingswissel werken: schakel pas over naar de nieuwe richting als die vrij is.

⭐ Stretch

  • Eet witte stippen: als Pac-Man een stip raakt, verwijder die en tel 10 punten.
  • Eet gele power stippen: verwijder, tel 50 punten en zet self.powered_up = True.
  • Win als alle stippen op zijn: zet self.staat = GEWONNEN.

🔥 Expert

  • Laat geesten bewegen: probeer richtingen en kies een vrije (geen muurbotsing).
  • Laat geesten op Pac-Man jagen: kies de richting die de afstand tot Pac-Man verkleint.
  • Als een geest Pac-Man raakt en powered_up is False: game over.
  • Als een geest Pac-Man raakt en powered_up is True: eet de geest (+200 punten).

Downloads

  • Startcode: main.py + geluidsbestanden; jij vult de beweging en botsingen in.
20 juni 2026, 10:54

Frogger

Notitie

Vereist: sessie 5 (Breakout) afgerond.

Een kikker wil de overkant bereiken. Daarvoor moet hij een drukke weg oversteken én een gevaarlijke rivier. Auto’s rijden in drie banen, houtblokken drijven op het water. Eén verkeerde stap en het is gedaan.

Opstarten

Download de startcode en pak de ZIP uit. Open main.py in Thonny en klik Run. Je ziet het speelveld maar de kikker beweegt nog niet. Jij vult de functies in.

Hoe werkt het?

Het scherm is opgedeeld in horizontale banen van 40 pixels:

  • Boven: 5 vijvers waar de kikker veilig moet geraken
  • Rivier (3 banen): houtblokken drijven voorbij. Op een blok ben je veilig, in het water verdrink je.
  • Gras berm: veilige rustplek in het midden
  • Weg (3 banen): auto’s rijden heen en weer. Geraakt worden = dood.
  • Onder: startpositie van de kikker
TEGEL = 40
KOLOMMEN, RIJEN = 13, 14

# Banen: (rij, richting, snelheid)
AUTO_BANEN = [
    (6,  1, 2),   # rechts, traag
    (7, -1, 4),   # links, snel
    (8,  1, 3),   # rechts, gemiddeld
]

Auto’s en houtblokken zijn pygame.Rect-objecten in een lijst. Elk frame verschuif je ze allemaal en verwijder je ze als ze buiten het scherm vallen.

De kikker beweegt in vaste stappen van TEGEL pixels. Druk op een pijltjestoets en de kikker springt één vakje in die richting.

Botsingen check je met rect.colliderect(andere_rect).

✅ Basic

  • Kikker verplaatsen: vang de pijltjestoetsen in KEYDOWN en pas kikker.x en kikker.y aan met TEGEL.
  • Auto’s spawnen: vul spawn_auto(). Kies een willekeurige baan, maak een Rect net buiten het scherm, voeg toe aan auto_lijst.
  • Auto’s bewegen: in update(): verschuif elke auto met rect.x += richting * snelheid. Verwijder auto’s buiten het scherm. Houd minstens 6 auto’s.
  • Houtblokken spawnen: vul spawn_hout(). Hetzelfde principe, maar met HOUT_BANEN en een grotere breedte.
  • Houtblokken bewegen: in update(): verschuif en verwijder zoals bij auto’s. Houd minstens 4 blokken.
  • Auto-botsing: als kikker.colliderect(auto): roep sterf() aan.
  • Verdrinking: als de kikker in de rivier is (rij 2, 3 of 4) en niet op een houtblok: sterf().
  • Vijver check: als de kikker dicht bij een onbezette vijver is: zet huizen_gevuld[i] = True, score += 10, reset de kikker. 5 vijvers vol = gewonnen.

Klaar? Je hebt een werkende Frogger!

⭐ Stretch

  • Levenden teller: het levens-systeem staat al klaar. Zorg dat het spel stopt als levens <= 0 en toon “GAME OVER”.
  • Timer: voeg een timer toe. Start met 30 seconden per level. Als de timer afloopt verlies je een leven. Toon de timer in de HUD. Bij elke gevulde vijver krijg je 5 seconden extra.
  • Moeilijkheidsgraad: verhoog de snelheid van auto’s en houtblokken naarmate de score stijgt (snelheid = basis + score // 50).

🔥 Expert

  • Bonusvlieg: een groen insect verschijnt willekeurig op de kaart en beweegt rond. Vang het voor 20 bonuspunten. Laat het na 5 seconden verdwijnen.
  • High score: sla de hoogste score op in een variabele high_score. Toon hem naast de huidige score. Werk bij als de speler een nieuw record zet.
  • Verschillende voertuigen: voeg vrachtwagens toe (2 vakjes breed, trager) en motoren (1 vakje, sneller). Gebruik een aparte lijst of een type-veld.
  • Animatie: laat de kikker kort knipperen als hij sterft (dood_timer staat al klaar). Teken een kleine plons als de kikker verdrinkt.

Downloads

  • Startcode: main.py met het speelveld, de kikker en alle teken-code klaar; jij vult de logica in.

Geïnspireerd op het klassieke Frogger (1981, Konami). Tekeningen met pygame.draw.

20 juni 2026, 10:54

Dojo Defender

Dojo Defender is onze huisgame: een top-down survival shooter die we samen bouwen over meerdere sessies.

Voor wie?

Dojo Defender is voor ninja’s die regelmatig langskomen. De game groeit over 7 sessies mee met de gewone lessen. Kom je maar af en toe? Geen probleem: doe de mini-game van die sessie. Die is altijd compleet op zich.

Reken niet op een complete Dojo Defender als je veel sessies mist. Maar het sjabloon en alle snapshots staan op deze site, dus thuis verder bouwen kan altijd.

Hoe doe ik mee?

  • Vanaf les 2 krijg je aan het einde van elke les een Dojo Defender snapshot: een ZIP-bestand dat je kan downloaden van de lespagina.
  • Pak die uit, open in Thonny, en volgende les begin je daarmee verder.
  • Mis je een les? Download de snapshot van die les, of spring een les over en pak het verderop op.

Mag ik er mijn eigen ding van maken?

Ja! Verander de naam, de grafische stijl, het verhaal. Dojo Defender is een sjabloon, geen verplichting.

1 juli 2026, 22:43

Subsecties van Dojo Defender

Dojo Defender: Sessie 1

Je ruimteschip is geland in de donkere ruimte. Tijd om het tot leven te brengen. In deze sessie voeg je een bestuurbaar schip toe en laat je asteroïden naar beneden vallen. Ontwijk ze of verlies een leven. Hoe lang overleef jij?

Wat je vandaag leert

  • Lijsten gebruiken om meerdere asteroïden bij te houden
  • Objecten spawnen en verwijderen
  • Botsingen detecteren met colliderect
  • Levens bijhouden en een explosie-effect tonen
  • Game-over afdwingen als je levens op zijn

Stap 0: Installeren

  1. Open Thonny.
  2. Download de Dojo Defender starter, pak de ZIP uit en open main.py.
  3. Klik op Run. Je ziet een donkerblauw scherm met je ruimteschip onderaan. Je kan het al bewegen met de pijltjestoetsen.
  4. Problemen? Kijk bij Thonny instellen.

Stap 1: Laat asteroïden spawnen

Een game zonder gevaar is saai. Voeg een lijst toe waarin je alle asteroïden bijhoudt, en een functie die nieuwe asteroïden aanmaakt:

# bovenaan, na WIDTH en 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)

Je hebt ook import random nodig bovenaan het bestand. Voeg die regel toe.

Roep de functie aan in update() met een teller zodat niet élke frame een asteroïde verschijnt:

# bovenaan, buiten de functies:
timer = 0

def update():
    global timer
    timer += 1
    if timer % 60 == 0:   # elke seconde (60 frames)
        spawn_asteroid()

Zet dit vóór de bewegingscode van het schip.

Stap 2: ✅ Basic: asteroïden laten vallen en tekenen

Asteroïden moeten naar beneden vallen en verdwijnen als ze het scherm uit zijn:

def update():
    global timer
    timer += 1
    if timer % 60 == 0:
        spawn_asteroid()

    for rock in asteroids[:]:      # [:] = kopie zodat je veilig kunt verwijderen
        rock.y += rock.speed
        if rock.y > HEIGHT + 30:
            asteroids.remove(rock)

Vergeet niet om de asteroïden te tekenen in draw():

def draw():
    screen.fill((10, 10, 30))
    for rock in asteroids:
        rock.draw()
    ship.draw()

Stap 3: ⭐ Stretch: botsing + levens

Als een asteroïde je schip raakt, verlies je een leven. Voeg lives en game_over toe bovenaan:

lives = 3
game_over = False

Voeg in de for-loop collision-detectie toe:

    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 = True

Toon de levens en 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 het spel als game_over True is:

def update():
    if game_over:
        return
    ...

Toon een game-over scherm 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,
        )

Stap 4: 🔥 Expert: explosies + onzichtbaarheid

Als een asteroïde je raakt, wil je een explosie zien in plaats van dat het schip zomaar verdwijnt. Gebruik de vier explosie-frames (explosion_1 t/m explosion_4):

explosion = Actor('explosion_1')
explosion_active = False
explosion_frame = 0
explosion_timer = 0

Bij een botsing: verberg het schip, start de explosie:

        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 = True

Laat de explosie afspelen 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 = 5

Voeg daarna een onzichtbaarheidsperiode toe zodat je niet meteen opnieuw geraakt wordt:

invincible = False
invincible_timer = 0

In update():

    if invincible:
        invincible_timer -= 1
        if invincible_timer <= 0:
            invincible = False
            ship.visible = True

Pas de collision-check aan:

        elif rock.colliderect(ship) and not invincible and ship.visible:

Teken de explosie in draw():

    if explosion_active:
        explosion.draw()

Showcase

Laat je Dojo Defender zien aan een coach en een buddy. Hoe lang overleef jij? Heb je explosies? Ziet het er vet uit?

Tot de volgende keer!

“Volgende keer: je ruimteschip SCHIET TERUG. Vijanden in formatie, en jij blast ze weg met lasers. Dit wordt episch.”

Neem mee naar huis

Probeer thuis één van deze uitbreidingen:

  1. Geluid — Voeg sounds/explosion.wav toe als een asteroïde ontploft (sounds.explosion.play()).
  2. Moeilijker — Laat asteroïden sneller vallen naarmate de score stijgt: rock.speed = random.uniform(3 + score // 10, 5 + score // 10).
  3. Power-up — Voeg een zeldzame groene power-up toe die een extra leven geeft.
  4. Kleiner — Gebruik asteroid_med of asteroid_small voor variatie in grootte en snelheid.

Download Dojo Defender

1 juli 2026, 22:43

Dojo Defender: Sessie 2

Je schip heeft nu een wapen. Vijanden komen eraan. Schiet ze neer voordat ze de bodem bereiken. Elke 10 vijanden betekent een nieuwe wave — en het wordt steeds moeilijker.

Wat je vandaag leert

  • Schieten met de spatiebalk en een cooldown
  • Lijsten voor kogels en vijanden
  • Collision tussen kogels en vijanden
  • Golven (waves) die steeds moeilijker worden
  • Vijanden die sinus-bewegingen maken

Stap 0: Open de starter

  1. Open Thonny.
  2. Download de Dojo Defender starter voor vandaag, pak de ZIP uit en open main.py.
  3. Was je er vorige keer niet bij? Download de oplossing van sessie 1 en begin daarmee.
  4. Klik op Run. Je ziet hetzelfde spel als vorige keer: schip, asteroïden, explosies.
  5. Problemen? Kijk bij Thonny instellen.

Stap 1: Schieten met SPATIE

Je schip heeft een laserkanon. Voeg een lijst voor kogels toe bovenaan:

bullets = []

Gebruik on_key_down — die wordt maar één keer aangeroepen als je een toets indrukt (in tegenstelling tot update die blijft herhalen):

def on_key_down(key):
    if key == keys.SPACE:
        bullet = Actor('bullet')
        bullet.x = ship.x
        bullet.y = ship.y - 20
        bullets.append(bullet)

Laat de kogels omhoog bewegen in update() en verwijder ze als ze uit beeld zijn:

for bullet in bullets[:]:
    bullet.y -= 8
    if bullet.y < -10:
        bullets.remove(bullet)

Vergeet niet de kogels te tekenen in draw():

for bullet in bullets:
    bullet.draw()

Stap 2: ✅ Basic: schiet-cooldown

Zonder cooldown vuur je tientallen kogels per seconde. Voeg een timer toe:

shoot_cooldown = 0

In update():

if shoot_cooldown > 0:
    shoot_cooldown -= 1

Pas on_key_down aan zodat je alleen kunt schieten als de cooldown 0 is:

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 = 15

Stap 3: ⭐ Stretch: vijanden toevoegen

Vijanden spawnen bovenaan en vallen naar beneden. Voeg een lijst toe:

enemies = []

Een spawn-functie:

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)

Roep spawn_enemy() aan in update() met een spawn-interval:

# bovenaan:
spawn_counter = 0
spawn_interval = 120

# in update():
spawn_counter += 1
if spawn_counter >= spawn_interval:
    spawn_counter = 0
    spawn_enemy()

Teken vijanden in draw():

for enemy in enemies:
    enemy.draw()

Stap 4: 🔥 Expert: collision + waves

Kogel raakt vijand: verwijder beide, +10 punten:

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
            break

Vijand raakt bodem of schip: verlies een leven (zelfde als asteroïde).

Waves: elke 10 vijanden een nieuwe wave. Vijanden spawnen sneller:

# bovenaan:
wave = 1
enemies_destroyed = 0

# bij destroy:
enemies_destroyed += 1
if enemies_destroyed % 10 == 0:
    wave += 1
    spawn_interval = max(60, spawn_interval - 10)

Toon de huidige wave in draw():

screen.draw.text(f"Wave: {wave}", (10, 70), color="yellow", fontsize=24)

Toon “WAVE N!” in het groot bij een nieuwe wave (gebruik een timer).

Sinus-beweging: laat vijanden links-rechts zwenken:

import math  # bovenaan

enemy.x = enemy.start_x + math.sin(timer * 0.03 + enemy.strafe_offset) * 50

Showcase

Laat je Dojo Defender zien aan een coach. Hoeveel waves haal jij? Heb je de sinus-beweging werkend? Vergelijk scores met een buddy.

Tot de volgende keer!

“Volgende keer: POWER-UPS. Schilden, speed-boosts en spread-shots. Je schip wordt steeds sterker naarmate je langer overleeft.”

Neem mee naar huis

Probeer thuis één van deze uitbreidingen:

  1. Vijandvariatie — Gebruik enemy_charger.png voor een snellere vijand die geen sinus maakt, maar recht op je af komt.
  2. Geluid — Voeg sounds/laser.wav en sounds/explosion.wav toe.
  3. Harde modus — Vijanden spawnen steeds sneller, tot maximaal elke 30 frames.
  4. Score multiplier — Hoe hoger de wave, hoe meer punten per kill: score += 10 * wave.

Download Dojo Defender

1 juli 2026, 22:43

Dojo Defender: Sessie 3

Vandaag verhuist Dojo Defender van pgzero naar echte pygame. Geen pgzrun.go() meer — jij schrijft de game loop zelf. Het spel blijft hetzelfde, maar de motor eronder is anders. En aan het einde voeg je GELUID toe.

Wat je vandaag leert

  • Het verschil tussen pgzero en raw pygame
  • De game loop zelf programmeren: while running: clock.tick(60)
  • Actors omzetten naar Surface + Rect
  • Geluid laden en afspelen met pygame.mixer
  • Achtergrondmuziek laten loepen

Stap 0: Download de starter

De starter voor vandaag is een volledige port van Dojo Defender Sessie 2 naar raw pygame. Geen pgzero meer, maar het speelt hetzelfde.

  1. Download de Dojo Defender starter voor vandaag, pak uit en open main.py.
  2. Was je er vorige keer niet bij? Download de oplossing van sessie 2 en probeer die te porten.
  3. Klik op Run. Het spel werkt zoals je gewend bent: schip, asteroïden, kogels, vijanden, waves, explosies.

Wat is er veranderd?

In pgzero schreef je draw() en update() en pgzrun deed de rest. In raw pygame doe je alles zelf:

pgzero (vroeger):

import pgzrun

def draw():
    screen.fill((10, 10, 30))
    ship.draw()

def update():
    if keyboard.left:
        ship.x -= 5

pgzrun.go()

raw pygame (nu):

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()

De belangrijkste verschillen:

pgzeroraw pygame
Actor('plaatje')GameObject('plaatje.png', x, y)
keyboard.leftkeys[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)

Stap 1: Verken de code

Open main.py en vergelijk met de oude pgzero-code van sessie 2. Zoek deze onderdelen:

  1. Game loop — Zoek de while running:-lus. Zie je clock.tick(60), pygame.display.flip()?
  2. GameObject — Zoek de class GameObject. Hoe wordt een afbeelding geladen? Wat is rect?
  3. Input — Zoek pygame.key.get_pressed() en pygame.KEYDOWN. Hoe werkt de spatiebalk?

Verander de WIDTH naar 1024. Wat gebeurt er? (Alles wordt breder — de game werkt nog steeds.)

Stap 2: ✅ Basic — Voeg restart toe met de R-toets

In de pgzero-versie kon je niet herstarten na game over. Voeg dat nu toe:

# in de event loop, bij KEYDOWN:
if event.key == pygame.K_r and game_over:
    # reset alles
    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 = True

Toon ook een hint op het game-over scherm: druk op R om opnieuw te spelen.

Stap 3: ⭐ Stretch — Voeg geluid toe

De starter heeft nog geen geluid. Voeg sounds toe!

Maak een sounds/ map naast images/ en kopieer de geluidsbestanden uit dojo-defender-assets/sounds/ (vraag de coach). Of download de oplossing die ze al bevat.

Voeg bovenaan toe:

pygame.mixer.init()  # na 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 = blijven herhalen

Let op: het pad moet kloppen. Gebruik os.path.join(_DIR, 'sounds/shoot.wav') zoals de starter ook met afbeeldingen doet.

Speel de geluiden af op de juiste momenten:

  • shoot_sound.play() — als je schiet
  • explode_sound.play() — als een vijand of asteroïde ontploft
  • hit_sound.play() — als je schip geraakt wordt

Stap 4: 🔥 Expert — Mute-knop en volume

Voeg een M-toets toe die het geluid aan/uit zet:

muted = False

# in KEYDOWN:
if event.key == pygame.K_m:
    muted = not muted
    pygame.mixer.music.set_volume(0 if muted else 1)

Elk geluid moet dan gecheckt worden:

if not muted:
    shoot_sound.play()

Volume per geluid: Laat het schietgeluid zachter dan de explosie:

shoot_sound.set_volume(0.3)
explode_sound.set_volume(0.7)

Showcase

Laat aan een coach zien dat:

  • Je spel werkt in echte pygame zonder pgzero
  • Je geluid hebt toegevoegd (shoot, explode, hit, bg music)
  • Bonus: restart met R of mute met M

Tot de volgende keer!

“Volgende keer: POWER-UPS. Schilden, speed-boosts en spread-shots. En je schip wordt sterker naarmate je langer overleeft.”

Neem mee naar huis

Probeer thuis één van deze uitbreidingen:

  1. Health bar — Vervang de tekst “Lives: 3” door een visuele balk: teken drie rechthoekjes bovenaan.
  2. Pauze — Voeg een P-toets toe die het spel pauzeert. Zet running = False is niet genoeg — gebruik een aparte paused variabele.
  3. Particle-effect — Laat kleine witte stipjes rondspatten bij een explosie (gebruik een lijst met deeltjes die steeds kleiner worden).
  4. Eigen skin — Vervang player_ship.png door een eigen ontwerp.

Download Dojo Defender

1 juli 2026, 22:43

Dojo Defender: Sessie 4

Vandaag wordt Dojo Defender een echt spel met een menu. Geen zwarte start meer — je krijgt een titelscherm, een game-over scherm, en drie verschillende vijandtypes die elk hun eigen beweging hebben.

Wat je vandaag leert

  • State machines: je spel heeft toestanden (MENU, PLAYING, GAME OVER)
  • Vijand-AI: elke vijand beweegt anders (recht, zigzag, charger)
  • Wave-systeem: steeds moeilijkere golven met mixen van vijanden
  • Een reset-functie: alles netjes terugzetten zonder herstart

Stap 0: Download de starter

De starter is de oplossing van sessie 3 — Dojo Defender in raw pygame met geluid.

  1. Download de starter voor vandaag, pak uit en open main.py.
  2. Was je er niet bij? Download de oplossing van sessie 3.
  3. Klik op Run. Het spel werkt zoals voorheen: schip, asteroïden, kogels, drones, geluid.

Stap 1: Voeg een state machine toe

Een state machine houdt bij in welke “toestand” het spel zich bevindt:

state = "MENU"  # kan "MENU", "PLAYING", "GAME_OVER" zijn

Plaats deze variabele bovenaan, bij de andere globale variabelen.

Menu-scherm tekenen: Vervang de screen.fill en al het tekenen door een check op state:

if state == "MENU":
    # Teken titel en instructies
    screen.fill((10, 10, 30))
    titel = title_font.render("DOJO DEFENDER", True, (0, 200, 255))
    screen.blit(titel, (WIDTH // 2 - titel.get_width() // 2, HEIGHT // 2 - 60))
    sub = big_font.render("Press SPACE to start", True, (200, 200, 200))
    screen.blit(sub, (WIDTH // 2 - sub.get_width() // 2, HEIGHT // 2))

elif state == "PLAYING":
    # = al jouw bestaande game code hier

elif state == "GAME_OVER":
    # Teken "GAME OVER" + score

Maak title_font en big_font bovenaan:

title_font = pygame.font.SysFont(None, 80)
big_font = pygame.font.SysFont(None, 60)

Nu moet SPACE het volgende doen:

  • Als state == "MENU": reset het spel en ga naar PLAYING
  • Als state == "GAME_OVER": reset het spel en ga naar PLAYING
  • Als state == "PLAYING" en game_over == False: schiet zoals normaal

Pas de KEYDOWN-logic aan:

if event.key == pygame.K_SPACE:
    if state == "MENU":
        reset_game()
        state = "PLAYING"
    elif state == "GAME_OVER":
        reset_game()
        state = "PLAYING"
    elif state == "PLAYING" and shoot_cooldown == 0 and not game_over:
        # schiet (bestaande code)

Stap 2: ✅ Basic — Schrijf de reset-functie

Maak een functie die alles terugzet naar de beginwaarden:

def reset_game():
    global ship, asteroids, bullets, enemies
    global lives, score, game_over, timer
    global explosion_active, explosion_frame, explosion_timer
    global invincible, invincible_timer, shoot_cooldown
    global wave, wave_display_timer, enemies_destroyed
    global spawn_counter, spawn_interval

    ship.rect.center = (WIDTH / 2, HEIGHT - 50)
    ship.visible = True
    asteroids.clear()
    bullets.clear()
    enemies.clear()
    lives = 3
    score = 0
    game_over = False
    timer = 0
    explosion_active = False
    explosion_frame = 0
    explosion_timer = 0
    invincible = False
    invincible_timer = 0
    shoot_cooldown = 0
    wave = 1
    wave_display_timer = 0
    enemies_destroyed = 0
    spawn_counter = 0
    spawn_interval = 120

Zet state = "GAME_OVER" als de speler 0 levens heeft (bij lives <= 0):

if lives <= 0:
    game_over = True
    state = "GAME_OVER"

Check: Het spel start nu met een blauw titelscherm. SPACE start het spel. Bij game over zie je GAME OVER en kun je met SPACE herstarten.

Stap 3: ⭐ Stretch — Drie vijandtypes

Vervang de ene spawn_enemy() door drie functies:

def spawn_enemy_drone():
    enemy = GameObject('enemy_drone.png', random.randint(30, WIDTH - 30), -30)
    enemy.type = 'drone'
    enemy.speed = random.uniform(1, 3)
    enemies.append(enemy)


def spawn_enemy_zigzag():
    enemy = GameObject('enemy_zigzag.png', random.randint(30, WIDTH - 30), -30)
    enemy.type = 'zigzag'
    enemy.speed = random.uniform(2, 4)
    enemy.dx = random.choice([-2, 2])
    enemies.append(enemy)


def spawn_enemy_charger():
    enemy = GameObject('enemy_charger.png', random.randint(30, WIDTH - 30), -30)
    enemy.type = 'charger'
    enemy.speed = random.uniform(3, 5)
    enemies.append(enemy)

Beweging per type

In de game loop, waar je enemy.rect.y += enemy.speed deed, moet je nu per type andere beweging toevoegen:

for enemy in enemies[:]:
    if enemy.type == 'drone':
        enemy.rect.y += enemy.speed

    elif enemy.type == 'zigzag':
        enemy.rect.x += enemy.dx
        enemy.rect.y += enemy.speed
        if enemy.rect.left <= 0 or enemy.rect.right >= WIDTH:
            enemy.dx = -enemy.dx

    elif enemy.type == 'charger':
        # Stuur naar de x-positie van de speler
        dx = ship.rect.centerx - enemy.rect.centerx
        if dx > 0:
            enemy.rect.centerx += min(enemy.speed, abs(dx))
        elif dx < 0:
            enemy.rect.centerx -= min(enemy.speed, abs(dx))
        enemy.rect.y += enemy.speed * 0.8

    # Daarna: off-screen check, collision met schip etc.

Belangrijk: De charger moet langzamer dalen (* 0.8) zodat de speler tijd heeft om te reageren.

Stap 4: 🔥 Expert — Enhanced waves

Maak een spawn_enemy() die beslist welk type er spawnt op basis van de huidige wave:

def spawn_enemy():
    if wave == 1:
        spawn_enemy_drone()
    elif wave <= 3:
        if random.random() < 0.5:
            spawn_enemy_drone()
        else:
            spawn_enemy_zigzag()
    else:
        r = random.random()
        if r < 0.3:
            spawn_enemy_drone()
        elif r < 0.65:
            spawn_enemy_zigzag()
        else:
            spawn_enemy_charger()

Zo wordt het steeds moeilijker:

  • Wave 1: alleen drones (warmlopen)
  • Wave 2-3: drones + zigzag (50/50)
  • Wave 4+: alle drie, met meer chargers (30% drone, 35% zigzag, 35% charger)

Showcase

Laat aan een coach zien dat:

  • Je een menu-scherm hebt met titel en “Press SPACE to start”
  • Je drie vijandtypes hebt: drone (↓), zigzag (↘↗), charger (→ jouw schip)
  • De waves steeds moeilijker worden (meer zigzags, dan chargers)
  • Game over werkt met scoreweergave en restart

Tot de volgende keer!

“Volgende keer: POWER-UPS. Schilden, speed-boosts, spread-shots. En een échte eindbaas.”

Neem mee naar huis

Probeer thuis één van deze uitbreidingen:

  1. High score — Bewaar de hoogste score in een bestand en toon hem op het menu.
  2. Moeilijkheidsgraad — Laat de speler kiezen tussen “Makkelijk” en “Moeilijk” op het menu.
  3. Boss wave — Elke 5 waves verschijnt een grote baas met veel hits.
  4. Charger Warning — Teken een rode lijn onder een charger voordat hij verschijnt.

Download Dojo Defender

1 juli 2026, 22:43

Dojo Defender: Sessie 5

Vandaag geef je Dojo Defender een diepte-effect met een parallax sterrenachtergrond en explosie-deeltjes. Asteroïden worden ook vernietigbaar: grote asteroïden splitsen in kleinere.

Wat je vandaag leert

  • Parallax rekenen: meerdere lagen sterren met eigen snelheid
  • Deeltjeslijsten: een lijst van dictionaries voor particles
  • Positie-gebaseerde effecten: particles spawnen op de juiste plek
  • Recursief splitsen: grote asteroïde → 2 medium → 2 klein → weg

Stap 0: Download de starter

De starter is de oplossing van sessie 4 — Dojo Defender met menu, drie vijandtypes en waves.

  1. Download de starter voor vandaag, pak uit en open main.py.
  2. Was je er niet bij? Download de oplossing van sessie 4.
  3. Klik op Run. Het spel werkt zoals voorheen: schip, asteroïden, kogels, drones, zigzags, chargers.

Stap 1: 🌟 Parallax sterrenachtergrond

Een parallax achtergrond heeft meerdere lagen die met verschillende snelheden bewegen. Ver weg = langzaam, dichtbij = snel. Dat geeft diepte.

Voeg een lijst stars = [] toe bovenaan het bestand, bij de andere globale variabelen:

stars = []

Maak een init_stars() functie die drie lagen sterren aanmaakt:

def init_stars():
    global stars
    stars = []
    layers = [
        (40, 0.3, 2, 120),   # ver weg: 40 sterren, speed 0.3, grootte 2, donkerder
        (40, 0.7, 3, 180),   # middel:  40 sterren, speed 0.7, grootte 3
        (30, 1.5, 4, 255),   # dichtbij: 30 sterren, speed 1.5, grootte 4, fel
    ]
    for count, speed, size, brightness in layers:
        for _ in range(count):
            stars.append({
                'x': random.randint(0, WIDTH),
                'y': random.randint(0, HEIGHT),
                'speed': speed,
                'size': size,
                'brightness': brightness,
            })

Wiskunde erachter: elke ster krijgt een y-snelheid. Verre sterren (0.3 px/frame) bewegen bijna niet, nabije sterren (1.5 px/frame) bewegen sneller. Je brein interpreteert dat als diepte, precies zoals in een auto: bomen naast de weg vliegen voorbij, bergen ver weg blijven stil.

Voeg ook de update-functie toe. Die laat sterren zakken en zet ze terug naar boven als ze onder het scherm zijn:

def update_stars():
    for star in stars:
        star['y'] += star['speed']
        if star['y'] > HEIGHT + star['size']:
            star['y'] = -star['size']
            star['x'] = random.randint(0, WIDTH)

En een teken-functie. Kleine sterren teken je met pygame.draw.rect (2×2), grotere met pygame.draw.circle:

def draw_stars(surface):
    for star in stars:
        c = (star['brightness'], star['brightness'], star['brightness'])
        if star['size'] <= 2:
            pygame.draw.rect(surface, c, (star['x'], star['y'], star['size'], star['size']))
        else:
            pygame.draw.circle(surface, c, (int(star['x']), int(star['y'])), star['size'] // 2)

Aanroepen: Roep init_stars() één keer aan (bij het opstarten, ná alle functies), update_stars() elke frame (zelfs in het menu!), en draw_stars(screen) na screen.fill() in ALLE states.

Check

Zie je drie lagen sterren naar beneden scrollen, ook in het menu? De verre sterren zijn kleiner en bewegen langzamer.

Stap 2: ✅ Basic — Particle systeem

Een particle is een klein deeltje dat beweegt, vervaagt en verdwijnt. We gebruiken een lijst van dictionaries:

particles = []  # {x, y, vx, vy, life, max_life, color, size}

Maak een functie om particles te spawnen:

def spawn_particles(x, y, count, colors, speed_range, size_range, life_range):
    for _ in range(count):
        angle = random.uniform(0, 2 * math.pi)
        speed = random.uniform(speed_range[0], speed_range[1])
        particles.append({
            'x': x,
            'y': y,
            'vx': math.cos(angle) * speed,
            'vy': math.sin(angle) * speed,
            'life': random.randint(life_range[0], life_range[1]),
            'max_life': life_range[1],
            'color': random.choice(colors),
            'size': random.uniform(size_range[0], size_range[1]),
        })

Wat gebeurt er? Elke particle krijgt:

  • Een positie (x, y)
  • Een snelheid in x en y (vx, vy), berekend uit een hoek en snelheid
  • Een levensduur (life wordt elke frame met 1 verlaagd)
  • Een kleur en grootte

Update-functie (elke frame elke particle 1 stap verplaatsen en de levensduur verkorten):

def update_particles():
    for p in particles[:]:
        p['x'] += p['vx']
        p['y'] += p['vy']
        p['life'] -= 1
        if p['life'] <= 0:
            particles.remove(p)

Waarom [:]? We passen de lijst aan tijdens het loopen (we verwijderen particles). Zonder [:] krijg je een runtime error. De slice [:] maakt een kopie van de lijst om veilig over te loopen.

Teken-functie met vervaging (alpha-fade): hoe minder levensduur, hoe kleiner en donkerder:

def draw_particles(surface):
    for p in particles:
        ratio = p['life'] / p['max_life']
        color = tuple(int(c * ratio) for c in p['color'])
        pygame.draw.circle(surface, color, (int(p['x']), int(p['y'])), int(p['size'] * ratio))

De ratio is een getal tussen 1 (net geboren) en 0 (bijna dood). We vermenigvuldigen de kleur en grootte ermee, zodat de particle langzaam verdwijnt.

Particle-lijst wissen in reset

Voeg particles.clear() toe aan de reset_game() functie, anders blijven dode particles na een restart zweven.

Roep update_particles() aan in de PLAYING state en draw_particles(screen) na draw_stars(screen) in alle states.

Check

Start het spel en druk op SPACE. Er gebeurt nog niets zichtbaars — maar het systeem staat klaar voor de volgende stappen.

Stap 3: ⭐ Stretch — Positie-gebaseerde effecten

Nu gaan we particles spawnen op de juiste momenten en posities.

Uitlaatgassen

Als het schip beweegt, spawn kleine rode/oranje particles onder het schip. Voeg dit toe in de if not game_over sectie, waar je de pijltjestoetsen checked:

keys = pygame.key.get_pressed()
moving = False
if keys[pygame.K_LEFT] and ship.rect.centerx > 30:
    ship.rect.centerx -= 5
    moving = True
if keys[pygame.K_RIGHT] and ship.rect.centerx < WIDTH - 30:
    ship.rect.centerx += 5
    moving = True

if moving and ship.visible:
    spawn_particles(
        ship.rect.centerx, ship.rect.bottom,
        1,  # of random.randint(1, 2)
        [(255, 100, 0), (255, 60, 0), (200, 50, 0)],
        (0.5, 1.5), (2, 4), (10, 20),
    )

De particles spawnen onder het schip (ship.rect.bottom) en krijgen een kleine snelheid omhoog (negatieve vy door de hoek).

Explosie puin

Als een vijand wordt geraakt door een kogel, spawn 8-12 deeltjes in alle richtingen. Voeg dit toe waar je een enemy uit de lijst haalt na een bullet-collision:

spawn_particles(
    enemy.rect.centerx, enemy.rect.centery,
    random.randint(8, 12),
    [(255, 140, 0), (255, 0, 0), (255, 255, 0), (255, 255, 255)],
    (1, 4), (2, 5), (20, 40),
)

Vijand-sterfsparkels

Als een vijand de onderkant van het scherm bereikt, spawn 3-5 witte vonkjes:

spawn_particles(
    enemy.rect.centerx, HEIGHT - 30,
    random.randint(3, 5),
    [(255, 255, 255), (200, 200, 255)],
    (1, 3), (1, 3), (15, 30),
)

Check

Beweeg het schip — zie je oranje uitlaatgassen? Schiet een vijand — zie je 8-12 rondvliegende deeltjes? Laat een vijand ontsnappen — zie je witte vonkjes?

Stap 4: 🔥 Expert — Vernietigbare asteroïden

Asteroïden zijn nu recursief splitsbaar:

  • asteroid_big.png → wordt geraakt → 2× asteroid_med.png
  • asteroid_med.png → wordt geraakt → 2× asteroid_small.png
  • asteroid_small.png → wordt geraakt → weg + particles

Pas spawn_asteroid() aan zodat elke asteroïde een asteroid_size krijgt:

def spawn_asteroid(size='big'):
    rock = GameObject(f'asteroid_{size}.png', random.randint(30, WIDTH - 30), -30)
    rock.speed = random.uniform(3, 5)
    rock.asteroid_size = size
    asteroids.append(rock)
    return rock

Maak een split_asteroid() functie die een asteroïde in twee kleinere splitst:

def split_asteroid(rock):
    # Eerst: exploderende particles
    spawn_particles(
        rock.rect.centerx, rock.rect.centery,
        random.randint(8, 12),
        [(255, 140, 0), (255, 0, 0), (255, 255, 0), (255, 255, 255)],
        (1, 4), (2, 5), (20, 40),
    )
    if rock.asteroid_size == 'big':
        for _ in range(2):
            r = spawn_asteroid('med')
            r.rect.center = rock.rect.center
            r.rect.x += random.randint(-20, 20)
            r.speed = random.uniform(2, 4)
    elif rock.asteroid_size == 'med':
        for _ in range(2):
            r = spawn_asteroid('small')
            r.rect.center = rock.rect.center
            r.rect.x += random.randint(-15, 15)
            r.speed = random.uniform(1, 3)
    asteroids.remove(rock)

Wat gebeurt hier?

  1. We spawnen explosie-particles op de positie van de asteroïde
  2. We checken de grootte: ‘big’ → 2 ‘med’, ‘med’ → 2 ‘small’, ‘small’ → geen verdere splitsing
  3. Nieuwe asteroïden spawnen vlakbij de oude positie met een lichte offset
  4. De kleinere asteroïden zijn langzamer dan de grotere

Pas de collision-logic aan: waar je astersoids.remove(rock) deed bij een kogelraak, roep nu split_asteroid(rock) aan:

for rock in asteroids[:]:
    if bullet.rect.colliderect(rock.rect):
        bullets.remove(bullet)
        explode_sound.play()
        split_asteroid(rock)
        score += 5
        break

Belangrijk: Het geluid (explode_sound.play()) speel je af vóór split_asteroid() zodat het niet per ongeluk twee keer afspeelt.

Check

Schiet een grote asteroïde. Zie je hem splitsen in twee medium asteroïden? Schiet die — ze splitsen in twee kleine. Schiet de kleine — die verdwijnen met een mooie particle-explosie.

Showcase

Laat aan een coach zien dat:

  • Parallax sterrenachtergrond met drie snelheden
  • Particle systeem met vervaging (alpha-fade)
  • Uitlaatgassen onder het bewegende schip
  • Explosie-puin bij vernietigde vijanden en asteroïden
  • Asteroïden splitsen: big → med → small → weg

Tot de volgende keer!

“Volgende keer: POWER-UPS. Schilden, speed-boosts, spread-shots. En een échte eindbaas.”

Neem mee naar huis

Probeer thuis één van deze uitbreidingen:

  1. Makkelijk: verander de kleur van de sterren per laag (bijv. verre sterren blauwachtig, nabije geel).
  2. Middel: voeg een vierde laag sterren toe, nog sneller (speed 3.0), met grote heldere sterren.
  3. Lastig: laat particle-kleur veranderen tijdens de levensduur (van geel naar rood naar grijs).
  4. Erg lastig: voeg een “comet” particle type toe dat een klein staartje heeft.

Download Dojo Defender

1 juli 2026, 22:43

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.

  1. Download de starter voor vandaag, pak uit en open main.py.
  2. Was je er niet bij? Download de oplossing van sessie 5.
  3. 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 = 1

Voeg 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 = 20

Tijdens 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 = 0

Pas 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:

  1. Makkelijk: verander de boss-kleur of -grootte via de image.
  2. Middel: voeg een vierde fase toe (HP < 10%) waarin de boss teleporteert.
  3. Lastig: laat de boss delay-bullets schieten die een spoor achterlaten.
  4. Erg lastig: voeg een tweede boss-type toe die in een andere wave verschijnt.

Download Dojo Defender

1 juli 2026, 22:43

Dojo Defender: Sessie 7

Vandaag bepaal jij wat er in Dojo Defender gebeurt. Geen stap-voor-stap instructies — kies een feature, plan hem, en bouw hem.

Je start van de complete Dojo Defender met alles erop en eraan: schip, asteroïden, 3 vijandtypes, waves, particles, parallax sterren, en een eindbaas met 3 fases.

Dit is jouw game. Maak er iets van.

Kies wat jij wil bouwen

Hieronder staan ideeën. Kies er één — of bedenk je eigen feature.

Makkelijk

  • Power-ups: vijanden laten soms (20%) een power-up vallen. Rood = spread shot (3 kogels), groen = schild, geel = speed boost. Power-ups werken 5 seconden.
  • Moeilijkheidsgraad: maak het spel makkelijker (meer levens, langzamere vijanden) of moeilijker (minder levens, snellere spawns).
  • Geluid: voeg een power-up geluid toe (powerup.wav), een game-over geluid, of achtergrondmuziek die versnelt bij hogere waves.

Gemiddeld

  • High score: bewaar de hoogste score in een bestand (highscore.txt). Toon hem op het menu en game-over scherm. Alleen overschrijven als de nieuwe score hoger is.
  • Moeilijkheidsschalen: maak het spel elke wave iets moeilijker — snellere vijanden, kleinere asteroïden, vaker spawnen.
  • Gifte vijanden: een nieuwe vijandsoort die een spoor van giftige deeltjes achterlaat.
  • Ship-animatie: het schip kan maar 3 kogels tegelijk schieten. Laad een “heat” indicator of maak een cooldown.

Moeilijk

  • Tweede boss: maak een nieuwe boss die op een andere wave verschijnt (bijv. wave 7). Ander uiterlijk, ander gedrag.
  • Power-up systeem uitbreiden: voeg een 4e power-up toe (bijv. “time freeze” — vijanden bevriezen voor 3 seconden).
  • Combo-systeem: hoe sneller je vijanden doodt, hoe meer punten. Een “combo multiplier” die oploopt.
  • Baas heeft eigen power-up drops: de baas laat een zeldzame power-up vallen als hij verslagen wordt.

Eigen idee

Heb je een eigen idee? Top. Bespreek het kort met een coach en begin met bouwen.

Tips voor het plannen van je feature

Voor je begint met code schrijven, beantwoord deze vragen:

  1. Wat moet de speler zien? (een nieuwe power-up, een tekst, een icoon?)
  2. Wanneer gebeurt het? (als je SPACE indrukt, als een vijand sterft, als wave 10 begint?)
  3. Hoe lang duurt het? (5 seconden? Permanent? Tot de volgende wave?)
  4. Wat moet er veranderen aan de code? (nieuwe variabelen? een nieuwe lijst? een timer?)
  5. Hoe weet ik dat het werkt? (wat is de “check” — wat moet ik zien om te weten dat het af is?)

Schrijf je antwoorden op papier of in commentaar bovenaan het bestand.

Cheatsheets van vorige sessies

Kun je iets niet meer herinneren? Gebruik de cheatsheets van vorige keren:

Coach-geleide planning

Dit deel vul je samen met een coach in voordat je begint met coderen.

Mijn feature: _________________________________________________

Ik kies:   Makkelijk  /  Gemiddeld  /  Moeilijk  /  Eigen idee

Nieuwe variabelen die ik nodig heb:
- __________________________________
- __________________________________
- __________________________________

Nieuwe afbeeldingen of geluiden:
- __________________________________
- __________________________________

Check: hoe weet ik dat het werkt?
- __________________________________

Showcase

Aan het einde van de sessie laat je aan een coach en je buddy zien wat je hebt gebouwd. Vertel in 30 seconden:

  • Wat je wilde maken
  • Of het gelukt is
  • Wat het moeilijkst was

Stappenplan (voor als je er zelf niet uitkomt)

  1. Open main.py in Thonny en klik Run — werkt het nog?
  2. Maak een kopie van de game-map voor het geval je vastloopt.
  3. Begin met de kleinste mogelijke verandering (bijv. een variabele toevoegen).
  4. Test. Werkt het? → Volgende stap. Werkt het niet? → Vraag een coach.

Tot de volgende keer!

“Volgende keer: een compleet nieuw spel. Wat wordt het?”

1 juli 2026, 22:43

Voortgang per sessie

DD SessieNa curriculumWat is er nieuw?Snapshot
1S2Ruimteschip + asteroïden + leven + explosies(volgt)
2S3Schieten + vijanden + score + waves(volgt)
3S4 (Pygame)Herbouwd in Pygame met sprites + geluid(volgt)
4S5Menu + 3 vijandtypes(volgt)
5S6Parallax sterren + particles + splitsende asteroïden(volgt)
6S7Boss fight met 3 fases + HP-balk(volgt)
7S8Jouw eigen versie (power-ups, high score, …)(volgt)
2 juli 2026, 10:38

Bronnen

(Inhoud volgt; deze sectie wordt later ingevuld.)

13 juni 2026, 13:30

Subsecties van Bronnen

Gratis assets

Je game ziet er meteen professioneler uit met goede sprites, geluidseffecten en muziek, en het hoeft je niets te kosten. Hieronder vind je de beste plekken om gratis assets te zoeken, netjes gesorteerd per categorie.

Sprites & tilesets

  • Kenney.nl: De absolute aanrader. Kenney maakt honderden sprite-packs, tilesets en UI-elementen, allemaal vrijgegeven als CC0 (publiek domein). Je hebt geen attributie nodig en je mag alles ook in commerciële projecten gebruiken. Begin hier.
  • OpenGameArt.org: Grote community met sprites, tilesets en animaties in allerlei stijlen. Let wel op de licentie per asset: sommige zijn CC0, andere CC-BY of CC-BY-SA. Filter op licentie zodat je geen verrassingen krijgt.
  • itch.io gratis game-assets: Veel indie-artiesten zetten gratis packs op itch.io. De kwaliteit varieert, maar er zitten echte pareltjes tussen. Kijk altijd naar de licentietekst op de pagina van de maker.

Geluidseffecten

  • Freesound.org: Enorme bibliotheek met geluidseffecten van over de hele wereld. Filter in de zoekresultaten op “Creative Commons 0” om alleen CC0-geluiden te zien, dan hoef je niemand te crediten.
  • Kenney audio packs: Ook voor geluid levert Kenney kant-en-klare CC0-packs: retro-geluiden, UI-klikjes, explosies, alles in één download.
  • sfxr.me: Genereer je eigen 8-bit geluidseffecten in de browser. Klik op een categorie (laser, explosion, powerup…), pas de sliders aan naar jouw smaak en download de .wav. Geen licentiezorgen: je genereert het zelf.

Muziek

  • Incompetech: Kevin MacLeod componeerde honderden nummers in alle genres, van chiptune tot orkest. De muziek is gratis te gebruiken onder CC-BY: je vermeldt gewoon “Music by Kevin MacLeod (incompetech.com)” ergens in je credits of README.
  • Pixabay music: Tracks die als CC0 of met een eigen vrije licentie zijn vrijgegeven. Je hoeft niets te crediten en je mag het in elk project gebruiken.

Lettertypen

  • Google Fonts: Duizenden open-source lettertypen die je gratis mag downloaden en gebruiken, ook in games. Wil je een retro-uitstraling? Zoek op “pixel” of “monospace” voor lettertypes die goed werken in Pygame (pygame.font.Font).

Licenties uitgelegd

Niet alle “gratis” assets zijn gelijk. Dit zijn de drie licenties die je het vaakst tegenkomt:

LicentieMag je gebruiken?Attributie nodig?Commercieel gebruik?
CC0JaNeeJa
CC-BYJaJa, vermeld de auteurJa
CC-BY-NCJaJaNee

Praktisch advies: Als je twijfelt, gebruik dan CC0. Je hebt geen toestemming nodig, je hoeft niemand te crediten en je loopt nooit het risico per ongeluk de regels te overtreden. Kenney.nl is je beste vriend.


Bekijk hoe wij assets in dit project documenteren op de pagina Asset-licenties.

13 juni 2026, 13:30

Asset-licenties (dit project)

De meeste assets zijn CC0 (publiek domein). Uitzonderingen staan vermeld in de tabel met licentie en verplichte credit-tekst.

Images

BestandBronLicentieAuteurDatum gedownload
basket.pngKenney Space Shooter ReduxCC0Kenney (kenney.nl)2026-05-11
star.pngKenney Space Shooter ReduxCC0Kenney (kenney.nl)2026-05-11
ship.pngKenney Space Shooter ReduxCC0Kenney (kenney.nl)2026-05-11
challenges/01-snake/fruits/*.png (9 stuks)Kenney Food KitCC0Kenney (kenney.nl)2026-05-24

Bestanden staan in static/_shared/images/ en zijn beschikbaar op het gepubliceerde URL-pad /_shared/images/<bestandsnaam>.

Sounds

BestandBronLicentieAuteurDatum gedownload
shoot.wavCC02026-05-11
hit.wavCC02026-05-11
explosion.wavCC02026-05-11
pickup.wavCC02026-05-11
jump.wavCC02026-05-11
music.oggincompetech.comCC-BY 4.0Kevin MacLeod2026-05-17

Verplichte credit voor music.ogg

“Ethereal Relaxation” Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 4.0
creativecommons.org/licenses/by/4.0

Hoe assets toe te voegen

  1. Download van een CC0-bron (kenney.nl, freesound.org filter Creative Commons 0).
  2. Plaats in static/_shared/images/ of static/_shared/sounds/.
  3. Werk een rij bij in de tabel hierboven met bron, licentie, auteur, datum.
  4. Voor niet-CC0 (bv. CC-BY): zet de credit-tekst onder de tabel.
20 juni 2026, 10:54

Publiceren met pygbag

Je hebt een gaaf spel gemaakt en nu wil je het delen met de rest van de wereld. Met pygbag zet je jouw Pygame-spel om in een webpagina die iedereen in de browser kan spelen. Geen installatie, geen gedoe. Gewoon een link sturen en klaar!

Wat is pygbag?

Pygbag compileert je Python-code naar WebAssembly en verpakt het met een kleine Pygame-runtime die in de browser draait. De speler hoeft niks te installeren: gewoon de pagina openen en spelen. Dat maakt het perfect om je spel te delen met vrienden, familie of op itch.io.

Beperkingen

Pygbag werkt niet met Pgzero (de vereenvoudigde bibliotheek van de eerste sessies). Je hebt echte Pygame nodig, dus dit is beschikbaar vanaf sessie 4 en later.

Verder zijn er een paar dingen die niet werken in de browser:

  • Geen schijftoegang: je kunt geen bestanden lezen of schrijven vanuit het spel.
  • Geen threads: gebruik geen threading of multiprocessing.
  • Geen blocking calls: alles moet asynchroon verlopen (zie hieronder).

Installeren

In een uv-project:

uv add pygbag

Of in Thonny via de pakketbeheerder:

pip install pygbag

Project klaarmaken

Pygbag vereist dat je hoofdlus asynchroon is. Dat betekent: je main-functie wordt async def main(), en in elke frame-iteratie voeg je await asyncio.sleep(0) toe. Die ene regel geeft de browser de kans om te ademen tussen frames.

Hieronder vind je een kant-en-klare basisstructuur: Pygbag-klare structuur voor main.py:

import asyncio
import pygame

pygame.init()
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()

async def main():
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
        screen.fill((30, 30, 60))
        pygame.display.flip()
        clock.tick(60)
        await asyncio.sleep(0)  # required for pygbag

asyncio.run(main())

Pas je eigen spelcode aan zodat alles binnen async def main() zit, en vergeet await asyncio.sleep(0) onderaan de while-lus niet.

Bouwen

Navigeer in je terminal naar de map met main.py en voer uit:

uv run python -m pygbag --build main.py

De output verschijnt in de map build/web/. Die map bevat alles wat nodig is om je spel te hosten.

Lokaal testen

Wil je eerst zien of het werkt voordat je publiceert? Gebruik hetzelfde commando zonder --build:

uv run python -m pygbag main.py

Pygbag start een lokale webserver op http://localhost:8000. Open die link in je browser en je speelt je eigen spel, precies zoals anderen het zullen zien.

Publiceren

Als alles werkt, zijn er twee manieren om je spel te delen:

  1. Via de coach: zip de map build/web/ en geef het zipbestand aan je coach. Die zorgt voor de hosting.
  2. Via itch.io: maak een gratis account op itch.io, maak een nieuw project aan als HTML-game, en upload de zip van build/web/. Binnen een paar minuten heeft je spel een eigen pagina die iedereen kan bezoeken!

Gefeliciteerd! Je hebt zojuist een echt webspel gepubliceerd. 🎮

13 juni 2026, 13:30

Vertaalgids

Voor een toekomstige Engelse versie.

Dit curriculum is gepubliceerd in het Nederlands. Een Engelse versie is niet gepland voor v1, maar de inhoud is gestructureerd om vertaling eenvoudig te maken.

Wat niet vertalen

  • Code-identifiers (basket, star, ship, score) blijven Engels, want ze staan in het Python-bestand.
  • Bestandsnamen (star.png, boom.wav) blijven taalneutraal.
  • Domein-termen die we als leenwoorden gebruiken:
    • “ninja” (CoderDojo-term)
    • “dojo”
    • “cheatsheet”
    • “cliffhanger”
    • “Dojo Defender” (eigennaam van de huisgame)
    • “pygbag”, “Pgzero”, “Pygame”, “Thonny” (productnamen)

Wat wel vertalen

  • Alle prozatekst in .md files.
  • Cheatsheet-titels en sectie-koppen.
  • Coach-notities.
  • Take-it-home uitbreidingsideeën.

Hoe een Engelse mirror te starten

Niet gepland voor v1. Mogelijke toekomstige aanpakken:

  • Optie A: Hugo’s ingebouwde i18n: een tweede taal toevoegen in hugo.toml en <page>.en.md per pagina.
  • Optie B: sibling content tree content.en/ met dezelfde structuur.
  • Optie C: fork voor een Engelstalige dojo.

Beslis dit als er een Engelstalige dojo om vraagt, niet eerder.