You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Web-Dev-For-Beginners/translations/nl/6-space-game/3-moving-elements-around/README.md

37 KiB

Bouw een Ruimtespel Deel 3: Beweging Toevoegen

journey
    title Your Game Animation Journey
    section Movement Basics
      Understand motion principles: 3: Student
      Learn coordinate updates: 4: Student
      Implement basic movement: 4: Student
    section Player Controls
      Handle keyboard events: 4: Student
      Prevent default behaviors: 5: Student
      Create responsive controls: 5: Student
    section Game Systems
      Build game loop: 5: Student
      Manage object lifecycle: 5: Student
      Implement pub/sub pattern: 5: Student

Denk aan je favoriete games wat ze boeiend maakt, is niet alleen de mooie graphics, maar ook hoe alles beweegt en reageert op jouw acties. Op dit moment is je ruimtespel als een prachtig schilderij, maar we gaan beweging toevoegen om het tot leven te brengen.

Toen NASA-ingenieurs de stuurcomputer voor de Apollo-missies programmeerden, stonden ze voor een vergelijkbare uitdaging: hoe zorg je ervoor dat een ruimtevaartuig reageert op pilootinvoer terwijl het automatisch koerscorrecties uitvoert? De principes die we vandaag leren, weerspiegelen diezelfde concepten het beheren van door spelers gecontroleerde bewegingen naast automatische systeemgedragingen.

In deze les leer je hoe je ruimteschepen soepel over het scherm laat glijden, laat reageren op spelerscommando's en vloeiende bewegingspatronen creëert. We splitsen alles op in beheersbare concepten die logisch op elkaar voortbouwen.

Aan het einde kunnen spelers hun heldenschip over het scherm laten vliegen terwijl vijandelijke schepen boven hun hoofd patrouilleren. Nog belangrijker, je begrijpt de kernprincipes die bewegingssystemen in games aandrijven.

mindmap
  root((Game Animation))
    Movement Types
      Player Controlled
      Automatic Motion
      Physics Based
      Scripted Paths
    Event Handling
      Keyboard Input
      Mouse Events
      Touch Controls
      Default Prevention
    Game Loop
      Update Logic
      Render Frame
      Clear Canvas
      Frame Rate Control
    Object Management
      Position Updates
      Collision Detection
      Lifecycle Management
      State Tracking
    Communication
      Pub/Sub Pattern
      Event Emitters
      Message Passing
      Loose Coupling

Pre-Lecture Quiz

Pre-lecture quiz

Beweging in Games Begrijpen

Games komen tot leven wanneer dingen beginnen te bewegen, en er zijn fundamenteel twee manieren waarop dit gebeurt:

  • Door spelers gecontroleerde beweging: Wanneer je een toets indrukt of met je muis klikt, beweegt er iets. Dit is de directe verbinding tussen jou en de gamewereld.
  • Automatische beweging: Wanneer de game zelf besluit dingen te bewegen zoals die vijandelijke schepen die over het scherm moeten patrouilleren, ongeacht wat jij doet.

Objecten op een computerscherm laten bewegen is eenvoudiger dan je denkt. Herinner je die x- en y-coördinaten uit de wiskundeles? Dat is precies waar we hier mee werken. Toen Galileo in 1610 de manen van Jupiter volgde, deed hij in wezen hetzelfde posities over tijd plotten om bewegingspatronen te begrijpen.

Dingen op het scherm bewegen is als het maken van een flipboekanimatie je moet deze drie eenvoudige stappen volgen:

flowchart LR
    A["Frame N"] --> B["Update Positions"]
    B --> C["Clear Canvas"]
    C --> D["Draw Objects"]
    D --> E["Frame N+1"]
    E --> F{Continue?}
    F -->|Yes| B
    F -->|No| G["Game Over"]
    
    subgraph "Animation Cycle"
        H["1. Calculate new positions"]
        I["2. Erase previous frame"]
        J["3. Render new frame"]
    end
    
    style B fill:#e1f5fe
    style C fill:#ffebee
    style D fill:#e8f5e8
  1. Update de positie Verander waar je object moet zijn (bijvoorbeeld 5 pixels naar rechts bewegen)
  2. Wis het oude frame Maak het scherm leeg zodat je geen spookachtige sporen ziet
  3. Teken het nieuwe frame Plaats je object op zijn nieuwe plek

Doe dit snel genoeg, en voilà! Je hebt vloeiende beweging die natuurlijk aanvoelt voor spelers.

Zo kan het eruitzien in code:

// Set the hero's location
hero.x += 5;
// Clear the rectangle that hosts the hero
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Redraw the game background and hero
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.drawImage(heroImg, hero.x, hero.y);

Wat deze code doet:

  • Update de x-coördinaat van de held met 5 pixels om horizontaal te bewegen
  • Wist het hele canvasgebied om het vorige frame te verwijderen
  • Vult het canvas met een zwarte achtergrondkleur
  • Tekent de afbeelding van de held op zijn nieuwe positie

Kun je een reden bedenken waarom het herhaaldelijk opnieuw tekenen van je held meerdere frames per seconde prestatiekosten kan veroorzaken? Lees meer over alternatieven voor dit patroon.

Toetsenbordgebeurtenissen afhandelen

Hier verbinden we spelersinvoer met acties in de game. Wanneer iemand op de spatiebalk drukt om een laser af te vuren of een pijltoets indrukt om een asteroïde te ontwijken, moet je game die invoer detecteren en erop reageren.

Toetsenbordgebeurtenissen vinden plaats op het niveau van het venster, wat betekent dat je hele browservenster luistert naar die toetsaanslagen. Muisclicks daarentegen kunnen worden gekoppeld aan specifieke elementen (zoals het klikken op een knop). Voor ons ruimtespel richten we ons op toetsenbordbediening, omdat dat spelers dat klassieke arcadegevoel geeft.

Dit doet me denken aan hoe telegraafoperators in de 19e eeuw morsecode-invoer moesten vertalen naar betekenisvolle berichten we doen iets soortgelijks, toetsaanslagen vertalen naar gamecommando's.

Om een gebeurtenis af te handelen, moet je de addEventListener()-methode van het venster gebruiken en deze voorzien van twee invoerparameters. De eerste parameter is de naam van de gebeurtenis, bijvoorbeeld keyup. De tweede parameter is de functie die moet worden uitgevoerd als gevolg van de gebeurtenis.

Hier is een voorbeeld:

window.addEventListener('keyup', (evt) => {
  // evt.key = string representation of the key
  if (evt.key === 'ArrowUp') {
    // do something
  }
});

Wat hier gebeurt:

  • Luistert naar toetsenbordgebeurtenissen op het hele venster
  • Vangt het gebeurtenisobject dat informatie bevat over welke toets is ingedrukt
  • Controleert of de ingedrukte toets overeenkomt met een specifieke toets (in dit geval de pijl omhoog)
  • Voert code uit wanneer aan de voorwaarde wordt voldaan

Voor toetsgebeurtenissen zijn er twee eigenschappen op het gebeurtenisobject die je kunt gebruiken om te zien welke toets is ingedrukt:

  • key - dit is een stringrepresentatie van de ingedrukte toets, bijvoorbeeld 'ArrowUp'
  • keyCode - dit is een numerieke representatie, bijvoorbeeld 37, wat overeenkomt met ArrowLeft

Manipulatie van toetsgebeurtenissen is nuttig buiten gameontwikkeling. Kun je andere toepassingen bedenken voor deze techniek?

sequenceDiagram
    participant User
    participant Browser
    participant EventSystem
    participant GameLogic
    participant Hero
    
    User->>Browser: Presses ArrowUp key
    Browser->>EventSystem: keydown event
    EventSystem->>EventSystem: preventDefault()
    EventSystem->>GameLogic: emit('KEY_EVENT_UP')
    GameLogic->>Hero: hero.y -= 5
    Hero->>Hero: Update position
    
    Note over Browser,GameLogic: Event flow prevents browser defaults
    Note over GameLogic,Hero: Pub/sub pattern enables clean communication

Speciale toetsen: een waarschuwing!

Sommige toetsen hebben ingebouwde browsergedragingen die je game kunnen verstoren. Pijltoetsen scrollen de pagina en de spatiebalk springt naar beneden gedragingen die je niet wilt wanneer iemand probeert zijn ruimteschip te besturen.

We kunnen deze standaardgedragingen voorkomen en onze game de invoer laten afhandelen. Dit is vergelijkbaar met hoe vroege computerprogrammeurs systeemonderbrekingen moesten overschrijven om aangepaste gedragingen te creëren we doen dit alleen op browserniveau. Zo werkt het:

const onKeyDown = function (e) {
  console.log(e.keyCode);
  switch (e.keyCode) {
    case 37:
    case 39:
    case 38:
    case 40: // Arrow keys
    case 32:
      e.preventDefault();
      break; // Space
    default:
      break; // do not block other keys
  }
};

window.addEventListener('keydown', onKeyDown);

Wat deze preventiecode doet:

  • Controleert op specifieke toetscodes die ongewenst browsergedrag kunnen veroorzaken
  • Voorkomt de standaard browseractie voor pijltoetsen en spatiebalk
  • Laat andere toetsen normaal functioneren
  • Gebruikt e.preventDefault() om het ingebouwde gedrag van de browser te stoppen

🔄 Pedagogische Check-in

Begrip van gebeurtenisafhandeling: Voordat je doorgaat naar automatische beweging, zorg ervoor dat je:

  • Het verschil kunt uitleggen tussen keydown en keyup gebeurtenissen
  • Begrijpt waarom we standaard browsergedragingen voorkomen
  • Kunt beschrijven hoe gebeurtenislisteners gebruikersinvoer verbinden met gamelogica
  • Kunt identificeren welke toetsen mogelijk interfereren met gamebediening

Snelle zelftest: Wat zou er gebeuren als je het standaardgedrag voor pijltoetsen niet voorkomt? Antwoord: De browser zou de pagina scrollen, wat de beweging in de game verstoort

Architectuur van het gebeurtenissysteem: Je begrijpt nu:

  • Luisteren op vensterniveau: Gebeurtenissen vastleggen op browserniveau
  • Eigenschappen van het gebeurtenisobject: key strings versus keyCode nummers
  • Voorkomen van standaardgedrag: Ongewenste browsergedragingen stoppen
  • Conditionele logica: Reageren op specifieke toetscombinaties

Door de game geïnduceerde beweging

Laten we nu praten over objecten die bewegen zonder spelersinvoer. Denk aan vijandelijke schepen die over het scherm cruisen, kogels die in rechte lijnen vliegen, of wolken die op de achtergrond drijven. Deze autonome beweging maakt je gamewereld levendig, zelfs wanneer niemand de bediening aanraakt.

We gebruiken de ingebouwde timers van JavaScript om posities op regelmatige intervallen bij te werken. Dit concept is vergelijkbaar met hoe slingerklokken werken een regelmatig mechanisme dat consistente, getimede acties triggert. Zo eenvoudig kan het zijn:

const id = setInterval(() => {
  // Move the enemy on the y axis
  enemy.y += 10;
}, 100);

Wat deze bewegingscode doet:

  • Creëert een timer die elke 100 milliseconden draait
  • Update de y-coördinaat van de vijand met 10 pixels elke keer
  • Slaat het interval-ID op zodat we het later kunnen stoppen indien nodig
  • Beweegt de vijand automatisch naar beneden op het scherm

De gameloop

Hier is het concept dat alles samenbrengt de gameloop. Als je game een film was, zou de gameloop de filmprojector zijn, die frame na frame laat zien, zo snel dat alles soepel lijkt te bewegen.

Elke game heeft een van deze loops die op de achtergrond draait. Het is een functie die alle gameobjecten bijwerkt, het scherm opnieuw tekent en dit proces continu herhaalt. Dit houdt je held, alle vijanden, eventuele lasers die rondvliegen de hele gamestatus bij.

Dit concept doet me denken aan hoe vroege filmanimators zoals Walt Disney personages frame voor frame moesten tekenen om de illusie van beweging te creëren. Wij doen hetzelfde, alleen met code in plaats van potloden.

Zo kan een gameloop er in code uitzien:

flowchart TD
    A["Start Game Loop"] --> B["Clear Canvas"]
    B --> C["Fill Background"]
    C --> D["Update Game Objects"]
    D --> E["Draw Hero"]
    E --> F["Draw Enemies"]
    F --> G["Draw UI Elements"]
    G --> H["Wait for Next Frame"]
    H --> I{Game Running?}
    I -->|Yes| B
    I -->|No| J["End Game"]
    
    subgraph "Frame Rate Control"
        K["60 FPS = 16.67ms"]
        L["30 FPS = 33.33ms"]
        M["10 FPS = 100ms"]
    end
    
    style B fill:#ffebee
    style D fill:#e1f5fe
    style E fill:#e8f5e8
    style F fill:#e8f5e8
const gameLoopId = setInterval(() => {
  function gameLoop() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = "black";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    drawHero();
    drawEnemies();
    drawStaticObjects();
  }
  gameLoop();
}, 200);

Begrip van de structuur van de gameloop:

  • Wist het hele canvas om het vorige frame te verwijderen
  • Vult de achtergrond met een effen kleur
  • Tekent alle gameobjecten op hun huidige posities
  • Herhaalt dit proces elke 200 milliseconden om vloeiende animatie te creëren
  • Beheert de framerate door de intervaltijd te regelen

Verder met het Ruimtespel

Nu gaan we beweging toevoegen aan de statische scène die je eerder hebt gebouwd. We gaan het transformeren van een screenshot naar een interactieve ervaring. We werken dit stap voor stap uit om ervoor te zorgen dat elk onderdeel voortbouwt op het vorige.

Pak de code van waar we in de vorige les zijn gebleven (of begin met de code in de Part II- starter map als je een frisse start nodig hebt).

Wat we vandaag gaan bouwen:

  • Heldbediening: Pijltoetsen zullen je ruimteschip over het scherm sturen
  • Vijandelijke beweging: Die buitenaardse schepen zullen hun aanval beginnen

Laten we beginnen met het implementeren van deze functies.

Aanbevolen stappen

Vind de bestanden die voor je zijn aangemaakt in de your-work submap. Het zou het volgende moeten bevatten:

-| assets
  -| enemyShip.png
  -| player.png
-| index.html
-| app.js
-| package.json

Je start je project in de your-work map door te typen:

cd your-work
npm start

Wat dit commando doet:

  • Navigeert naar je projectmap
  • Start een HTTP-server op adres http://localhost:5000
  • Serveert je gamebestanden zodat je ze in een browser kunt testen

Het bovenstaande start een HTTP-server op adres http://localhost:5000. Open een browser en voer dat adres in, op dit moment zou het de held en alle vijanden moeten weergeven; niets beweegt nog niet!

Code toevoegen

  1. Voeg toegewijde objecten toe voor hero, enemy en game object, ze moeten x en y eigenschappen hebben. (Herinner je het gedeelte over Inheritance or composition).

    TIP game object moet degene zijn met x en y en de mogelijkheid om zichzelf op een canvas te tekenen.

    Tip: Begin met het toevoegen van een nieuwe GameObject klasse met zijn constructor zoals hieronder beschreven, en teken het vervolgens op het canvas:

    class GameObject {
      constructor(x, y) {
        this.x = x;
        this.y = y;
        this.dead = false;
        this.type = "";
        this.width = 0;
        this.height = 0;
        this.img = undefined;
      }
    
      draw(ctx) {
        ctx.drawImage(this.img, this.x, this.y, this.width, this.height);
      }
    }
    

    Begrip van deze basisklasse:

    • Definieert gemeenschappelijke eigenschappen die alle gameobjecten delen (positie, grootte, afbeelding)
    • Bevat een dead vlag om bij te houden of het object moet worden verwijderd
    • Biedt een draw() methode die het object op het canvas weergeeft
    • Stelt standaardwaarden in voor alle eigenschappen die kindklassen kunnen overschrijven
    classDiagram
        class GameObject {
            +x: number
            +y: number
            +dead: boolean
            +type: string
            +width: number
            +height: number
            +img: Image
            +draw(ctx)
        }
    
        class Hero {
            +speed: number
            +type: "Hero"
            +width: 98
            +height: 75
        }
    
        class Enemy {
            +type: "Enemy"
            +width: 98
            +height: 50
            +setInterval()
        }
    
        GameObject <|-- Hero
        GameObject <|-- Enemy
    
        class EventEmitter {
            +listeners: object
            +on(message, listener)
            +emit(message, payload)
        }
    

    Breid nu deze GameObject uit om de Hero en Enemy te maken:

    class Hero extends GameObject {
      constructor(x, y) {
        super(x, y);
        this.width = 98;
        this.height = 75;
        this.type = "Hero";
        this.speed = 5;
      }
    }
    
    class Enemy extends GameObject {
      constructor(x, y) {
        super(x, y);
        this.width = 98;
        this.height = 50;
        this.type = "Enemy";
        const id = setInterval(() => {
          if (this.y < canvas.height - this.height) {
            this.y += 5;
          } else {
            console.log('Stopped at', this.y);
            clearInterval(id);
          }
        }, 300);
      }
    }
    

    Belangrijke concepten in deze klassen:

    • Erft van GameObject met behulp van het extends sleutelwoord
    • Roept de ouderconstructor aan met super(x, y)
    • Stelt specifieke afmetingen en eigenschappen in voor elk type object
    • Implementeert automatische beweging voor vijanden met behulp van setInterval()
  2. Voeg toetsgebeurtenis-handlers toe om toetsnavigatie af te handelen (beweeg de held omhoog/omlaag links/rechts)

    ONTHOUD het is een cartesiaans systeem, linksboven is 0,0. Vergeet ook niet code toe te voegen om standaardgedrag te stoppen.

    Tip: Maak je onKeyDown functie en koppel deze aan het venster:

    const onKeyDown = function (e) {
      console.log(e.keyCode);
      // Add the code from the lesson above to stop default behavior
      switch (e.keyCode) {
        case 37:
        case 39:
        case 38:
        case 40: // Arrow keys
        case 32:
          e.preventDefault();
          break; // Space
        default:
          break; // do not block other keys
      }
    };
    
    window.addEventListener("keydown", onKeyDown);
    

    Wat deze gebeurtenishandler doet:

    • Luistert naar keydown-gebeurtenissen op het hele venster
    • Logt de toetscode om je te helpen debuggen welke toetsen worden ingedrukt
    • Voorkomt standaard browsergedrag voor pijltoetsen en spatiebalk
    • Laat andere toetsen normaal functioneren

    Controleer op dit punt je browserconsole en bekijk de geregistreerde toetsaanslagen.

  3. Implementeer het Pub sub pattern, dit houdt je code schoon terwijl je de resterende delen volgt.

    Het Publish-Subscribe patroon helpt je code te organiseren door gebeurtenisdetectie te scheiden van gebeurtenisafhandeling. Dit maakt je code meer modulair en gemakkelijker te onderhouden.

    Om dit laatste deel te doen, kun je:

    1. Voeg een gebeurtenislistener toe aan het venster:

      window.addEventListener("keyup", (evt) => {
        if (evt.key === "ArrowUp") {
          eventEmitter.emit(Messages.KEY_EVENT_UP);
        } else if (evt.key === "ArrowDown") {
          eventEmitter.emit(Messages.KEY_EVENT_DOWN);
        } else if (evt.key === "ArrowLeft") {
          eventEmitter.emit(Messages.KEY_EVENT_LEFT);
        } else if (evt.key === "ArrowRight") {
          eventEmitter.emit(Messages.KEY_EVENT_RIGHT);
        }
      });
      

    Wat dit gebeurtenissysteem doet:

    • Detecteert toetsenbordinvoer en zet dit om in aangepaste gamegebeurtenissen
    • Scheidt invoerdetectie van gamelogica
    • Maakt het gemakkelijk om later bedieningselementen te wijzigen zonder de gamecode te beïnvloeden
    • Staat toe dat meerdere systemen reageren op dezelfde invoer
    flowchart TD
        A["Keyboard Input"] --> B["Window Event Listener"]
        B --> C["Event Emitter"]
        C --> D["KEY_EVENT_UP"]
        C --> E["KEY_EVENT_DOWN"]
        C --> F["KEY_EVENT_LEFT"]
        C --> G["KEY_EVENT_RIGHT"]
    
        D --> H["Hero Movement"]
        D --> I["Sound System"]
        D --> J["Visual Effects"]
    
        E --> H
        F --> H
        G --> H
    
        style A fill:#e1f5fe
        style C fill:#e8f5e8
        style H fill:#fff3e0
    
    1. Maak een EventEmitter klasse om berichten te publiceren en erop te abonneren:

      class EventEmitter {
        constructor() {
          this.listeners = {};
        }
      
        on(message, listener) {
          if (!this.listeners[message]) {
            this.listeners[message] = [];
          }
          this.listeners[message].push(listener);
        }
      
      
    2. Voeg constanten toe en stel de EventEmitter in:

      const Messages = {
        KEY_EVENT_UP: "KEY_EVENT_UP",
        KEY_EVENT_DOWN: "KEY_EVENT_DOWN",
        KEY_EVENT_LEFT: "KEY_EVENT_LEFT",
        KEY_EVENT_RIGHT: "KEY_EVENT_RIGHT",
      };
      
      let heroImg, 
          enemyImg, 
          laserImg,
          canvas, ctx, 
          gameObjects = [], 
          hero, 
          eventEmitter = new EventEmitter();
      

    Begrip van de setup:

    • Definieert berichtconstanten om typfouten te vermijden en refactoring te vergemakkelijken
    • Declareert variabelen voor afbeeldingen, canvascontext en gamestatus
    • Creëert een globale event emitter voor het pub-sub systeem
    • Initialiseert een array om alle game-objecten op te slaan
    1. Initialiseer het spel

      function initGame() {
        gameObjects = [];
        createEnemies();
        createHero();
      
        eventEmitter.on(Messages.KEY_EVENT_UP, () => {
          hero.y -= 5;
        });
      
        eventEmitter.on(Messages.KEY_EVENT_DOWN, () => {
          hero.y += 5;
        });
      
        eventEmitter.on(Messages.KEY_EVENT_LEFT, () => {
          hero.x -= 5;
        });
      
      
  4. Stel de game loop in

    Herstructureer de window.onload functie om het spel te initialiseren en een game loop op een goed interval in te stellen. Je voegt ook een laserstraal toe:

    window.onload = async () => {
      canvas = document.getElementById("canvas");
      ctx = canvas.getContext("2d");
      heroImg = await loadTexture("assets/player.png");
      enemyImg = await loadTexture("assets/enemyShip.png");
      laserImg = await loadTexture("assets/laserRed.png");
    
      initGame();
      const gameLoopId = setInterval(() => {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = "black";
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        drawGameObjects(ctx);
      }, 100);
    };
    

    Begrijpen van de game setup:

    • Wacht tot de pagina volledig is geladen voordat het spel start
    • Haalt het canvas-element en de 2D-rendering context op
    • Laadt alle afbeeldingsassets asynchroon met behulp van await
    • Start de game loop met intervallen van 100ms (10 FPS)
    • Leegt en tekent het hele scherm opnieuw bij elke frame
  5. Voeg code toe om vijanden op een bepaald interval te laten bewegen

    Herstructureer de createEnemies() functie om de vijanden te creëren en ze toe te voegen aan de nieuwe gameObjects klasse:

    function createEnemies() {
      const MONSTER_TOTAL = 5;
      const MONSTER_WIDTH = MONSTER_TOTAL * 98;
      const START_X = (canvas.width - MONSTER_WIDTH) / 2;
      const STOP_X = START_X + MONSTER_WIDTH;
    
      for (let x = START_X; x < STOP_X; x += 98) {
        for (let y = 0; y < 50 * 5; y += 50) {
          const enemy = new Enemy(x, y);
          enemy.img = enemyImg;
          gameObjects.push(enemy);
        }
      }
    }
    

    Wat de vijandcreatie doet:

    • Bereken posities om vijanden in het midden van het scherm te plaatsen
    • Creëert een raster van vijanden met behulp van geneste loops
    • Wijst de vijandafbeelding toe aan elk vijandobject
    • Voegt elke vijand toe aan de globale game-objecten array

    en voeg een createHero() functie toe om een soortgelijk proces voor de held uit te voeren.

    function createHero() {
      hero = new Hero(
        canvas.width / 2 - 45,
        canvas.height - canvas.height / 4
      );
      hero.img = heroImg;
      gameObjects.push(hero);
    }
    

    Wat de heldcreatie doet:

    • Positioneert de held onderaan in het midden van het scherm
    • Wijst de heldafbeelding toe aan het heldobject
    • Voegt de held toe aan de game-objecten array voor rendering

    en tot slot, voeg een drawGameObjects() functie toe om te beginnen met tekenen:

    function drawGameObjects(ctx) {
      gameObjects.forEach(go => go.draw(ctx));
    }
    

    Begrijpen van de tekenfunctie:

    • Itereert door alle game-objecten in de array
    • Roept de draw() methode aan op elk object
    • Geeft de canvas context door zodat objecten zichzelf kunnen renderen

    🔄 Pedagogische Check-in

    Volledig begrip van het gamesysteem: Controleer je beheersing van de hele architectuur:

    • Hoe maakt overerving het mogelijk dat Hero en Enemy gemeenschappelijke GameObject-eigenschappen delen?
    • Waarom maakt het pub/sub patroon je code beter onderhoudbaar?
    • Welke rol speelt de game loop bij het creëren van vloeiende animatie?
    • Hoe verbinden event listeners gebruikersinvoer met het gedrag van game-objecten?

    Systeemintegratie: Je spel demonstreert nu:

    • Objectgeoriënteerd ontwerp: Basisklassen met gespecialiseerde overerving
    • Event-gedreven architectuur: Pub/sub patroon voor losse koppeling
    • Animatieframework: Game loop met consistente frame-updates
    • Invoerbeheer: Toetsenbordgebeurtenissen met standaardpreventie
    • Assetbeheer: Afbeeldingsladen en sprite-rendering

    Professionele patronen: Je hebt geïmplementeerd:

    • Scheiding van verantwoordelijkheden: Invoer, logica en rendering gescheiden
    • Polymorfisme: Alle game-objecten delen een gemeenschappelijke tekeninterface
    • Berichtpassing: Schone communicatie tussen componenten
    • Resourcebeheer: Efficiënt sprite- en animatiebeheer

    Je vijanden zouden nu moeten beginnen met het aanvallen van je heldenruimteschip! } }

    
    and add a `createHero()` function to do a similar process for the hero.
    
    ```javascript
    function createHero() {
      hero = new Hero(
        canvas.width / 2 - 45,
        canvas.height - canvas.height / 4
      );
      hero.img = heroImg;
      gameObjects.push(hero);
    }
    

    en tot slot, voeg een drawGameObjects() functie toe om te beginnen met tekenen:

    function drawGameObjects(ctx) {
      gameObjects.forEach(go => go.draw(ctx));
    }
    

    Je vijanden zouden nu moeten beginnen met het aanvallen van je heldenruimteschip!


GitHub Copilot Agent Challenge 🚀

Hier is een uitdaging die de afwerking van je spel zal verbeteren: het toevoegen van grenzen en vloeiende besturing. Op dit moment kan je held van het scherm vliegen en voelt de beweging misschien schokkerig aan.

Jouw missie: Laat je ruimteschip realistischer aanvoelen door schermgrenzen en vloeiende beweging te implementeren. Dit lijkt op hoe NASA's vluchtcontrolesystemen voorkomen dat ruimtevaartuigen veilige operationele parameters overschrijden.

Wat je moet bouwen: Maak een systeem dat je heldenruimteschip op het scherm houdt en zorg dat de besturing soepel aanvoelt. Wanneer spelers een pijltjestoets ingedrukt houden, moet het schip continu glijden in plaats van in discrete stappen bewegen. Overweeg visuele feedback toe te voegen wanneer het schip de schermgrenzen bereikt misschien een subtiel effect om de rand van het speelgebied aan te geven.

Meer informatie over agent mode vind je hier.

🚀 Uitdaging

Codeorganisatie wordt steeds belangrijker naarmate projecten groeien. Je hebt misschien gemerkt dat je bestand vol raakt met functies, variabelen en klassen die allemaal door elkaar staan. Dit doet me denken aan hoe de ingenieurs die de Apollo-missie code organiseerden duidelijke, onderhoudbare systemen moesten creëren waar meerdere teams tegelijkertijd aan konden werken.

Jouw missie: Denk als een softwarearchitect. Hoe zou je je code organiseren zodat je (of een teamgenoot) over zes maanden begrijpt wat er gebeurt? Zelfs als alles voorlopig in één bestand blijft, kun je een betere organisatie creëren:

  • Groeperen van gerelateerde functies met duidelijke commentaarheaders
  • Scheiding van verantwoordelijkheden - houd game-logica gescheiden van rendering
  • Consistente naamgeving gebruiken voor variabelen en functies
  • Modules of namespaces maken om verschillende aspecten van je spel te organiseren
  • Documentatie toevoegen die het doel van elke hoofdsectie uitlegt

Reflectievragen:

  • Welke delen van je code zijn het moeilijkst te begrijpen als je er later op terugkomt?
  • Hoe kun je je code organiseren om het gemakkelijker te maken voor iemand anders om bij te dragen?
  • Wat zou er gebeuren als je nieuwe functies zoals power-ups of verschillende soorten vijanden wilt toevoegen?

Quiz na de les

Quiz na de les

Review & Zelfstudie

We hebben alles vanaf nul opgebouwd, wat geweldig is om te leren, maar hier is een klein geheim er zijn enkele geweldige JavaScript-frameworks die veel van het zware werk voor je kunnen doen. Zodra je je comfortabel voelt met de basis die we hebben behandeld, is het de moeite waard om te ontdekken wat er beschikbaar is.

Zie frameworks als een goed gevulde gereedschapskist in plaats van elk gereedschap met de hand te maken. Ze kunnen veel van die uitdagingen op het gebied van codeorganisatie oplossen waar we het over hadden, en bieden bovendien functies die weken zouden kosten om zelf te bouwen.

Dingen die de moeite waard zijn om te verkennen:

  • Hoe game-engines code organiseren je zult versteld staan van de slimme patronen die ze gebruiken
  • Prestatie-trucs om canvasgames soepel te laten draaien
  • Moderne JavaScript-functies die je code schoner en beter onderhoudbaar maken
  • Verschillende benaderingen om game-objecten en hun relaties te beheren

🎯 Jouw tijdlijn voor meesterschap in game-animatie

timeline
    title Game Animation & Interaction Learning Progression
    
    section Movement Fundamentals (20 minutes)
        Animation Principles: Frame-based animation
                            : Position updates
                            : Coordinate systems
                            : Smooth movement
        
    section Event Systems (25 minutes)
        User Input: Keyboard event handling
                  : Default behavior prevention
                  : Event object properties
                  : Window-level listening
        
    section Game Architecture (30 minutes)
        Object Design: Inheritance patterns
                     : Base class creation
                     : Specialized behaviors
                     : Polymorphic interfaces
        
    section Communication Patterns (35 minutes)
        Pub/Sub Implementation: Event emitters
                              : Message constants
                              : Loose coupling
                              : System integration
        
    section Game Loop Mastery (40 minutes)
        Real-time Systems: Frame rate control
                         : Update/render cycle
                         : State management
                         : Performance optimization
        
    section Advanced Techniques (45 minutes)
        Professional Features: Collision detection
                             : Physics simulation
                             : State machines
                             : Component systems
        
    section Game Engine Concepts (1 week)
        Framework Understanding: Entity-component systems
                               : Scene graphs
                               : Asset pipelines
                               : Performance profiling
        
    section Production Skills (1 month)
        Professional Development: Code organization
                                : Team collaboration
                                : Testing strategies
                                : Deployment optimization

🛠️ Samenvatting van je game-ontwikkelingsgereedschapskist

Na het voltooien van deze les, heb je nu beheerst:

  • Animatieprincipes: Beweging op basis van frames en vloeiende overgangen
  • Event-gedreven programmeren: Toetsenbordinvoerbeheer met correcte eventafhandeling
  • Objectgeoriënteerd ontwerp: Overervingshiërarchieën en polymorfe interfaces
  • Communicatiepatronen: Pub/sub architectuur voor onderhoudbare code
  • Game loop architectuur: Real-time update- en rendercycli
  • Invoersystemen: Gebruikerscontrole mapping met standaardgedragspreventie
  • Assetbeheer: Sprite laden en efficiënte rendertechnieken

Wat je in de komende 5 minuten kunt doen

  • Open de browserconsole en probeer addEventListener('keydown', console.log) om toetsenbordgebeurtenissen te zien
  • Maak een eenvoudig div-element en beweeg het rond met pijltjestoetsen
  • Experimenteer met setInterval om continue beweging te creëren
  • Probeer standaardgedrag te voorkomen met event.preventDefault()

🎯 Wat je in dit uur kunt bereiken

  • Voltooi de quiz na de les en begrijp event-gedreven programmeren
  • Bouw het bewegende heldenruimteschip met volledige toetsenbordbesturing
  • Implementeer vloeiende bewegingspatronen voor vijanden
  • Voeg grenzen toe om te voorkomen dat game-objecten het scherm verlaten
  • Maak basisbotsingsdetectie tussen game-objecten

📅 Jouw weeklange animatiereis

  • Voltooi het volledige ruimtespel met gepolijste beweging en interacties
  • Voeg geavanceerde bewegingspatronen toe zoals bochten, versnelling en fysica
  • Implementeer vloeiende overgangen en easing functies
  • Maak deeltjeseffecten en visuele feedbacksystemen
  • Optimaliseer de gameprestaties voor vloeiende 60fps gameplay
  • Voeg mobiele touch-besturing en responsief ontwerp toe

🌟 Jouw maandlange interactieve ontwikkeling

  • Bouw complexe interactieve applicaties met geavanceerde animatiesystemen
  • Leer animatielibraries zoals GSAP of maak je eigen animatie-engine
  • Draag bij aan open source game-ontwikkeling en animatieprojecten
  • Beheers prestatieoptimalisatie voor grafisch intensieve applicaties
  • Maak educatieve content over game-ontwikkeling en animatie
  • Bouw een portfolio met geavanceerde interactieve programmeervaardigheden

Toepassingen in de echte wereld: Je game-animatievaardigheden zijn direct toepasbaar op:

  • Interactieve webapplicaties: Dynamische dashboards en real-time interfaces
  • Datavisualisatie: Geanimeerde grafieken en interactieve graphics
  • Educatieve software: Interactieve simulaties en leertools
  • Mobiele ontwikkeling: Touch-gebaseerde games en gebarenbeheer
  • Desktopapplicaties: Electron-apps met vloeiende animaties
  • Webanimaties: CSS- en JavaScript-animatielibraries

Verworven professionele vaardigheden: Je kunt nu:

  • Architectuur ontwerpen voor event-gedreven systemen die schaalbaar zijn
  • Vloeiende animaties implementeren met wiskundige principes
  • Complexe interactiesystemen debuggen met browserontwikkelaarstools
  • Gameprestaties optimaliseren voor verschillende apparaten en browsers
  • Onderhoudbare code structuren ontwerpen met bewezen patronen

Beheerde game-ontwikkelingsconcepten:

  • Frame rate management: Begrip van FPS en tijdsbeheer
  • Invoerbeheer: Cross-platform toetsenbord- en eventsystemen
  • Objectlevenscyclus: Patronen voor creatie, update en vernietiging
  • Statussynchronisatie: Consistente gamestatus tussen frames
  • Eventarchitectuur: Losgekoppelde communicatie tussen gamesystemen

Volgende niveau: Je bent klaar om botsingsdetectie, scoresystemen, geluidseffecten toe te voegen of moderne game-frameworks zoals Phaser of Three.js te verkennen!

🌟 Prestatie ontgrendeld: Je hebt een compleet interactief gamesysteem gebouwd met professionele architectuurpatronen!

Opdracht

Commentaar op je code


Disclaimer:
Dit document is vertaald met behulp van de AI-vertalingsservice Co-op Translator. Hoewel we streven naar nauwkeurigheid, dient u zich ervan bewust te zijn dat geautomatiseerde vertalingen fouten of onnauwkeurigheden kunnen bevatten. Het originele document in de oorspronkelijke taal moet worden beschouwd als de gezaghebbende bron. Voor kritieke informatie wordt professionele menselijke vertaling aanbevolen. Wij zijn niet aansprakelijk voor misverstanden of verkeerde interpretaties die voortvloeien uit het gebruik van deze vertaling.