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/sv/6-space-game/3-moving-elements-around/README.md

890 lines
36 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Skapa ett rymdspel del 3: Lägga till rörelse
```mermaid
journey
title Din SpelsAnimationsresa
section Rörelsegrunder
Förstå rörelseprinciper: 3: Student
Lära sig koordinatuppdateringar: 4: Student
Implementera grundläggande rörelse: 4: Student
section Spelarkontroller
Hantera tangentbordevenemang: 4: Student
Förhindra standardbeteenden: 5: Student
Skapa responsiva kontroller: 5: Student
section Spelsystem
Bygg spelloop: 5: Student
Hantera objekts livscykel: 5: Student
Implementera pub/sub-mönster: 5: Student
```
Tänk på dina favoritspel det som gör dem fängslande är inte bara snygg grafik, det är sättet allt rör sig och svarar på dina handlingar. Just nu är ditt rymdspel som en vacker målning, men vi är på väg att lägga till rörelse som ger det liv.
När NASAs ingenjörer programmerade styrdatorn för Apollo-uppdragen stod de inför en liknande utmaning: hur får man ett rymdskepp att svara på pilotens input samtidigt som det automatiskt gör kurskorrigeringar? Principerna vi ska lära oss idag ekar samma koncept att hantera spelarstyrd rörelse tillsammans med automatiska systembeteenden.
I denna lektion ska du lära dig hur du får rymdskepp att glida över skärmen, svara på spelarkommandon och skapa mjuka rörelsemönster. Vi delar upp allt i hanterbara koncept som bygger på varandra på ett naturligt sätt.
I slutet kommer spelarna flyga sitt hjälteskepp runt på skärmen medan fientliga skepp patrullerar ovanför. Än viktigare är att du kommer förstå de grundläggande principerna som driver spelsystemen för rörelse.
```mermaid
mindmap
root((Spelanimation))
Movement Types
Spelarkontrollerad
Automatisk Rörelse
Fysikbaserad
Skriptade Vägar
Event Handling
Tangentbordsinmatning
Musehändelser
Pekskärmskontroller
Standardförebyggande
Game Loop
Uppdatera Logik
Rendera Bildruta
Rensa Canvas
Bildrutehastighetskontroll
Object Management
Positionsuppdateringar
Kollisionsdetektion
Livscykelhantering
Statusspårning
Communication
Pub/Sub Mönster
Händelseavsändare
Meddelandeöverföring
Lös Koppling
```
## Förförelsetest
[Förförelsetest](https://ff-quizzes.netlify.app/web/quiz/33)
## Förståelse för spelrörelse
Spel blir levande när saker börjar röra sig, och det finns i grunden två sätt detta händer på:
- **Spelarstyrd rörelse**: När du trycker på en tangent eller klickar med musen, rör sig något. Det är den direkta kopplingen mellan dig och din spelvärld.
- **Automatisk rörelse**: När spelet självt bestämmer att något ska röra sig som de där fientliga skeppen som måste patrullera skärmen oavsett om du gör någonting eller inte.
Att få objekt att röra sig på en datorskärm är enklare än du kanske tror. Kommer du ihåg x- och y-koordinaterna från mattetimmarna? Det är precis det vi jobbar med här. När Galileo 1610 kartlade Jupiters månar gjorde han i princip samma sak plottade positioner över tid för att förstå rörelsemönster.
Att flytta saker på skärmen är som att skapa en bläddra-bok-animation du behöver följa dessa tre enkla steg:
```mermaid
flowchart LR
A["Bildruta N"] --> B["Uppdatera positioner"]
B --> C["Rensa duk"]
C --> D["Rita objekt"]
D --> E["Bildruta N+1"]
E --> F{Fortsätta?}
F -->|Ja| B
F -->|Nej| G["Spelet är slut"]
subgraph "Animationscykel"
H["1. Beräkna nya positioner"]
I["2. Radera föregående bildruta"]
J["3. Rendera ny bildruta"]
end
style B fill:#e1f5fe
style C fill:#ffebee
style D fill:#e8f5e8
```
1. **Uppdatera positionen** Ändra var ditt objekt ska vara (kanske flytta det 5 pixlar åt höger)
2. **Radera den gamla ramen** Rensa skärmen så att du inte ser spöklika spår överallt
3. **Rita den nya ramen** Placera ditt objekt på sin nya plats
Gör du detta tillräckligt snabbt, och pang! Har du mjuk rörelse som känns naturlig för spelarna.
Så här kan det se ut i kod:
```javascript
// Ställ in hjälterens plats
hero.x += 5;
// Rensa rektangeln som rymmer hjälten
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Rita om spelbakgrunden och hjälten
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.drawImage(heroImg, hero.x, hero.y);
```
**Det här gör koden:**
- **Uppdaterar** hjälterymdskeppets x-koordinat med 5 pixlar för att flytta det horisontellt
- **Rensar** hela canvasområdet för att ta bort den tidigare ramen
- **Fyller** canvas med en svart bakgrundsfärg
- **Ritar om** hjälten i sin nya position
✅ Kan du tänka dig en anledning till varför att rita om hjälten många gånger per sekund kan leda till prestandakostnader? Läs om [alternativ till detta mönster](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Optimizing_canvas).
## Hantera tangentbords-händelser
Här kopplar vi spelarinmatning till spelfunktioner. När någon trycker på mellanslag för att skjuta en laser eller trycker på en piltangent för att ducka för en asteroid, måste spelet känna av och svara på den inmatningen.
Tangentbordshändelser inträffar på fönsternivå, vilket betyder att hela din webbläsarfönster lyssnar efter dessa tangenttryckningar. Musklick kan däremot kopplas till specifika element (som att klicka på en knapp). För vårt rymdspel fokuserar vi på tangentbordsstyrning eftersom det ger spelarna den klassiska arkadkänslan.
Det påminner om hur telegrafoperatörer på 1800-talet behövde översätta morsekod till meningsfulla meddelanden vi gör något liknande här, vi översätter tangenttryckningar till spelkommandon.
För att hantera en händelse måste du använda fönstrets `addEventListener()`-metod och ge den två inparametrar. Den första parametern är namnet på händelsen, t.ex. `keyup`. Den andra är funktionen som ska anropas när händelsen inträffar.
Så här ser ett exempel ut:
```javascript
window.addEventListener('keyup', (evt) => {
// evt.key = strängrepresentation av tangenten
if (evt.key === 'ArrowUp') {
// gör något
}
});
```
**Det här händer i koden:**
- **Lyssnar** efter tangentbordshändelser på hela fönstret
- **Fångar** event-objektet som innehåller information om vilken tangent som trycktes
- **Kontrollerar** om den tryckta tangenten matchar en viss tangent (i detta fall pil upp)
- **Utför** kod när villkoret är sant
För tangentbords-händelser finns två egenskaper på event-objektet som du kan använda för att se vilken tangent som trycktes:
- `key` - detta är en strängrepresentation av tryckta tangenten, t.ex. `'ArrowUp'`
- `keyCode` - detta är ett nummer, t.ex. `37`, som motsvarar `ArrowLeft`
✅ Tangentbordsmanipulation är användbart även utanför spelutveckling. Vilka andra användningar kan du tänka dig för denna teknik?
```mermaid
sequenceDiagram
participant User
participant Browser
participant EventSystem
participant GameLogic
participant Hero
User->>Browser: Trycker på pil upp-tangenten
Browser->>EventSystem: keydown-händelse
EventSystem->>EventSystem: preventDefault()
EventSystem->>GameLogic: emit('KEY_EVENT_UP')
GameLogic->>Hero: hero.y -= 5
Hero->>Hero: Uppdaterar position
Note over Browser,GameLogic: Händelseflöde förhindrar webbläsarens standardåtgärder
Note over GameLogic,Hero: Pub/sub-mönster möjliggör ren kommunikation
```
### Speciella tangenter: en varning!
Vissa tangenter har inbyggda beteenden i webbläsaren som kan störa ditt spel. Piltangenter scrollar sidan och mellanslag hoppar ner beteenden du inte vill ha när någon försöker styra sitt rymdskepp.
Vi kan förhindra dessa standardbeteenden och låta vårt spel hantera inmatningen istället. Det är likt hur tidiga programmerare var tvungna att överstyra systemavbrott för att skapa egna beteenden här gör vi det på webbläsarnivå. Så här gör du:
```javascript
const onKeyDown = function (e) {
console.log(e.keyCode);
switch (e.keyCode) {
case 37:
case 39:
case 38:
case 40: // Piltangenter
case 32:
e.preventDefault();
break; // Mellanslag
default:
break; // blockera inte andra tangenter
}
};
window.addEventListener('keydown', onKeyDown);
```
**Det här förhindrande-koden gör:**
- **Kollar** specifika tangentkoder som kan orsaka oönskat beteende i webbläsaren
- **Stoppar** standardåtgärden i webbläsaren för piltangenter och mellanslag
- **Tillåter** andra tangenter att fungera normalt
- **Använder** `e.preventDefault()` för att stoppa webbläsarens inbyggda beteende
### 🔄 **Pedagogisk kontroll**
**Förståelse för Händelsehantering**: Innan du går vidare till automatisk rörelse, se till att du kan:
- ✅ Förklara skillnaden mellan `keydown` och `keyup`-händelser
- ✅ Förstå varför vi förhindrar standardbeteenden i webbläsaren
- ✅ Beskriva hur eventlyssnare kopplar användarinmatning till spel-logik
- ✅ Identifiera vilka tangenter som kan störa spelkontroller
**Snabbt självtest**: Vad skulle hända om du inte förhindrade standardbeteende för piltangenterna?
*Svar: Webbläsaren skulle scrolla sidan och störa spelets rörelse*
**Event-systemets arkitektur**: Du förstår nu:
- **Lyssnande på fönsternivå**: Fånga händelser på webbläsarnivå
- **Egenskaper på event-objektet**: `key` strängar vs `keyCode` nummer
- **Standardförhindrande**: Stoppa oönskade webbläsarbeteenden
- **Villkorslogik**: Svara på specifika tangentkombinationer
## Spelinducerad rörelse
Nu pratar vi om objekt som rör sig utan spelarinput. Tänk på fiendeskepp som seglar över skärmen, kulor som flyger i raka linjer eller moln som driver i bakgrunden. Denna autonoma rörelse gör att din spelvärld känns levande även när ingen rör kontrollerna.
Vi använder JavaScripts inbyggda timers för att uppdatera positioner med jämna intervaller. Detta koncept påminner om hur pendelur fungerar en regelbunden mekanism som triggar konsekventa, tidsstyrda åtgärder. Så här enkelt kan det vara:
```javascript
const id = setInterval(() => {
// Flytta fienden på y-axeln
enemy.y += 10;
}, 100);
```
**Det här gör rörelsekoden:**
- **Skapar** en timer som körs var 100:e millisekund
- **Uppdaterar** fiendens y-koordinat med 10 pixlar varje gång
- **Sparar** intervallets ID så vi kan stoppa det senare om det behövs
- **Flyttar** fienden automatiskt nedåt på skärmen
## Spelloopen
Här är konceptet som binder samman allt spelloopen. Om ditt spel vore en film, skulle spelloopen vara filmprojektorn som visar bildruta efter bildruta så snabbt att allt ser ut att röra sig mjukt.
Alla spel har en sådan loop som körs i bakgrunden. Det är en funktion som uppdaterar alla spelobjekt, ritar om skärmen och upprepar detta kontinuerligt. Den håller koll på hjälten, alla fiender, laserskott hela spelstatusen.
Det påminner mig om hur tidiga filmanimatörer som Walt Disney var tvungna att rita om karaktärer bildruta för bildruta för att skapa illusionen av rörelse. Vi gör samma sak, fast med kod istället för pennor.
Så här kan en spelloop typiskt se ut i kod:
```mermaid
flowchart TD
A["Starta Spel Loopen"] --> B["Rensa Duk"]
B --> C["Fyll Bakgrund"]
C --> D["Uppdatera Spelobjekt"]
D --> E["Rita Hjälte"]
E --> F["Rita Fiender"]
F --> G["Rita UI Element"]
G --> H["Vänta på Nästa Bildruta"]
H --> I{Spel Körs?}
I -->|Ja| B
I -->|Nej| J["Avsluta Spel"]
subgraph "Bildrutehastighetskontroll"
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
```
```javascript
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);
```
**Förståelse för spelloopsstruktur:**
- **Rensar** hela canvas för att ta bort föregående bildruta
- **Fyller** bakgrunden med en enfärgad bakgrund
- **Ritar** alla spelobjekt i sina aktuella positioner
- **Upprepar** denna process var 200:e millisekund för att skapa mjuk animation
- **Hanterar** bildrutefrekvensen genom att kontrollera intervallets timing
## Fortsättning på Rymdspelet
Nu ska vi lägga till rörelse till den statiska scenen du byggt tidigare. Vi ska förvandla den från en stillbild till en interaktiv upplevelse. Vi arbetar steg för steg för att varje del ska bygga på den föregående.
Hämta koden från där vi slutade i föregående lektion (eller börja med koden i mappen [Part II- starter](../../../../6-space-game/3-moving-elements-around/your-work) om du behöver börja från början).
**Det här bygger vi idag:**
- **Hjälte-kontroller**: Piltangenter styr ditt rymdskepp runt skärmen
- **Fienderörelse**: De där utomjordiska skeppen påbörjar sin framryckning
Låt oss börja implementera dessa funktioner.
## Rekommenderade steg
Leta upp filerna som har skapats åt dig i undermappen `your-work`. Den bör innehålla följande:
```bash
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
```
Du börjar ditt projekt i mappen `your-work` genom att skriva:
```bash
cd your-work
npm start
```
**Det här kommandot gör:**
- **Navigerar** till din projektkatalog
- **Startar** en HTTP-server på adressen `http://localhost:5000`
- **Serverar** dina spel-filer så att du kan testa dem i en webbläsare
Ovanstående startar en HTTP-server på adressen `http://localhost:5000`. Öppna en webbläsare och skriv in den adressen, för tillfället ska det visa hjälten och alla fiender; inget rör sig än!
### Lägg till kod
1. **Lägg till dedikerade objekt** för `hero`, `enemy` och `game object`, de ska ha `x` och `y` egenskaper. (Kom ihåg delen om [Inheritance eller composition](../README.md)).
*TIPS* `game object` ska vara den som har `x` och `y` och förmågan att rita sig själv på en canvas.
> **Tips**: Börja med att lägga till en ny `GameObject`-klass med konstruktorn definierad enligt nedan, och sedan rita den på canvas:
```javascript
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);
}
}
```
**Förståelse för basklassen:**
- **Definierar** gemensamma egenskaper som alla spelobjekt delar (position, storlek, bild)
- **Inkluderar** en flagga `dead` för att hålla koll på om objektet ska tas bort
- **Tillhandahåller** en `draw()`-metod som ritar objektet på canvas
- **Sätter** standardvärden för alla egenskaper som underklasser kan åsidosätta
```mermaid
classDiagram
class GameObject {
+x: number
+y: number
+död: boolean
+typ: string
+bredd: number
+höjd: number
+bild: Image
+rita(ctx)
}
class Hero {
+hastighet: number
+typ: "Hero"
+bredd: 98
+höjd: 75
}
class Enemy {
+typ: "Enemy"
+bredd: 98
+höjd: 50
+sättIntervall()
}
GameObject <|-- Hero
GameObject <|-- Enemy
class EventEmitter {
+lyssnare: object
+på(meddelande, lyssnare)
+skicka(meddelande, data)
}
```
Nu, utöka denna `GameObject` för att skapa `Hero` och `Enemy`:
```javascript
class Hero extends GameObject {
constructor(x, y) {
super(x, y);
this.width = 98;
this.height = 75;
this.type = "Hero";
this.speed = 5;
}
}
```
```javascript
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);
}
}
```
**Nyckelkoncept i dessa klasser:**
- **Ärver** från `GameObject` via `extends`-nyckelordet
- **Kallar** föräldrakonstruktorn med `super(x, y)`
- **Sätter** specifika dimensioner och egenskaper för varje objekttyp
- **Implementerar** automatisk rörelse för fiender med hjälp av `setInterval()`
2. **Lägg till tangentbords-händelsehanterare** för att hantera tangentsurfning (flytta hjälten upp/ner vänster/höger)
*KOM IHÅG* det är ett kartesiskt system, övre vänstra hörnet är `0,0`. Kom också ihåg att lägga till kod för att stoppa *standardbeteende*
> **Tips**: Skapa din `onKeyDown`-funktion och koppla den till fönstret:
```javascript
const onKeyDown = function (e) {
console.log(e.keyCode);
// Lägg till koden från lektionen ovan för att stoppa standardbeteendet
switch (e.keyCode) {
case 37:
case 39:
case 38:
case 40: // Piltangenter
case 32:
e.preventDefault();
break; // Mellanslag
default:
break; // blockera inte andra tangenter
}
};
window.addEventListener("keydown", onKeyDown);
```
**Det här gör eventhanteraren:**
- **Lyssnar** efter keydown-händelser på hela fönstret
- **Loggar** tangentkoden för att hjälpa dig debugga vilka tangenter som trycks
- **Förhindrar** standard webbläsarbeteende för piltangenter och mellanslag
- **Tillåter** andra tangenter att fungera normalt
Kolla din webbläsarkonsol vid detta steg och se tangenttryckningarna loggas.
3. **Implementera** [Pub sub-mönstret](../README.md), detta håller din kod ren när du följer resterande delar.
Publish-Subscribe-mönstret hjälper till att organisera koden genom att separera detektering av händelser från händelsehantering. Det gör koden mer modulär och enklare att underhålla.
För att göra denna sista del kan du:
1. **Lägga till en event listener** på fönstret:
```javascript
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);
}
});
```
**Det här gör event-systemet:**
- **Känner av** tangentbordsinput och konverterar det till egna spelsevenemang
- **Separera** inmatningsdetektion från spelloop-logik
- **Gör det lätt** att ändra kontroller senare utan att påverka spelkoden
- **Tillåter** flera system att svara på samma inmatning
```mermaid
flowchart TD
A["Tangentbordsinmatning"] --> B["Fönsterhändelselyssnare"]
B --> C["Händelseutlösare"]
C --> D["TANGENTEVENT_UP"]
C --> E["TANGENTEVENT_NER"]
C --> F["TANGENTEVENT_VÄNSTER"]
C --> G["TANGENTEVENT_HÖGER"]
D --> H["Hjältens rörelse"]
D --> I["Ljudsystem"]
D --> J["Visuella effekter"]
E --> H
F --> H
G --> H
style A fill:#e1f5fe
style C fill:#e8f5e8
style H fill:#fff3e0
```
2. **Skapa en EventEmitter-klass** för att publicera och prenumerera på meddelanden:
```javascript
class EventEmitter {
constructor() {
this.listeners = {};
}
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
}
this.listeners[message].push(listener);
}
3. **Lägg till konstanter** och sätt upp EventEmitter:
```javascript
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();
```
**Förståelse för uppsättningen:**
- **Definierar** meddelandekonstanter för att undvika felskrivningar och underlätta refaktorering
- **Deklarerar** variabler för bilder, canvas-kontext och spelstatus
- **Skapar** en global event emitter för pub-sub-systemet
- **Initierar** en array för att hålla alla spelobjekt
4. **Initiera spelet**
```javascript
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. **Sätt upp spel-loopen**
Refaktorera funktionen `window.onload` för att initiera spelet och sätta upp en spel-loop med ett bra intervall. Du kommer även att lägga till en laserstråle:
```javascript
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);
};
```
**Förståelse för speluppsättningen:**
- **Väntar** på att sidan ska laddas klart innan start
- **Hämtar** canvas-elementet och dess 2D-renderingskontext
- **Laddar** alla bildresurser asynkront med `await`
- **Startar** spel-loopen som körs med 100ms intervall (10 FPS)
- **Rensar** och ritar om hela skärmen varje bildruta
5. **Lägg till kod** för att röra fiender med ett visst intervall
Refaktorera funktionen `createEnemies()` för att skapa fienderna och lägga till dem i den nya gameObjects-klassen:
```javascript
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);
}
}
}
```
**Vad fiendeskapandet gör:**
- **Beräknar** positioner för att centrera fiender på skärmen
- **Skapar** ett rutnät av fiender med nästlade loopar
- **Tilldelar** fiendebilden till varje fiendeobjekt
- **Lägger till** varje fiende i den globala game objects-arrayen
och lägg till en `createHero()`-funktion för att göra en liknande process för hjälten.
```javascript
function createHero() {
hero = new Hero(
canvas.width / 2 - 45,
canvas.height - canvas.height / 4
);
hero.img = heroImg;
gameObjects.push(hero);
}
```
**Vad hjälteskapandet gör:**
- **Positionerar** hjälten längst ner i mitten av skärmen
- **Tilldelar** hjältebilden till hjälteobjektet
- **Lägger till** hjälten i game objects-arrayen för rendering
och slutligen, lägg till en `drawGameObjects()`-funktion för att börja rita:
```javascript
function drawGameObjects(ctx) {
gameObjects.forEach(go => go.draw(ctx));
}
```
**Förståelse för ritfunktionen:**
- **Itererar** genom alla spelobjekt i arrayen
- **Anropar** `draw()`-metoden på varje objekt
- **Skickar** canvas-kontexten så att objekten kan rita sig själva
### 🔄 **Pedagogisk kontroll**
**Fullständig förståelse av spelsystemet**: Verifiera din behärskning av hela arkitekturen:
- Hur låter arv hjälte och fiende dela gemensamma GameObject-egenskaper?
- Varför gör pub/sub-mönstret din kod mer underhållbar?
- Vilken roll spelar spel-loopen för att skapa smidig animation?
- Hur kopplar event-lyssnare användarinput till spelobjektsbeteende?
**Systemintegration**: Ditt spel demonstrerar nu:
- **Objektorienterad design**: Bas-klasser med specialiserat arv
- **Händelsestyrd arkitektur**: Pub/sub-mönster för lös koppling
- **Animationsramverk**: Spel-loop med konsekventa uppdateringar
- **Inputhantering**: Tangentbords-händelser med standardförhindran
- **Resurshantering**: Bildinladdning och spritrendering
**Professionella mönster**: Du har implementerat:
- **Separation av ansvar**: Input, logik och rendering separerade
- **Polymorfism**: Alla spelobjekt delar en gemensam ritningsgränssnitt
- **Meddelandepassning**: Ren kommunikation mellan komponenter
- **Resurshantering**: Effektiv sprite- och animationshantering
Dina fiender bör nu börja avancera mot ditt hjälteskepp!
}
}
```
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);
}
```
och slutligen, lägg till en `drawGameObjects()`-funktion för att börja rita:
```javascript
function drawGameObjects(ctx) {
gameObjects.forEach(go => go.draw(ctx));
}
```
Dina fiender bör nu börja avancera mot ditt hjälteskepp!
---
## GitHub Copilot Agent-utmaning 🚀
Här är en utmaning som kommer att förbättra ditt spels höjdpunkt: att lägga till gränser och smidiga kontroller. Just nu kan din hjälte flyga utanför skärmen, och rörelsen kan kännas hackig.
**Din uppgift:** Få ditt rymdskepp att kännas mer realistiskt genom att implementera skärmkanter och flytande rörelse. Detta liknar hur NASAs flygkontrollsystem förhindrar att rymdfarkoster överskrider säkra operativa gränser.
**Det här ska du bygga:** Skapa ett system som håller ditt hjälteskepp på skärmen, och gör kontrollerna smidiga. När spelare håller ned en piltangent ska skeppet glida kontinuerligt istället för att röra sig i steg. Tänk också på att lägga till visuell feedback när skeppet når skärmens gränser kanske en subtil effekt för att indikera spelområdet.
Läs mer om [agentläge](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) här.
## 🚀 Utmaning
Kodorganisation blir allt viktigare i takt med att projekt växer. Du kanske har märkt att din fil börjar bli överfull med funktioner, variabler och klasser blandat tillsammans. Detta påminner mig om hur ingenjörerna som organiserade Apollo-uppdragets kod var tvungna att skapa tydliga, underhållbara system som flera team kunde arbeta på samtidigt.
**Din uppgift:**
Tänk som en mjukvaruarkitekt. Hur skulle du organisera din kod så att sex månader från nu, du (eller en kollega) kan förstå vad som händer? Även om allt är i en fil just nu kan du skapa bättre organisation:
- **Gruppera relaterade funktioner** tillsammans med tydliga kommentar-rubriker
- **Separera ansvarsområden** - håll spel-logik åtskild från rendering
- **Använda konsekventa namngivningsprinciper** för variabler och funktioner
- **Skapa moduler** eller namnrymder för att organisera olika delar av spelet
- **Lägga till dokumentation** som förklarar syftet med varje större sektion
**Reflektionsfrågor:**
- Vilka delar av din kod är svårast att förstå när du återvänder till dem?
- Hur skulle du organisera din kod för att göra det lättare för någon annan att bidra?
- Vad skulle hända om du ville lägga till nya funktioner som power-ups eller olika fiendetyper?
## Quiz efter föreläsning
[Quiz efter föreläsning](https://ff-quizzes.netlify.app/web/quiz/34)
## Granskning & Självstudier
Vi har byggt allt från grunden, vilket är fantastiskt för lärande, men här är en liten hemlighet det finns några fantastiska JavaScript-ramverk som kan hantera mycket av det tunga arbetet åt dig. När du känner dig bekväm med grunderna vi gått igenom är det värt att [utforska vad som finns](https://github.com/collections/javascript-game-engines).
Tänk på ramverk som en välfylld verktygslåda istället för att göra varje verktyg för hand. De kan lösa många av de kodorganisationsutmaningar vi talade om, plus erbjuda funktioner som skulle ta veckor att utveckla själv.
**Saker värda att utforska:**
- Hur spelmotorer organiserar kod du kommer att bli förvånad över de smarta mönster de använder
- Prestandatricks för att få canvas-spel att flyta på fint
- Moderna JavaScript-funktioner som kan göra din kod renare och mer underhållbar
- Olika metoder för att hantera spelobjekt och deras relationer
## 🎯 Din tidslinje för spelanimationsexpertis
```mermaid
timeline
title Spelanimering & Interaktionsinlärningsprogression
section Rörelsegrunder (20 minuter)
Animationsprinciper: Ram-baserad animation
: Positionsuppdateringar
: Koordinatsystem
: Mjuk rörelse
section Händelsesystem (25 minuter)
Användarinmatning: Tangentbords-händelsehantering
: Förhindrande av standardbeteende
: Egenskaper hos händelseobjekt
: Lyssning på fönsternivå
section Spelarkitektur (30 minuter)
Objektutformning: Arvsmönster
: Skapande av basklass
: Specialiserade beteenden
: Polymorfa gränssnitt
section Kommunikationsmönster (35 minuter)
Pub/Sub-implementering: Händelseutfärdare
: Meddelandekonstanter
: Lös koppling
: Systemintegration
section Spelloopskunskap (40 minuter)
Realtidssystem: Bildrutehastighetskontroll
: Uppdaterings/rendercykel
: Statushantering
: Prestandaoptimering
section Avancerade tekniker (45 minuter)
Professionella funktioner: Kollisiondetektion
: Fysiksimulering
: Tillståndsmaskiner
: Komponentsystem
section Spelmotorbegrepp (1 vecka)
Ramverksförståelse: Entitets-komponentsystem
: Scengrafer
: Resurspipelines
: Prestandaprofilering
section Produktionskunskaper (1 månad)
Professionell utveckling: Kodorganisation
: Teamsamarbete
: Teststrategier
: Driftsättningsoptimering
```
### 🛠️ Din sammanfattning av spelutvecklingsverktyg
Efter att ha slutfört denna lektion har du nu behärskat:
- **Animationsprinciper**: Bildrörelse och mjuka övergångar
- **Händelsestyrd programmering**: Tangentbordsinmatning med korrekt eventhantering
- **Objektorienterad design**: Arvshierarkier och polymorfa gränssnitt
- **Kommunikationsmönster**: Pub/sub-arkitektur för underhållbar kod
- **Spel-loop-arkitektur**: Realtidsuppdatering och renderingscykler
- **Inmatningssystem**: Användarkontroll med standardbeteendeförhindran
- **Resurshantering**: Spriteinladdning och effektiv rendering
### ⚡ **Vad du kan göra de närmaste 5 minuterna**
- [ ] Öppna webbläsarens konsol och prova `addEventListener('keydown', console.log)` för att se tangentbords-händelser
- [ ] Skapa ett enkelt div-element och flytta det med piltangenter
- [ ] Experimentera med `setInterval` för kontinuerlig rörelse
- [ ] Testa att förhindra standardbeteende med `event.preventDefault()`
### 🎯 **Vad du kan åstadkomma denna timme**
- [ ] Klara quizet efter lektionen och förstå händelsestyrd programmering
- [ ] Bygga det rörliga hjälteskeppet med full tangentbordskontroll
- [ ] Implementera smidiga fienderörelsemönster
- [ ] Lägga till gränser för att förhindra att spelobjekt lämnar skärmen
- [ ] Skapa grundläggande krockdetektering mellan spelobjekt
### 📅 **Din vecka med animation**
- [ ] Slutföra hela rymdspelet med polerad rörelse och interaktioner
- [ ] Lägga till avancerade rörelsemönster som kurvor, acceleration och fysik
- [ ] Implementera mjuka övergångar och easing-funktioner
- [ ] Skapa partikeleffekter och visuella feedback-system
- [ ] Optimera spelets prestanda för mjuk 60fps
- [ ] Lägga till mobil touch-kontroller och responsiv design
### 🌟 **Din månad med interaktiv utveckling**
- [ ] Bygga komplexa interaktiva applikationer med avancerade animationssystem
- [ ] Lära dig animationsbibliotek som GSAP eller skapa din egen animationsmotor
- [ ] Bidra till open source projekt för spelutveckling och animation
- [ ] Bemästra prestandaoptimering för grafikintensiva applikationer
- [ ] Skapa utbildningsmaterial om spelutveckling och animation
- [ ] Bygga en portfolio som visar avancerad interaktiv programmering
**Tillämpningar i verkliga världen**: Dina animationskunskaper används direkt för:
- **Interaktiva webbapplikationer**: Dynamiska instrumentpaneler och realtidsgränssnitt
- **Datavisualisering**: Animerade diagram och interaktiva grafik
- **Utbildningsprogramvara**: Interaktiva simuleringar och lärverktyg
- **Mobilutveckling**: Touchbaserade spel och geststyrning
- **Skrivbordsapplikationer**: Electron-appar med smidig animation
- **Webbanimationer**: CSS- och JavaScript animationsbibliotek
**Professionella färdigheter som uppnåtts**: Du kan nu:
- **Designa** händelsestyrda system som skalar med komplexitet
- **Implementera** mjuka animationer med matematiska principer
- **Felsöka** komplexa interaktionssystem med webbläsarens utvecklarverktyg
- **Optimera** spels prestanda för olika enheter och webbläsare
- **Designa** underhållbara kodstrukturer med beprövade mönster
**Behärskade koncept för spelutveckling**:
- **Bildfrekvenshantering**: Förstå FPS och tidsstyrning
- **Inputhantering**: Plattformoberoende tangentbord och event-system
- **Objektlivscykel**: Skapande, uppdatering och destruktion
- **Tillståndssynkronisering**: Hålla spelstatus konsekvent över bildrutor
- **Händelsearkitektur**: Lös kopplad kommunikation mellan spelsystem
**Nästa nivå**: Du är redo att lägga till krockdetektering, poängsystem, ljudeffekter eller utforska moderna spelramverk som Phaser eller Three.js!
🌟 **Uppdrag slutfört**: Du har byggt ett komplett interaktivt spelsystem med professionella arkitekturmönster!
## Uppgift
[Kommentera din kod](assignment.md)
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**Ansvarsfriskrivning**:
Detta dokument har översatts med hjälp av AI-översättningstjänsten [Co-op Translator](https://github.com/Azure/co-op-translator). Även om vi strävar efter noggrannhet, vänligen observera att automatiska översättningar kan innehålla fel eller brister. Originaldokumentet på dess ursprungliga språk bör betraktas som den auktoritativa källan. För kritisk information rekommenderas professionell mänsklig översättning. Vi ansvarar inte för några missförstånd eller feltolkningar som uppstår från användningen av denna översättning.
<!-- CO-OP TRANSLATOR DISCLAIMER END -->