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
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
- Update de positie – Verander waar je object moet zijn (bijvoorbeeld 5 pixels naar rechts bewegen)
- Wis het oude frame – Maak het scherm leeg zodat je geen spookachtige sporen ziet
- 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, bijvoorbeeld37, wat overeenkomt metArrowLeft
✅ 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
keydownenkeyupgebeurtenissen - ✅ 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:
keystrings versuskeyCodenummers - 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
-
Voeg toegewijde objecten toe voor
hero,enemyengame object, ze moetenxenyeigenschappen hebben. (Herinner je het gedeelte over Inheritance or composition).TIP
game objectmoet degene zijn metxenyen de mogelijkheid om zichzelf op een canvas te tekenen.Tip: Begin met het toevoegen van een nieuwe
GameObjectklasse 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
deadvlag 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
GameObjectuit om deHeroenEnemyte 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
GameObjectmet behulp van hetextendssleutelwoord - 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()
-
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
onKeyDownfunctie 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.
-
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:
-
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-
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); } -
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
-
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; });
-
-
Stel de game loop in
Herstructureer de
window.onloadfunctie 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
-
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
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
setIntervalom 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
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.