Ga naar inhoud

Level 6: Bouw Je Eigen Spel!

Kies een starter

Dit level heeft 3 starter kits. Elke starter leert je iets nieuws. Kies er een, of combineer ideeën uit meerdere starters!

Het doel

Bouw je eigen tactisch zombie spel! Gebruik alles wat je geleerd hebt in de vorige levels en voeg nieuwe dingen toe.

De starters

Starter A: Game States

cd levels/level-6/starter-a-states
pgzrun zombie.py
Bekijk de code
# =============================================================================
# ZOMBIE APOCALYPSE - Level 6 - Starter A: Game States
# =============================================================================
# Start met: pgzrun zombie.py
#
# Deze starter laat zien hoe je een Enum gebruikt voor game states.
# In Level 5 gebruikten we strings zoals "spel" en "game_over".
# Met een Enum kan dat niet meer fout gaan!
#
# Nieuw in deze starter:
# - Enum: een vaste lijst van mogelijke waarden
# - Duidelijkere code door Toestand.SPEL i.p.v. "spel"
# =============================================================================

import random
from enum import Enum

from pgzero.builtins import clock
from pgzero.screen import Screen
screen: Screen

WIDTH = 800
HEIGHT = 600
TITLE = "Brainstorm"


# =============================================================================
# ENUM VOOR GAME STATES
# =============================================================================
# Een Enum (enumeration) is een klasse met vaste waarden.
# Voordeel t.o.v. strings:
# - Je kan geen typfout maken (Toestand.SPEL vs "speel")
# - Je editor helpt je met automatisch aanvullen
# - Je ziet meteen welke toestanden er zijn
#
# class Seizoen(Enum):
#     LENTE = "lente"
#     ZOMER = "zomer"
#     HERFST = "herfst"
#     WINTER = "winter"
# =============================================================================
class Toestand(Enum):
    SPEL = "spel"
    RESULTAAT = "resultaat"
    GAME_OVER = "game_over"


toestand = Toestand.SPEL
levens = 3
resultaat_tekst = ""


def draw():
    if toestand == Toestand.SPEL:
        screen.blit("achtergrond", (0, 0))

        for i in range(levens):
            screen.blit("hart", (20 + i * 50, 20))

        screen.draw.text("Klik om te vechten!", center=(400, 300), fontsize=48, color="white")

    elif toestand == Toestand.RESULTAAT:
        screen.blit("achtergrond", (0, 0))
        screen.draw.text(resultaat_tekst, center=(400, 300), fontsize=48, color="white")

    elif toestand == Toestand.GAME_OVER:
        screen.blit("game_over", (0, 0))
        screen.draw.text("GAME OVER", center=(400, 250), fontsize=60, color="red")
        screen.draw.text("Klik om opnieuw te spelen", center=(400, 350), fontsize=24, color="gray")


def update(dt):
    """Wordt elk frame aangeroepen. Handig voor animaties!"""
    pass


def on_mouse_down(pos):
    global toestand, levens, resultaat_tekst

    if toestand == Toestand.SPEL:
        if random.randint(1, 2) == 1:
            resultaat_tekst = "Je verslaat de zombie!"
        else:
            resultaat_tekst = "De zombie bijt je..."
            levens -= 1

        toestand = Toestand.RESULTAAT
        clock.schedule(ga_naar_volgende, 2.0)

    elif toestand == Toestand.GAME_OVER:
        toestand = Toestand.SPEL
        levens = 3


def ga_naar_volgende():
    global toestand
    if levens <= 0:
        toestand = Toestand.GAME_OVER
    else:
        toestand = Toestand.SPEL

Een Enum is een klasse met vaste waarden. In Level 5 gebruikten we strings zoals "spel" en "game_over". Met een Enum kan je geen typfouten meer maken!

from enum import Enum

class Toestand(Enum):
    SPEL = "spel"
    RESULTAAT = "resultaat"
    GAME_OVER = "game_over"

In plaats van:

if toestand == "spel":     # typfout? "speel"? 😱

Gebruik je nu:

if toestand == Toestand.SPEL:  # geen typfout mogelijk! ✅

Je editor helpt je zelfs met automatisch aanvullen.


Starter B: Locaties

cd levels/level-6/starter-b-world-map
pgzrun zombie.py
Bekijk de code
# =============================================================================
# ZOMBIE APOCALYPSE - Level 6 - Starter B: Locaties
# =============================================================================
# Start met: pgzrun zombie.py
#
# Deze starter laat zien hoe je met pijltjestoetsen tussen
# locaties kan navigeren. Elke locatie heeft een eigen achtergrond.
#
# Nieuw in deze starter:
# - Keyboard input met on_key_down()
# - Navigatie tussen verschillende schermen
# =============================================================================

from pgzero.screen import Screen
screen: Screen

WIDTH = 800
HEIGHT = 600
TITLE = "Brainstorm"


# =============================================================================
# LOCATIES
# =============================================================================
# Een lijst van tuples: (naam, afbeelding)
#
# Een tuple lijkt op een lijst maar kan niet veranderd worden.
# Perfect voor vaste data!
#   locaties[0]    -> ("Kelder", "loc_basement")
#   locaties[0][0] -> "Kelder"
#   locaties[0][1] -> "loc_basement"
# =============================================================================
locaties = [
    ("Kelder", "loc_basement"),
    ("Supermarkt", "loc_supermarket"),
    ("Park", "loc_park"),
    ("Dak", "loc_rooftop"),
]

huidige_locatie = 0


def draw():
    naam, afbeelding = locaties[huidige_locatie]

    screen.blit(afbeelding, (0, 0))
    screen.draw.text(naam.upper(), center=(400, 50), fontsize=48, color="white",
                     shadow=(2, 2), scolor="black")

    teken_navigatie()







# =============================================================================
# KEYBOARD INPUT
# =============================================================================
# on_key_down(key) wordt aangeroepen als je een toets indrukt.
# key is een constante zoals keys.LEFT, keys.SPACE, etc.
# =============================================================================
def on_key_down(key):
    if key == keys.UP:
        ga_naar(-1)
    elif key == keys.DOWN:
        ga_naar(1)


def on_mouse_down(pos):
    x, y = pos
    # Klik op de pijltjes onderaan
    if 350 < x < 450:
        if y > 545:
            ga_naar(1)
        elif y > 510:
            ga_naar(-1)


def update(dt):
    """Wordt elk frame aangeroepen. Handig voor animaties!"""
    pass


# =============================================================================
# HELPER FUNCTIES
# =============================================================================
def ga_naar(richting):
    """Verander van locatie. richting is 1 (vooruit) of -1 (terug)."""
    global huidige_locatie
    nieuwe_locatie = huidige_locatie + richting
    if 0 <= nieuwe_locatie < len(locaties):
        huidige_locatie = nieuwe_locatie


def teken_navigatie():
    """Teken pijltjes onderaan om te navigeren (klikbaar!)."""
    if huidige_locatie > 0:
        screen.draw.text("^", center=(400, 530), fontsize=60, color="white",
                         shadow=(2, 2), scolor="black")
    if huidige_locatie < len(locaties) - 1:
        screen.draw.text("v", center=(400, 560), fontsize=60, color="white",
                         shadow=(2, 2), scolor="black")

Navigeer met de pijltjestoetsen (of klik) tussen verschillende locaties. Elke locatie heeft een eigen achtergrond.

Nieuw: Keyboard input!

def on_key_down(key):
    if key == keys.UP:
        ga_naar(-1)
    elif key == keys.DOWN:
        ga_naar(1)

Dit is anders dan on_mouse_down(pos) uit Level 5. Andere toetsen die je kan gebruiken: keys.LEFT, keys.RIGHT, keys.SPACE, keys.ESCAPE.

Nieuw: Tuples!

locaties = [
    ("Kelder", "loc_basement"),
    ("Supermarkt", "loc_supermarket"),
    ("Park", "loc_park"),
    ("Dak", "loc_rooftop"),
]

Een tuple lijkt op een lijst maar kan niet veranderd worden. Perfect voor vaste data! Je kan de waarden uitpakken:

naam, afbeelding = locaties[0]
# naam = "Kelder", afbeelding = "loc_basement"

Starter C: JSON Data

cd levels/level-6/starter-c-json-data
pgzrun zombie.py
Bekijk de code
# =============================================================================
# ZOMBIE APOCALYPSE - Level 6 - Starter C: JSON Data
# =============================================================================
# Start met: pgzrun zombie.py
#
# Deze starter laat zien hoe je game data uit een JSON bestand laadt.
# Zo kan je zombies, items en locaties aanpassen zonder code te wijzigen!
#
# Nieuw in deze starter:
# - JSON bestanden laden met json.load()
# - Data-driven design: speldata staat los van de code
# - Afbeeldingen laden op basis van namen uit data
# =============================================================================

import json
import random

import pygame
from pgzero.builtins import Rect, images
from pgzero.screen import Screen
screen: Screen

WIDTH = 800
HEIGHT = 600
TITLE = "Brainstorm"


# =============================================================================
# JSON LADEN
# =============================================================================
# JSON (JavaScript Object Notation) is een standaard formaat
# om data op te slaan. Het lijkt heel erg op Python dictionaries!
#
# json.load(bestand) leest een JSON bestand en geeft een
# Python dictionary of lijst terug.
#
# Voordeel: je kan de data aanpassen in data.json zonder
# de Python code te veranderen!
# =============================================================================
with open("data.json") as bestand:
    data = json.load(bestand)

zombies = data["zombies"]

# Willekeurige zombie uit de JSON data
huidige_zombie = random.choice(zombies)


def draw():
    screen.fill((30, 30, 40))

    teken_tekst("Zombie Dex", center=(400, 40), fontsize=44)
    teken_zombie(huidige_zombie)

    teken_tekst("Klik voor een andere zombie",
                center=(400, HEIGHT - 40), fontsize=20, kleur="gray")


def teken_zombie(zombie):
    """Teken een zombie met afbeelding en stats."""
    # ==========================================================================
    # AFBEELDING OP BASIS VAN DATA
    # ==========================================================================
    # In data.json staat: "afbeelding": "zombie_chef"
    # Dit is dezelfde naam als het bestand in images/: zombie_chef.png
    #
    # getattr(images, naam) laadt het plaatje met die naam
    # transform.scale past de grootte van het plaatje aan
    # ==========================================================================
    img = getattr(images, zombie["afbeelding"])
    geschaald = pygame.transform.scale(img, (210, 140))
    screen.surface.blit(geschaald, (295, 100))

    # Zombie naam
    teken_tekst(zombie["naam"], center=(400, 310), fontsize=36)

    # ==========================================================================
    # STATS TEKENEN
    # ==========================================================================
    # We tekenen een simpele balk voor elke stat
    # De breedte van de balk is gebaseerd op de waarde (1-5)
    # ==========================================================================
    stats = zombie["stats"]
    stat_namen = {
        "snelheid": "Snelheid",
        "kracht": "Kracht",
        "slimheid": "Slimheid",
    }

    y_start = 400
    for i, (sleutel, label) in enumerate(stat_namen.items()):
        y = y_start + i * 50
        waarde = stats[sleutel]

        # Label
        teken_tekst(label, center=(250, y), fontsize=24)

        # Achtergrond balk
        balk_x = 340
        balk_breedte = 200
        screen.draw.filled_rect(Rect(balk_x, y - 10, balk_breedte, 20), (60, 60, 80))

        # Gekleurde balk op basis van waarde (1-5)
        gevuld = int(balk_breedte * waarde / 5)
        kleur = (50 + waarde * 40, 200 - waarde * 30, 50)
        screen.draw.filled_rect(Rect(balk_x, y - 10, gevuld, 20), kleur)

        # Waarde als tekst
        teken_tekst(str(waarde), center=(balk_x + balk_breedte + 30, y), fontsize=24)


def on_mouse_down(pos):
    """Klik voor een nieuwe willekeurige zombie."""
    global huidige_zombie
    huidige_zombie = random.choice(zombies)


def teken_tekst(tekst, center, fontsize, kleur="white"):
    x, y = center
    screen.draw.text(tekst, center=(x+2, y+2), fontsize=fontsize, color="black")
    screen.draw.text(tekst, center=(x, y), fontsize=fontsize, color=kleur)

Laad zombie data uit een JSON bestand. Zo staat je speldata los van de code!

Nieuw: JSON laden!

import json

with open("data.json") as bestand:
    data = json.load(bestand)

zombies = data["zombies"]

JSON lijkt heel erg op Python dictionaries. Open data.json om te zien hoe de data eruitziet. Je kan makkelijk zombies toevoegen of aanpassen zonder de Python code te veranderen.

En nu?

Website

De Brainstorm, Zombiepedia en Pygame Recepten staan op de website: laoujin.github.io/CoderDojo-Zombies

Extra plaatjes

Elke starter heeft alleen de plaatjes die hij nodig heeft. Wil je meer? In levels/level-5/images/ vind je 70+ plaatjes: achtergronden, knoppen, items, zombies en locaties. In levels/level-5/images/sprites/ vind je losse zombie sprites met transparante achtergrond — perfect om als Actor te gebruiken! Kopieer wat je nodig hebt naar de images/ map van jouw starter.

Kies een starter en bouw je eigen spel! Hier zijn een paar ideeën om je op weg te helpen:

Tactisch gevecht met zombie stats

Begin met Starter C (JSON Data). De zombies hebben al stats (snelheid, slimheid, kracht). Gebruik die stats om te bepalen welke actie werkt:

  • Snelle zombie? → Rennen werkt niet!
  • Slimme zombie? → Verstoppen werkt niet!
  • Sterke zombie? → Vechten zonder wapen werkt niet!

Bekijk de Brainstorm voor voorbeeldcode die laat zien hoe je kansen berekent op basis van stats.

Meerdere locaties ontdekken

Begin met Starter B (Locaties). Voeg zombie-ontmoetingen toe op elke locatie. Misschien heeft het ziekenhuis andere zombies dan het park?

Bekijk de Zombiepedia om te kiezen welke zombies waar verschijnen.

Combineer alles

Wil je het ultieme spel? Combineer de ideeën!

  1. Gebruik Starter A (Enums) voor nette code
  2. Voeg locaties toe uit Starter B
  3. Laad zombie data uit een JSON bestand zoals in Starter C
  4. Gebruik de Brainstorm voor tactisch gevecht
  5. Kies je zombies uit de Zombiepedia

Hulp nodig?

  • Brainstorm — de uitdaging, spelregels en voorbeeldcode
  • Zombiepedia — alle zombies met stats en zwaktes
  • Pygame Recepten — handige patronen voor animaties, meerdere sprites en meer