Vallende stenen
0 Wat gaan we leren
We gaan nog een projectje maken met Thonny (uit de vorige les) en bij dit project gaan we gebruik maken van de standaard Python library:
pgzero
Weet je nog hoe je een pacakge installeert in Thonny?
Yep, Tools - Manage Packages en dan pgzero zoeken en installeren.
1 Teken speler en steen
In deze les gaan we de speler en één vallende steen tekenen.
We beginnen met een eenvoudige versie: een blokje onderaan dat je straks kunt besturen, en een steen die we straks laten vallen.
Wat gaan we doen?
We tekenen een rechthoek voor de speler en een rechthoek voor de steen.
We gebruiken vaste posities om de eerste versie werkend te krijgen.
🔰 Code
import pgzrun
WIDTH = 800
HEIGHT = 600
# Speler onderaan het scherm
player_x = 400
player_y = 550
player_width = 80
player_height = 20
# Vallende steen bovenaan
rock_x = 300
rock_y = 0
rock_size = 40
def draw():
screen.clear()
screen.draw.filled_rect(Rect((player_x, player_y), (player_width, player_height)), "blue")
screen.draw.filled_rect(Rect((rock_x, rock_y), (rock_size, rock_size)), "gray")
pgzrun.go()
ℹ️ Uitleg
player_x
enplayer_y
: positie van de speler (een blauw blokje onderaan)rock_x
enrock_y
: positie van de steenscreen.draw.filled_rect(...)
: tekent een blokje op het scherm
🛠️ Opdracht
- Verplaats de steen naar een andere plek op het scherm door
rock_x
enrock_y
aan te passen - Maak de speler breder of smaller
- Verander de kleuren van speler en steen
💡 Extra uitdaging
- Teken meerdere stenen op het scherm (gebruik meerdere
draw.filled_rect()
)
📤 Inleveren
- Maak een screenshot waarop je de speler en minstens één steen ziet (waarbij je de plaats van de steen dus hebt aangepast).
2 Speler bewegen
In deze les gaan we de speler besturen met de pijltjestoetsen.
De speler beweegt alleen naar links en rechts, en mag niet buiten het scherm gaan.
Wat gaan we doen?
We bewerken de update()
-functie om de x
-positie van de speler aan te passen als je op pijltjes drukt.
We voegen een maximale en minimale positie toe zodat de speler niet van het scherm glijdt.
🔰 Code
import pgzrun
WIDTH = 800
HEIGHT = 600
player_x = 400
player_y = 550
player_width = 80
player_height = 20
player_speed = 5
rock_x = 300
rock_y = 0
rock_size = 40
def draw():
screen.clear()
screen.draw.filled_rect(Rect((player_x, player_y), (player_width, player_height)), "blue")
screen.draw.filled_rect(Rect((rock_x, rock_y), (rock_size, rock_size)), "gray")
def update():
global player_x
if keyboard.left:
player_x -= player_speed
if keyboard.right:
player_x += player_speed
# Speler binnen scherm houden
if player_x < 0:
player_x = 30
if player_x > WIDTH - player_width:
player_x = WIDTH - player_width
pgzrun.go()
ℹ️ Uitleg
player_speed
: hoe snel de speler beweegtkeyboard.left
enkeyboard.right
: detecteren of een toets is ingedruktif player_x > WIDTH - player_width
: voorkomt dat de speler buiten beeld schuift
🛠️ Opdracht
- Beweeg de speler heen en weer met je pijltjestoetsen
- Verander
player_speed
– wordt de speler sneller of trager? - Aan de linker kant van het scherm stuitert de speler terwijl aan de rechterkant de speler netjes op de rand stopt. Zie jij hoe dat komt? Probeer dit aan te passen, probeer gewoon maar wat, je kan niets kapot maken!
Tip: bij welke if
wordt gekeken of de speler tegen de linkerkant van het scherm aan zit?
💡 Extra uitdaging
- Laat de speler sneller bewegen als je de toets langer inhoudt
- Laat de speler automatisch naar links of rechts bewegen als je een extra toets indrukt (bijvoorbeeld
A
ofD
)
📤 Inleveren
- Leg in eigen woorden uit hoe de
player_speed
de snelheid van het spel veranderd. - Leg uit waarom de speler aan de linkerkant van het scherm 'stuitert' en wat heb je aangepast om dit te voorkomen?
3 Steen laten vallen
In deze les gaan we de steen automatisch laten vallen.
Zodra de steen de onderkant van het scherm bereikt, verschijnt hij opnieuw bovenaan op een willekeurige plek.
Wat gaan we doen?
We voegen een rock_speed
toe en laten de y
-positie van de steen langzaam toenemen in update()
.
We controleren of de steen het scherm uit valt, en zetten hem dan opnieuw bovenaan met een willekeurige x-positie.
🔰 Code
import pgzrun
import random
WIDTH = 800
HEIGHT = 600
player_x = 400
player_y = 550
player_width = 80
player_height = 20
player_speed = 5
rock_x = random.randint(0, WIDTH - 40)
rock_y = 0
rock_size = 40
rock_speed = 3
def draw():
screen.clear()
screen.draw.filled_rect(Rect((player_x, player_y), (player_width, player_height)), "blue")
screen.draw.filled_rect(Rect((rock_x, rock_y), (rock_size, rock_size)), "gray")
def update():
global player_x, rock_y, rock_x, rock_size
if keyboard.left:
player_x -= player_speed
if keyboard.right:
player_x += player_speed
if player_x < 0:
player_x = 0
if player_x > WIDTH - player_width:
player_x = WIDTH - player_width
# Steen laten vallen
rock_y += rock_speed
# Als de steen onderaan is, zet hem weer bovenaan met random x
if rock_y > HEIGHT:
rock_y = 0
rock_x = random.randint(0, WIDTH - rock_size)
pgzrun.go()
ℹ️ Uitleg
rock_speed
: bepaalt hoe snel de steen valtrock_y += rock_speed
: laat de steen naar beneden bewegenrandom.randint(...)
: zorgt voor een willekeurige x-positie als de steen opnieuw verschijnt
🛠️ Opdracht
- Verander de
rock_speed
– wat gebeurt er? - Maak de steen groter of kleiner door
rock_size
aan te passen. - Zorg er nu voor dat elke keer als de steen opnieuw valt de rock_size wordt aangepast en een random grootte krijgt.
- Met random.randint(1, 10) wordt er een getal tussen 1 en 10 gegenereerd. Bedenk zelf mooie waarden en pas de code aan zodat de grootte van de steen telkens anders wordt.
💡 Extra uitdaging
- Laat meerdere stenen tegelijk vallen
- Laat elke steen een willekeurige snelheid hebben
📤 Inleveren
- Leg uit hoe je ervoor hebt gezorgdt dat de steen telkens een ander grootte kijgt.
4 Botsing & Game Over
In deze les gaan we controleren of de speler de steen raakt.
Als er een botsing is, stopt het spel en verschijnt er de tekst “Game Over”.
Wat gaan we doen?
We maken een Rect
van de speler en van de steen, en gebruiken colliderect()
om te zien of ze elkaar raken.
We gebruiken een variabele game_over
om te stoppen met het spel als er een botsing is.
🔰 Code
import pgzrun
import random
from pygame import Rect
WIDTH = 800
HEIGHT = 600
player_x = 400
player_y = 550
player_width = 80
player_height = 20
player_speed = 5
rock_x = random.randint(0, WIDTH - 40)
rock_y = 0
rock_size = 40
rock_speed = 3
game_over = False
def draw():
screen.clear()
if game_over:
screen.draw.text("GAME OVER", center=(WIDTH // 2, HEIGHT // 2), fontsize=60, color="red")
return
screen.draw.filled_rect(Rect((player_x, player_y), (player_width, player_height)), "blue")
screen.draw.filled_rect(Rect((rock_x, rock_y), (rock_size, rock_size)), "gray")
def update():
global player_x, rock_y, rock_x, game_over
if game_over:
return
if keyboard.left:
player_x -= player_speed
if keyboard.right:
player_x += player_speed
if player_x < 0:
player_x = 0
if player_x > WIDTH - player_width:
player_x = WIDTH - player_width
rock_y += rock_speed
if rock_y > HEIGHT:
rock_y = 0
rock_x = random.randint(0, WIDTH - rock_size)
# Botsing detecteren
speler_rect = Rect(player_x, player_y, player_width, player_height)
steen_rect = Rect(rock_x, rock_y, rock_size, rock_size)
if speler_rect.colliderect(steen_rect):
game_over = True
pgzrun.go()
ℹ️ Uitleg
Rect(x, y, w, h)
maakt een rechthoek op de juiste plekcolliderect()
kijkt of twee rechthoeken elkaar rakengame_over
bepaalt of het spel nog doorgaat
🛠️ Opdracht
- Laat de speler de steen raken en kijk of het spel stopt
- Verplaats de speler naar de andere kant van het scherm – bots je dan nog?
- Verander de “GAME OVER” tekst (bijvoorbeeld kleur of grootte)
💡 Extra uitdaging
- Laat het spel herstarten als je op de
R
-toets drukt - Speel een geluid af bij de botsing (bijv.
sounds.hit.play()
)
📤 Inleveren
- Leg eersts stap-voor-stap in he eigen woorden uit hoe er een botsing van de steen met de speler wordt gedetecteerd.
- Leg daarna stap-voor-stap, in eigen woorden uit wat er allemaal gebeurt als de steen tegen de speler aan komt. Verwijs daarbij naar de code.
5 Meerdere stenen & moeilijker maken
In deze les gaan we meerdere stenen tegelijk laten vallen.
Bovendien maakt het spel zichzelf moeilijker naarmate je langer speelt: de stenen vallen sneller.
Wat gaan we doen?
We maken een lijst van stenen, elk met hun eigen positie en snelheid.
We laten deze stenen vallen en bij een botsing stoppen we het spel.
We laten het spel steeds moeilijker worden door de snelheid te verhogen na een paar seconden.
🔰 Code
import pgzrun
import random
from pygame import Rect
WIDTH = 800
HEIGHT = 600
player_x = 400
player_y = 550
player_width = 80
player_height = 20
player_speed = 5
rocks = []
game_over = False
rock_timer = 0
rock_interval = 60 # frames
difficulty = 1.0
# Start met een paar stenen
for _ in range(3):
x = random.randint(0, WIDTH - 40)
speed = random.uniform(2, 4)
rocks.append({"x": x, "y": 0, "size": 40, "speed": speed})
def draw():
screen.clear()
if game_over:
screen.draw.text("GAME OVER", center=(WIDTH // 2, HEIGHT // 2), fontsize=60, color="red")
return
screen.draw.filled_rect(Rect((player_x, player_y), (player_width, player_height)), "blue")
for rock in rocks:
screen.draw.filled_rect(Rect((rock["x"], rock["y"]), (rock["size"], rock["size"])), "gray")
def update():
global player_x, game_over, rock_timer, difficulty
if game_over:
return
if keyboard.left:
player_x -= player_speed
if keyboard.right:
player_x += player_speed
player_x = max(0, min(WIDTH - player_width, player_x))
speler_rect = Rect(player_x, player_y, player_width, player_height)
for rock in rocks:
rock["y"] += rock["speed"] * difficulty
if rock["y"] > HEIGHT:
rock["y"] = 0
rock["x"] = random.randint(0, WIDTH - rock["size"])
rock["speed"] = random.uniform(2, 5)
rock_rect = Rect(rock["x"], rock["y"], rock["size"], rock["size"])
if speler_rect.colliderect(rock_rect):
game_over = True
# Verhoog de moeilijkheid langzaam
rock_timer += 1
if rock_timer % 300 == 0:
difficulty += 0.2
pgzrun.go()
ℹ️ Uitleg
- Elke steen is een dict met
x
,y
,size
enspeed
- We gebruiken een
for
-loop om alle stenen te laten vallen - Met
difficulty
verhogen we langzaam de snelheid van de stenen
🛠️ Opdracht
- Test het spel en kijk of het spel moeilijker wordt na ongeveer 15 seconden
- Nu gaan we de grootte van de stenen weer aanpassen naar een random waarde zoals we dat bij de vorige opdracht ook hebben gedaan.
- Dat gaat iets anders omdat we nu meerdere 'rocks' hebben. In de
for rock in rocks:
loop worden één voor één alle blokken behandeld. De grootte van de rock staat inrock["size"]
- Dat gaat iets anders omdat we nu meerdere 'rocks' hebben. In de
Tip: weet je nog dat je met random.randint(1,4)
een getal tussen 1 en 4 kan genereren?
💡 Extra uitdaging
- Laat het aantal stenen toenemen naarmate het spel langer duurt
- Laat een andere kleur steen verschijnen bij hogere moeilijkheid.
- Laat elke steen een ander formaat hebben
- Toon je “score” op het scherm: hoe lang heb je overleefd?
📤 Inleveren
- Lever je code (.py bestand) in.
6 Bonusstenen en Score
In deze les voegen we een nieuw soort steen toe: de bonussteen. Deze blokjes hebben een andere kleur en een getal erin tussen -5 en +5. Als je ze opvangt krijg je (of verlies je) punten.
Wat gaan we doen?
- We maken een lijst met bonusstenen naast de gewone stenen.
- Elke bonussteen heeft een willekeurige waarde tussen -5 en +5.
- Als je een bonussteen opvangt, verandert je score met dat aantal punten.
🔰 Code
import pgzrun
import random
from pygame import Rect
WIDTH = 800
HEIGHT = 600
player_x = 400
player_y = 550
player_width = 80
player_height = 20
player_speed = 5
rocks = []
bonusstones = []
score = 0
game_over = False
# Start met een paar normale stenen
for _ in range(3):
rocks.append({
"x": random.randint(0, WIDTH - 40),
"y": 0,
"size": 40,
"speed": random.uniform(2, 4)
})
# Start met een paar bonusstenen
for _ in range(2):
bonusstones.append({
"x": random.randint(0, WIDTH - 40),
"y": -random.randint(100, 300),
"size": 40,
"speed": random.uniform(1.5, 3),
"value": random.randint(-5, 5)
})
def draw():
screen.clear()
if game_over:
screen.draw.text("GAME OVER", center=(WIDTH // 2, HEIGHT // 2), fontsize=60, color="red")
return
screen.draw.filled_rect(Rect((player_x, player_y), (player_width, player_height)), "blue")
screen.draw.text(f"Score: {score}", topleft=(10, 10), fontsize=30, color="white")
for rock in rocks:
screen.draw.filled_rect(Rect((rock["x"], rock["y"]), (rock["size"], rock["size"])), "gray")
for bonus in bonusstones:
rect = Rect((bonus["x"], bonus["y"]), (bonus["size"], bonus["size"]))
color = "green" if bonus["value"] >= 0 else "orange"
screen.draw.filled_rect(rect, color)
screen.draw.text(str(bonus["value"]), center=rect.center, fontsize=24, color="black")
def update():
global player_x, game_over, score
if game_over:
return
if keyboard.left:
player_x -= player_speed
if keyboard.right:
player_x += player_speed
player_x = max(0, min(WIDTH - player_width, player_x))
speler_rect = Rect(player_x, player_y, player_width, player_height)
for rock in rocks:
rock["y"] += rock["speed"]
if rock["y"] > HEIGHT:
rock["y"] = 0
rock["x"] = random.randint(0, WIDTH - rock["size"])
rock["speed"] = random.uniform(2, 5)
rock_rect = Rect(rock["x"], rock["y"], rock["size"], rock["size"])
if speler_rect.colliderect(rock_rect):
game_over = True
for bonus in bonusstones:
bonus["y"] += bonus["speed"]
if bonus["y"] > HEIGHT:
bonus["y"] = 0
bonus["x"] = random.randint(0, WIDTH - bonus["size"])
bonus["speed"] = random.uniform(1.5, 3)
bonus["value"] = random.randint(-5, 5)
bonus_rect = Rect(bonus["x"], bonus["y"], bonus["size"], bonus["size"])
if speler_rect.colliderect(bonus_rect):
score += bonus["value"]
bonus["y"] = 0
bonus["x"] = random.randint(0, WIDTH - bonus["size"])
bonus["value"] = random.randint(-5, 5)
pgzrun.go()
ℹ️ Uitleg
bonusstones
: lijst met bonusstenen, net als bijrocks
value
: getal tussen -5 en 5 dat je toevoegt aan je scorescreen.draw.text()
: hiermee schrijf je het cijfer in de bonussteenscore += bonus["value"]
: verhoog of verlaag de score
🛠️ Opdracht
- Vang een bonussteen op en kijk of je score verandert
- Wat gebeurt er als je een steen met een negatieve waarde pakt?
- Verander de kleuren van de positieve en negatieve stenen.
- In de vorige les heb je de grootte van de stenen laten veranderen. Elke steen had een random grootte.
Zorg ervoor dat in ieder geval de grijze stenen een weer een random gtootte krijgen.
💡 Extra uitdaging
- Laat de bonusstenen zeldzamer worden (bijvoorbeeld maar één tegelijk op het scherm)
- Laat alleen positieve bonusstenen verschijnen als je score negatief is
📤 Inleveren
- Leg uit wat je hebt aangepast; kleur van de stenen? En welke stenen heb je een random grootte gegeven; de grijze, de gekleurde? Leg uit hoe je de stenen een random waarde hebt gegegven en waar je dat in de code hebt gedaan?
7 Score-overzicht bij Game Over
In deze les leer je een scorescherm maken dat verschijnt als je verliest. Daarin zie je hoeveel bonuspunten je had, hoeveel negatieve stenen je raakte, en je eindscore.
In deze les geven we niet meer de gehele code maar geven we alleen aan hoe je de code moet aanpassen. Dit is een goede oefening want als code heel complex is het belangijk om te kunnen begrijpen waar wat moet staan in code.
🔧 Wat voegen we toe?
- Tel hoeveel bonusstenen je hebt gevangen
- Tel hoeveel daarvan positief of negatief waren
- Toon een scorescherm als je Game Over bent
📌 Stap 1: Tel positieve en negatieve bonusstenen
Doel: zet de variablen waarin de scores worden bijgehouden op 0. Doe dit bij de start van je programma.
Hoe: Voeg bovenin je script de volgende variabelen toe, net onder score = 0
:
bonus_hits = 0
positive_hits = 0
negative_hits = 0
Doel: In de def_update die telkens wordt uitgevoerd gaan we de scores bijwerken
Hoe: Zorg er eerst voor dat je de variabelen in de def_update kan gebruiken:
Verander de eerste regel na def_update()
in
global player_x, game_over, score, bonus_hits, positive_hits, negative_hits
Hoe: werk nu de variabelen bij
Voeg in je update()
-functie, bij het botsen met een bonussteen, dit toe na score += bonus["value"]
:
bonus_hits += 1
if bonus["value"] > 0:
positive_hits += 1
else:
negative_hits += 1
📌 Stap 2: Toon scores bij game over
Pas in je draw()
-functie het stuk aan waar de tekst "GAME OVER" wordt getekend. Vervang dit door het volgende:
if game_over:
screen.draw.text("GAME OVER", center=(WIDTH // 2, HEIGHT // 2 - 60), fontsize=60, color="red")
screen.draw.text(f"Score: {score}", center=(WIDTH // 2, HEIGHT // 2), fontsize=40, color="white")
screen.draw.text(f"Bonusstenen geraakt: {bonus_hits}", center=(WIDTH // 2, HEIGHT // 2 + 40), fontsize=30, color="lightblue")
screen.draw.text(f"Positief: {positive_hits} | Negatief: {negative_hits}", center=(WIDTH // 2, HEIGHT // 2 + 80), fontsize=30, color="yellow")
return
ℹ️ Uitleg
bonus_hits
: telt hoeveel bonusstenen je geraakt hebtpositive_hits
ennegative_hits
: verdeling van de bonuspuntendraw.text(...)
: toont extra regels tekst op het scherm bij game over
🛠️ Opdracht
- Voeg de drie nieuwe variabelen toe
- Pas de
update()
-functie aan zodat je weet welke bonusstenen geraakt zijn - Pas de
draw()
-functie aan zodat je een eindscherm krijgt met statistieken
💡 Extra uitdaging
- Toon ook hoeveel seconden je hebt gespeeld
- Laat bij positieve score een groene tekst zien, en bij negatieve score een rode
📤 Inleveren
- Maak een screenshot van je eindscherm
- Lever je .py-bestand in
8 Vrije uitbreiding met AI
In deze les ga je helemaal zelf een uitbreiding bedenken én proberen toe te voegen aan het spel. Je mag daarbij hulp vragen aan ChatGPT of een andere AI. Dit wordt je eigen versie van het spel!
🧠 Wat ga je doen?
- Je bedenkt een eigen uitbreiding of verbetering voor het spel
- Je maakt zelf een plan: wat wil je precies veranderen of toevoegen?
- Je gebruikt AI om hulp te vragen bij het schrijven van de code
💡 Suggesties voor uitbreidingen
- 🎮 Voeg een startscherm toe met “druk op spatie om te starten”
- 📦 Voeg power-ups toe: tijdelijk onzichtbaar of sneller bewegen
- 🏁 Maak het spel eindig: speel 60 seconden en toon dan de eindscore
- 🧱 Voeg obstakels toe die op het veld blijven liggen
- 🎵 Voeg muziek of geluidseffecten toe
- 🔁 Laat de speler herstarten met de
R
-toets - 🌈 Laat kleuren of graphics veranderen naarmate het spel vordert
🤖 AI slim gebruiken
Gebruik ChatGPT om hulp te vragen. Voorbeelden van goede vragen zijn:
- “Hoe voeg ik geluid toe aan een Pygame Zero spel?”
- “Hoe maak ik een timer die telt vanaf 0?”
- “Hoe zorg ik dat mijn speler niet door obstakels heen kan?”
Vraag niet gewoon: “Schrijf de hele code voor mij”, maar stel gerichte vragen. Jij bent de maker, de AI helpt je alleen.
Probeer te begrijpen wat je doet.
🛠️ Opdracht
- Bedenk een uitbreiding die je leuk lijkt
- Schrijf kort op wat je wil maken
- Gebruik AI om gerichte vragen te stellen
- Voeg de uitbreiding toe aan je code
💡 Extra uitdaging
- Werk samen in tweetallen: ieder met een eigen idee
- Presenteer je spel kort aan de klas
- Laat anderen jouw spel testen en feedback geven
📤 Inleveren
- Scrhijf wat voor uitbreiding je hebt bedacht en zet je AI -log (dus alle vragen en atwoorden) in een .txt bestand (pdf mag ook) en lever die in.
9 Reflectieverslag
In deze laatste opdracht kijk je terug op wat je geleerd hebt tijdens het maken van het project Vallende Stenen. Je schrijft een kort reflectieverslag waarin je vertelt wat je goed vond gaan, wat lastig was, en wat je geleerd hebt.
📝 Wat ga je doen?
- Je kijkt terug op je werk in dit project
- Je beantwoordt de reflectievragen hieronder
- Je levert je reflectie digitaal in, in een pdf bestand.
📋 Reflectievragen
Beantwoord de volgende vragen in een paar zinnen per vraag:
- Wat vond je het leukste onderdeel van het spel om te maken? Waarom?
- Wat vond je het moeilijkst? Hoe heb je dat opgelost?
- Welke uitbreiding of uitdaging heb je zelf gekozen? Hoe is dat gegaan?
- Wat heb je geleerd over programmeren met Python of Pygame Zero?
- Hoe heb je AI (bijv. ChatGPT) gebruikt? Wat ging er goed? Wat vond je lastig?
- Wat zou je anders doen als je het spel nog een keer mocht maken?
📤 Inleveren
- Refelctieverstlag met antwoor dop de reflectievagen.
--