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

890 lines
37 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.

# Tworzenie gry kosmicznej Część 3: Dodawanie ruchu
```mermaid
journey
title Twoja podróż animacji w grze
section Podstawy ruchu
Zrozum zasady ruchu: 3: Student
Naucz się aktualizacji współrzędnych: 4: Student
Wdroż podstawowy ruch: 4: Student
section Sterowanie graczem
Obsłuż zdarzenia klawiatury: 4: Student
Zapobiegaj domyślnym zachowaniom: 5: Student
Stwórz responsywne sterowanie: 5: Student
section Systemy gry
Zbuduj pętlę gry: 5: Student
Zarządzaj cyklem życia obiektów: 5: Student
Wdroż wzorzec pub/sub: 5: Student
```
Pomyśl o swoich ulubionych grach to, co czyni je fascynującymi, to nie tylko ładna grafika, ale sposób, w jaki wszystko się porusza i reaguje na twoje działania. Obecnie twoja gra kosmiczna przypomina piękny obraz, ale zaraz dodamy ruch, który ożywi ją.
Gdy inżynierowie NASA programowali komputer nawigacyjny dla misji Apollo, stali przed podobnym wyzwaniem: jak sprawić, by statek kosmiczny reagował na polecenia pilota, jednocześnie automatycznie utrzymując poprawki kursu? Zasady, których się dziś nauczymy, odzwierciedlają właśnie te koncepcje zarządzanie ruchem kontrolowanym przez gracza oraz automatycznymi zachowaniami systemu.
W tej lekcji nauczysz się, jak sprawić, by statki kosmiczne sunęły po ekranie, reagowały na polecenia gracza i tworzyły płynne wzorce ruchu. Wszystko rozłożymy na przystępne koncepcje, które naturalnie ze sobą współgrają.
Na koniec gracze będą pilotować swój statek bohatera po ekranie, podczas gdy statki wroga będą patrolować niebo. Co ważniejsze, zrozumiesz podstawowe zasady napędzające systemy ruchu w grach.
```mermaid
mindmap
root((Animacja Gry))
Movement Types
Sterowanie Gracza
Ruch Automatyczny
Oparte na Fizyce
Zaprogramowane Ścieżki
Event Handling
Wejście z Klawiatury
Zdarzenia Myszy
Sterowanie Dotykowe
Zapobieganie Domyślne
Game Loop
Aktualizacja Logiki
Renderowanie Klatki
Czyszczenie Płótna
Kontrola Częstotliwości Klatek
Object Management
Aktualizacje Pozycji
Detekcja Kolizji
Zarządzanie Cyklem Życia
Śledzenie Stanu
Communication
Wzorzec Pub/Sub
Emitery Zdarzeń
Przesyłanie Wiadomości
Luźne Powiązania
```
## Quiz przed wykładem
[Quiz przed wykładem](https://ff-quizzes.netlify.app/web/quiz/33)
## Zrozumienie ruchu w grach
Gry ożywają, gdy coś zaczyna się poruszać, a zasadniczo dzieje się to na dwa sposoby:
- **Ruch kontrolowany przez gracza**: Gdy naciskasz klawisz lub klikasz myszą, coś się porusza. To bezpośrednie powiązanie między tobą a światem gry.
- **Ruch automatyczny**: Gdy gra sama decyduje o ruchu na przykład statki wroga, które muszą patrolować ekran, niezależnie od tego, co robisz.
Poruszanie obiektów na ekranie komputera jest prostsze, niż myślisz. Pamiętasz te współrzędne x i y z lekcji matematyki? To właśnie nimi się tu posługujemy. Gdy Galileo obserwował księżyce Jowisza w 1610 roku, robił zasadniczo to samo śledził pozycje w czasie, by zrozumieć wzory ruchu.
Poruszanie czegoś na ekranie jest jak tworzenie animacji flipbook trzeba wykonać te trzy proste kroki:
```mermaid
flowchart LR
A["Klatka N"] --> B["Aktualizuj pozycje"]
B --> C["Wyczyść płótno"]
C --> D["Narysuj obiekty"]
D --> E["Klatka N+1"]
E --> F{Kontynuować?}
F -->|Tak| B
F -->|Nie| G["Koniec gry"]
subgraph "Cykl animacji"
H["1. Oblicz nowe pozycje"]
I["2. Zetrzyj poprzednią klatkę"]
J["3. Renderuj nową klatkę"]
end
style B fill:#e1f5fe
style C fill:#ffebee
style D fill:#e8f5e8
```
1. **Zaktualizuj pozycję** Zmień miejsce, w którym powinien się znaleźć obiekt (np. przesuń go o 5 pikseli w prawo)
2. **Usuń stary obraz** Wyczyść ekran, aby nie było widoczne "duchowe" ślady
3. **Narysuj nową klatkę** Umieść obiekt w nowym miejscu
Robiąc to wystarczająco szybko, masz płynny ruch, który graczom wydaje się naturalny.
Oto jak może to wyglądać w kodzie:
```javascript
// Ustaw lokalizację bohatera
hero.x += 5;
// Wyczyść prostokąt, który gości bohatera
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Prerysuj tło gry i bohatera
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.drawImage(heroImg, hero.x, hero.y);
```
**To, co robi ten kod:**
- **Aktualizuje** współrzędną x bohatera o 5 pikseli, aby przesunąć go w poziomie
- **Czyści** cały obszar canvas, usuwając poprzednią klatkę
- **Wypełnia** canvas czarnym kolorem tła
- **Rysuje ponownie** obraz bohatera na jego nowej pozycji
✅ Potrafisz wskazać powód, dla którego rysowanie bohatera wielu klatek na sekundę może obciążać wydajność? Przeczytaj o [alternatywach dla tego wzorca](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Optimizing_canvas).
## Obsługa zdarzeń klawiatury
To miejsce, gdzie łączymy wejście gracza z akcją w grze. Gdy ktoś naciśnie spację, by wystrzelić laser, lub dotknie klawisza strzałki, by uniknąć asteroidy, twoja gra musi wykryć i zareagować na te dane wejściowe.
Zdarzenia klawiatury zachodzą na poziomie okna, co oznacza, że całe twoje okno przeglądarki nasłuchuje na naciśnięcia klawiszy. Kliknięcia myszą natomiast mogą być powiązane z konkretnymi elementami (np. kliknięcie przycisku). W naszej grze kosmicznej skupimy się na sterowaniu klawiaturą, ponieważ to one dają graczom klasyczne arcade'owe wrażenie.
Przypomina mi to, jak operatorzy telegrafu w XIX wieku musieli tłumaczyć kod Morsea na zrozumiałe wiadomości robimy podobnie, tłumacząc naciśnięcia klawiszy na polecenia gry.
Aby obsłużyć zdarzenie, należy użyć metody `addEventListener()` okna i podać jej dwa parametry. Pierwszy to nazwa zdarzenia, np. `keyup`. Drugi to funkcja, która zostanie wywołana po zajściu zdarzenia.
Oto przykład:
```javascript
window.addEventListener('keyup', (evt) => {
// evt.key = reprezentacja klawisza w postaci łańcucha znaków
if (evt.key === 'ArrowUp') {
// zrób coś
}
});
```
**Analiza tego, co się tutaj dzieje:**
- **Nasłuchuje** zdarzeń klawiatury na całym oknie
- **Przechwytuje** obiekt zdarzenia, który zawiera informacje, jaki klawisz został naciśnięty
- **Sprawdza**, czy naciśnięty klawisz to konkretny (tu strzałka w górę)
- **Wykonuje** kod, jeśli warunek jest spełniony
Do zdarzeń klawiaturowych można użyć dwóch właściwości obiektu event, by zobaczyć, który klawisz został naciśnięty:
- `key` to jest reprezentacja tekstowa klawisza, np. `'ArrowUp'`
- `keyCode` to jest liczba, np. `37`, odpowiadająca `ArrowLeft`
✅ Manipulacja zdarzeniami klawiatury jest przydatna także poza tworzeniem gier. Jakie inne zastosowania przychodzą ci do głowy dla tej techniki?
```mermaid
sequenceDiagram
participant User
participant Browser
participant EventSystem
participant GameLogic
participant Hero
User->>Browser: Naciska klawisz StrzałkaWgórę
Browser->>EventSystem: zdarzenie keydown
EventSystem->>EventSystem: preventDefault()
EventSystem->>GameLogic: emit('KEY_EVENT_UP')
GameLogic->>Hero: hero.y -= 5
Hero->>Hero: Aktualizuj pozycję
Note over Browser,GameLogic: Przepływ zdarzeń zapobiega domyślnym działaniom przeglądarki
Note over GameLogic,Hero: Wzorzec pub/sub umożliwia czystą komunikację
```
### Specjalne klawisze: uwaga!
Niektóre klawisze mają wbudowane zachowania przeglądarki, które mogą przeszkadzać w grze. Strzałki przewijają stronę, a spacja przesuwa ją w dół tego nie chcemy, gdy ktoś steruje statkiem kosmicznym.
Możemy zapobiec tym domyślnym zachowaniom i pozwolić naszej grze obsłużyć wejście. To podobne do tego, jak pierwsi programiści musieli nadpisać przerwania systemowe, by tworzyć własne zachowania tutaj robimy to na poziomie przeglądarki. Oto jak:
```javascript
const onKeyDown = function (e) {
console.log(e.keyCode);
switch (e.keyCode) {
case 37:
case 39:
case 38:
case 40: // Klawisze strzałek
case 32:
e.preventDefault();
break; // Spacja
default:
break; // nie blokuj innych klawiszy
}
};
window.addEventListener('keydown', onKeyDown);
```
**Zrozumienie tego kodu zapobiegającego:**
- **Sprawdza** konkretne kody klawiszy, które mogą wywołać niechciane zachowania przeglądarki
- **Zatrzymuje** domyślną akcję przeglądarki dla klawiszy strzałek i spacji
- **Pozwala** innym klawiszom działać normalnie
- **Używa** `e.preventDefault()` aby zatrzymać wbudowane zachowanie przeglądarki
### 🔄 **Pedagogiczne podsumowanie**
**Zrozumienie obsługi zdarzeń**: Zanim przejdziemy do ruchu automatycznego, upewnij się, że potrafisz:
- ✅ Wyjaśnić różnicę między zdarzeniami `keydown` i `keyup`
- ✅ Zrozumieć, dlaczego zapobiegamy domyślnym zachowaniom przeglądarki
- ✅ Opisać jak event listenery łączą wejścia użytkownika z logiką gry
- ✅ Wskazać, które klawisze mogą przeszkadzać w sterowaniu grą
**Szybki test**: Co by się stało, gdybyś nie zapobiegł domyślnemu zachowaniu klawiszy strzałek?
*Odpowiedź: Przeglądarka przewijałaby stronę, przeszkadzając w ruchu gry*
**Architektura systemu zdarzeń**: Teraz rozumiesz:
- **Nasłuchiwanie na poziomie okna**: przechwytywanie zdarzeń w przeglądarce
- **Właściwości obiektu zdarzenia**: łańcuchy `key` vs liczby `keyCode`
- **Zapobieganie domyślnemu zachowaniu**: zatrzymywanie niepożądanych działań przeglądarki
- **Logika warunkowa**: reagowanie na konkretne kombinacje klawiszy
## Ruch sterowany przez grę
Porozmawiajmy teraz o obiektach, które poruszają się bez wejścia od gracza. Pomyśl o statkach wroga przemierzających ekran, pociskach lecących po liniach prostych lub chmurach unoszących się w tle. Ten autonomiczny ruch sprawia, że świat twojej gry jest żywy, nawet gdy nikt nie steruje.
Używamy wbudowanych timerów JavaScript do aktualizacji pozycji w regularnych odstępach czasu. Ta koncepcja przypomina działanie zegarów wahadłowych mechanizm wyzwalający spójne, ustalone działania w określonych odstępach. Oto jak prosto to może działać:
```javascript
const id = setInterval(() => {
// Przesuń wroga na osi y
enemy.y += 10;
}, 100);
```
**To, co robi ten kod ruchu:**
- **Tworzy** timer, który działa co 100 milisekund
- **Aktualizuje** współrzędną y wroga o 10 pikseli za każdym razem
- **Przechowuje** ID interwału, aby można go było zatrzymać w razie potrzeby
- **Przesuwa** wroga automatycznie w dół na ekranie
## Pętla gry
Oto koncepcja łącząca wszystko razem pętla gry. Gdyby twoja gra była filmem, pętla gry byłaby projektorem, pokazującym klatkę za klatką na tyle szybko, że wszystko wygląda na płynny ruch.
Każda gra ma taką pętlę działającą w tle. To funkcja, która aktualizuje obiekty gry, rysuje ekran i ciągle powtarza ten proces. Śledzi twojego bohatera, wszystkich wrogów, pociski latające — stan całej gry.
Przypomina mi to, jak pierwsi filmowi animatorzy, tacy jak Walt Disney, musieli rysować postaci klatka po klatce, by stworzyć iluzję ruchu. Teraz robimy to samo, ale za pomocą kodu zamiast ołówków.
Oto jak typowo może wyglądać pętla gry, wyrażona w kodzie:
```mermaid
flowchart TD
A["Rozpocznij pętlę gry"] --> B["Wyczyść płótno"]
B --> C["Wypełnij tło"]
C --> D["Zaktualizuj obiekty gry"]
D --> E["Narysuj bohatera"]
E --> F["Narysuj przeciwników"]
F --> G["Narysuj elementy interfejsu"]
G --> H["Poczekaj na następnej klatki"]
H --> I{Gra działa?}
I -->|Tak| B
I -->|Nie| J["Zakończ grę"]
subgraph "Sterowanie liczba klatek"
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);
```
**Zrozumienie struktury pętli gry:**
- **Czyści** cały canvas, usuwając poprzednią klatkę
- **Wypełnia** tło jednostajnym kolorem
- **Rysuje** wszystkie obiekty gry w ich aktualnych pozycjach
- **Powtarza** ten proces co 200 milisekund, tworząc płynną animację
- **Zarządza** liczbą klatek przez kontrolę interwału czasowego
## Kontynuacja gry kosmicznej
Teraz dodamy ruch do statycznej sceny, którą wcześniej zbudowałeś. Zamienimy ją ze statycznego obrazka w interaktywne doświadczenie. Przejdziemy przez to krok po kroku, aby każdy element naturalnie wynikał z poprzedniego.
Weź kod z miejsca, w którym skończyliśmy w poprzedniej lekcji (lub zacznij od kodu w folderze [Part II- starter](../../../../6-space-game/3-moving-elements-around/your-work), jeśli potrzebujesz świeży start).
**To, co dziś budujemy:**
- **Sterowanie bohaterem**: Klawisze strzałek będą pilotować twój statek kosmiczny po ekranie
- **Ruch wrogów**: Te obce statki rozpoczną swój marsz
Zacznijmy implementować te funkcje.
## Polecane kroki
Znajdź pliki utworzone dla ciebie w podfolderze `your-work`. Powinny zawierać:
```bash
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
```
Projekt rozpoczynasz w folderze `your-work` wpisując:
```bash
cd your-work
npm start
```
**Co robi to polecenie:**
- **Przechodzi** do katalogu twojego projektu
- **Uruchamia** serwer HTTP pod adresem `http://localhost:5000`
- **Serwuje** pliki gry, byś mógł testować ją w przeglądarce
Powyższe uruchomi serwer HTTP pod adresem `http://localhost:5000`. Otwórz przeglądarkę i wpisz ten adres powinien wyświetlić bohatera i wszystkich wrogów; nic się jeszcze nie porusza!
### Dodaj kod
1. **Dodaj dedykowane obiekty** dla `hero`, `enemy` i `game object`, powinny mieć właściwości `x` i `y`. (Przypomnij sobie fragment o [dziedziczeniu lub kompozycji](../README.md)).
*PODPOWIEDŹ* `game object` powinien mieć `x` i `y` oraz zdolność do rysowania siebie na canvas.
> **Wskazówka**: Zacznij od dodania nowej klasy `GameObject` z konstruktorem przedstawionym poniżej, a potem narysuj ją na 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);
}
}
```
**Zrozumienie tej klasy bazowej:**
- **Definiuje** wspólne właściwości współdzielone przez wszystkie obiekty gry (pozycja, rozmiar, obraz)
- **Zawiera** flagę `dead`, by śledzić, czy obiekt powinien zostać usunięty
- **Dostarcza** metodę `draw()`, która renderuje obiekt na canvas
- **Ustawia** wartości domyślne dla wszystkich właściwości, które mogą być nadpisane przez klasy potomne
```mermaid
classDiagram
class GameObject {
+x: number
+y: number
+dead: boolean
+type: string
+width: number
+height: number
+img: Image
+draw(ctx)
}
class Hero {
+speed: number
+type: "Bohater"
+width: 98
+height: 75
}
class Enemy {
+type: "Wróg"
+width: 98
+height: 50
+setInterval()
}
GameObject <|-- Hero
GameObject <|-- Enemy
class EventEmitter {
+listeners: object
+on(wiadomość, słuchacz)
+emit(wiadomość, ładunek)
}
```
Teraz rozszerz tę klasę `GameObject`, aby stworzyć `Hero` i `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);
}
}
```
**Kluczowe koncepcje w tych klasach:**
- **Dziedziczy** po `GameObject` używając słowa kluczowego `extends`
- **Wywołuje** konstruktor klasy nadrzędnej za pomocą `super(x, y)`
- **Ustawia** specyficzne wymiary i właściwości dla każdego typu obiektu
- **Implementuje** automatyczny ruch wrogów wykorzystując `setInterval()`
2. **Dodaj obsługę zdarzeń klawiatury** do nawigacji (ruch bohatera góra/dół/lewo/prawo)
*PAMIĘTAJ* to system kartezjański, lewy górny róg to `0,0`. Pamiętaj też o dodaniu kodu blokującego *domyślne zachowanie*
> **Wskazówka**: Utwórz funkcję `onKeyDown` i podłącz ją do okna:
```javascript
const onKeyDown = function (e) {
console.log(e.keyCode);
// Dodaj kod z powyższej lekcji, aby zatrzymać domyślne zachowanie
switch (e.keyCode) {
case 37:
case 39:
case 38:
case 40: // Klawisze strzałek
case 32:
e.preventDefault();
break; // Spacja
default:
break; // nie blokuj innych klawiszy
}
};
window.addEventListener("keydown", onKeyDown);
```
**Co robi ten handler zdarzeń:**
- **Nasłuchuje** zdarzeń `keydown` na całym oknie
- **Loguje** kod klawisza, aby ułatwić debugowanie, które klawisze są naciskane
- **Zapobiega** domyślnemu zachowaniu przeglądarki dla klawiszy strzałek i spacji
- **Pozwala** innym klawiszom działać normalnie
Sprawdź teraz konsolę przeglądarki i obserwuj logowanie naciśnięć klawiszy.
3. **Zaimplementuj** wzorzec [Pub sub](../README.md), co pozwoli utrzymać kod w czystości, gdy przejdziesz do dalszych części.
Wzorzec Publish-Subscribe pomaga zorganizować kod przez oddzielenie wykrywania zdarzeń od ich obsługi. Sprawia to, że kod jest bardziej modułowy i łatwiejszy w utrzymaniu.
Aby to zrobić, możesz:
1. **Dodać listener zdarzeń** na oknie:
```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);
}
});
```
**Co robi ten system zdarzeń:**
- **Wykrywa** wejścia z klawiatury i zamienia je na niestandardowe zdarzenia gry
- **Oddziela** wykrywanie wejścia od logiki gry
- **Ułatwia** późniejszą zmianę sterowania bez wpływu na kod gry
- **Pozwala** wielu systemom reagować na to samo wejście
```mermaid
flowchart TD
A["Wejście z klawiatury"] --> B["Nasłuchiwacz zdarzeń okna"]
B --> C["Emiter zdarzeń"]
C --> D["WYDARZENIE_KLAWISZA_GÓRA"]
C --> E["WYDARZENIE_KLAWISZA_DÓŁ"]
C --> F["WYDARZENIE_KLAWISZA_LEWO"]
C --> G["WYDARZENIE_KLAWISZA_PRAWO"]
D --> H["Ruch bohatera"]
D --> I["System dźwiękowy"]
D --> J["Efekty wizualne"]
E --> H
F --> H
G --> H
style A fill:#e1f5fe
style C fill:#e8f5e8
style H fill:#fff3e0
```
2. **Utwórz klasę EventEmitter**, aby publikować i subskrybować wiadomości:
```javascript
class EventEmitter {
constructor() {
this.listeners = {};
}
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
}
this.listeners[message].push(listener);
}
3. **Dodaj stałe** i skonfiguruj 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();
```
**Zrozumienie konfiguracji:**
- **Definiuje** stałe wiadomości, by unikać literówek i ułatwić refaktoryzację
- **Deklaruje** zmienne dla obrazów, kontekstu canvas i stanu gry
- **Tworzy** globalny emiter zdarzeń dla systemu pub-sub
- **Inicjuje** tablicę do przechowywania wszystkich obiektów gry
4. **Zainicjuj grę**
```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. **Ustaw pętlę gry**
Zrefaktoryzuj funkcję `window.onload`, aby zainicjować grę i ustawić pętlę gry w dobrym odstępie czasowym. Dodasz też wiązkę lasera:
```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);
};
```
**Zrozumienie ustawień gry:**
- **Czeka** na pełne załadowanie strony przed rozpoczęciem
- **Pobiera** element canvas i jego kontekst renderowania 2D
- **Ładuje** asynchronicznie wszystkie assety obrazów używając `await`
- **Uruchamia** pętlę gry działającą co 100 ms (10 kl./s)
- **Czyści** i ponownie rysuje cały ekran każdą klatkę
5. **Dodaj kod** do poruszania się wrogów w określonych odstępach czasu
Zrefaktoryzuj funkcję `createEnemies()`, aby tworzyć wrogów i dodawać ich do nowej klasy gameObjects:
```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);
}
}
}
```
**Co robi tworzenie wrogów:**
- **Oblicza** pozycje, by wycentrować wrogów na ekranie
- **Tworzy** siatkę wrogów za pomocą zagnieżdżonych pętli
- **Przypisuje** obraz wroga do każdego obiektu wroga
- **Dodaje** każdego wroga do globalnej tablicy obiektów gry
oraz dodaj funkcję `createHero()`, aby w podobny sposób utworzyć bohatera.
```javascript
function createHero() {
hero = new Hero(
canvas.width / 2 - 45,
canvas.height - canvas.height / 4
);
hero.img = heroImg;
gameObjects.push(hero);
}
```
**Co robi tworzenie bohatera:**
- **Ustawia** bohatera na dole, na środku ekranu
- **Przypisuje** obraz bohatera do obiektu bohatera
- **Dodaje** bohatera do tablicy obiektów gry do renderowania
i na koniec dodaj funkcję `drawGameObjects()`, aby rozpocząć rysowanie:
```javascript
function drawGameObjects(ctx) {
gameObjects.forEach(go => go.draw(ctx));
}
```
**Zrozumienie funkcji rysującej:**
- **Iteruje** przez wszystkie obiekty gry w tablicy
- **Wywołuje** metodę `draw()` na każdym obiekcie
- **Przekazuje** kontekst canvas, aby obiekty mogły się same renderować
### 🔄 **Pedagogiczne Podsumowanie**
**Kompletne zrozumienie systemu gry**: Zweryfikuj swoją znajomość całej architektury:
- Jak dziedziczenie pozwala bohaterowi i wrogom dzielić wspólne właściwości GameObject?
- Dlaczego wzorzec pub/sub sprawia, że twój kod jest bardziej utrzymywany?
- Jaką rolę pełni pętla gry w tworzeniu płynnej animacji?
- Jak nasłuchiwacze zdarzeń łączą interakcję użytkownika z zachowaniem obiektów gry?
**Integracja systemu**: Twoja gra teraz demonstruje:
- **Projektowanie obiektowe**: Klasy bazowe z wyspecjalizowanym dziedziczeniem
- **Architektura zdarzeniowa**: Wzorzec pub/sub dla luźnego powiązania
- **Framework animacji**: Pętla gry ze spójnymi aktualizacjami klatek
- **Obsługa wejścia**: Zdarzenia klawiatury z zapobieganiem domyślnemu działaniu
- **Zarządzanie assetami**: Ładowanie obrazów i renderowanie spritew
**Wzorce profesjonalne**: Zaimplementowałeś:
- **Separation of Concerns**: Separacja logiki, wejścia i renderowania
- **Polimorfizm**: Wszystkie obiekty gry dzielą wspólny interfejs rysowania
- **Przesyłanie wiadomości**: Czysta komunikacja między komponentami
- **Zarządzanie zasobami**: Efektywne zarządzanie sprite'ami i animacjami
Twoi wrogowie powinni zacząć przesuwać się w stronę Twojego statku bohatera!
}
}
```
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);
}
```
i na koniec dodaj funkcję `drawGameObjects()`, aby rozpocząć rysowanie:
```javascript
function drawGameObjects(ctx) {
gameObjects.forEach(go => go.draw(ctx));
}
```
Twoi wrogowie powinni zacząć przesuwać się w stronę Twojego statku bohatera!
---
## GitHub Copilot Agent Challenge 🚀
Oto wyzwanie, które poprawi dopracowanie Twojej gry: dodanie granic i płynnych sterowań. Obecnie Twój bohater może wyjść poza ekran, a ruch może wydawać się szarpany.
**Twoja misja:** Spraw, aby Twój statek kosmiczny był bardziej realistyczny, implementując granice ekranu i płynny ruch. To podobne do systemów kontroli lotów NASA, które zapobiegają przekraczaniu bezpiecznych parametrów operacyjnych przez statki kosmiczne.
**Co zbudować:** Stwórz system, który utrzymuje statek bohatera na ekranie i spraw, aby sterowanie było płynne. Gdy gracze przytrzymają klawisz strzałki, statek powinien się ślizgać ciągle, zamiast poruszać się skokowo. Rozważ dodanie wizualnej informacji zwrotnej, gdy statek dotrze do granic ekranu np. subtelny efekt wskazujący krawędź obszaru gry.
Dowiedz się więcej o [trybie agenta](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode).
## 🚀 Wyzwanie
Organizacja kodu staje się coraz ważniejsza w miarę rozrostu projektów. Mogłeś zauważyć, że Twój plik robi się zatłoczony od funkcji, zmiennych i klas wymieszanych razem. Przypomina to to, jak inżynierowie organizujący kod misji Apollo musieli tworzyć jasne, łatwe w utrzymaniu systemy, nad którymi mogło pracować wiele zespołów jednocześnie.
**Twoja misja:**
Myśl jak architekt oprogramowania. Jak zorganizowałbyś swój kod, aby za sześć miesięcy Ty (lub kolega) mogli z łatwością zrozumieć, co się dzieje? Nawet jeśli wszystko zostanie w jednym pliku, możesz stworzyć lepszą organizację:
- **Grupowanie powiązanych funkcji** razem z wyraźnymi nagłówkami komentarzy
- **Separacja obowiązków** oddzielenie logiki gry od renderowania
- **Stosowanie spójnej nazewnictwa** zmiennych i funkcji
- **Tworzenie modułów** lub przestrzeni nazw do organizacji różnych aspektów Twojej gry
- **Dodanie dokumentacji** wyjaśniającej cel każdej większej sekcji
**Pytania refleksyjne:**
- Które części Twojego kodu są najtrudniejsze do zrozumienia po powrocie do nich?
- Jak mógłbyś zorganizować kod, by ułatwić innym programistom współpracę?
- Co by się stało, gdybyś chciał dodać nowe funkcje, jak power-upy lub różne typy wrogów?
## Quiz po wykładzie
[Quiz po wykładzie](https://ff-quizzes.netlify.app/web/quiz/34)
## Przegląd & Samodzielna nauka
Budowaliśmy wszystko od podstaw, co jest fantastyczne dla nauki, ale oto mały sekret istnieje wiele świetnych frameworków JavaScript, które mogą odciążyć Cię w wielu zadaniach. Kiedy poczujesz się pewnie z podstawami, warto [sprawdzić, co jest dostępne](https://github.com/collections/javascript-game-engines).
Pomyśl o frameworkach jak o dobrze wyposażonej skrzynce narzędziowej zamiast tworzeniu każdego narzędzia samodzielnie. Mogą rozwiązać wiele wyzwań związanych z organizacją kodu, o których rozmawialiśmy, a także oferują funkcje, które zbudowanie samemu zajęłoby tygodnie.
**Warto zbadać:**
- Jak silniki gier organizują kod zdziwisz się, jakie sprytne wzorce stosują
- Triki wydajnościowe pozwalające uruchomić gry canvas płynnie
- Nowoczesne funkcje JavaScript, które mogą uczynić Twój kod czystszym i łatwiejszym w utrzymaniu
- Różne podejścia do zarządzania obiektami gry i ich relacjami
## 🎯 Twój Oś Czasu Mistrzostwa Animacji Gry
```mermaid
timeline
title Postępy w nauce animacji i interakcji w grach
section Podstawy ruchu (20 minut)
Zasady animacji: Animacja klatkowa
: Aktualizacje pozycji
: Układy współrzędnych
: Płynny ruch
section Systemy zdarzeń (25 minut)
Wejście użytkownika: Obsługa zdarzeń klawiatury
: Zapobieganie domyślnemu zachowaniu
: Właściwości obiektu zdarzenia
: Nasłuchiwanie na poziomie okna
section Architektura gry (30 minut)
Projektowanie obiektów: Wzorce dziedziczenia
: Tworzenie klasy bazowej
: Wyszukane zachowania
: Polimorficzne interfejsy
section Wzorce komunikacji (35 minut)
Implementacja Pub/Sub: Emiter zdarzeń
: Stałe wiadomości
: Luźne powiązania
: Integracja systemu
section Opanowanie pętli gry (40 minut)
Systemy czasu rzeczywistego: Kontrola liczby klatek
: Cykl aktualizacji/renderowania
: Zarządzanie stanem
: Optymalizacja wydajności
section Zaawansowane techniki (45 minut)
Funkcje profesjonalne: Wykrywanie kolizji
: Symulacja fizyki
: Maszyny stanów
: Systemy komponentów
section Koncepcje silnika gry (1 tydzień)
Zrozumienie frameworka: Systemy encja-komponent
: Grafy scen
: Kanały zasobów
: Profilowanie wydajności
section Umiejętności produkcyjne (1 miesiąc)
Rozwój zawodowy: Organizacja kodu
: Współpraca w zespole
: Strategie testowania
: Optymalizacja wdrożenia
```
### 🛠️ Podsumowanie Twojego Zestawu Narzędzi Do Tworzenia Gier
Po ukończeniu tej lekcji, opanowałeś:
- **Zasady animacji**: Ruch oparty na klatkach i płynne przejścia
- **Programowanie zdarzeniowe**: Obsługa wejścia z klawiatury z odpowiednim zarządzaniem zdarzeniami
- **Projektowanie obiektowe**: Hierarchie dziedziczenia i polimorficzne interfejsy
- **Wzorce komunikacji**: Architektura pub/sub dla utrzymania kodu
- **Architektura pętli gry**: Aktualizacje i cykle renderowania w czasie rzeczywistym
- **Systemy wejścia**: Mapowanie sterowania użytkownika z zapobieganiem domyślnemu zachowaniu
- **Zarządzanie zasobami**: Ładowanie sprite’ów i efektywne techniki renderowania
### ⚡ **Co Możesz Zrobić w Następnych 5 Minutach**
- [ ] Otwórz konsolę przeglądarki i wypróbuj `addEventListener('keydown', console.log)` aby zobaczyć zdarzenia klawiatury
- [ ] Stwórz prosty element div i przesuwaj go za pomocą klawiszy strzałek
- [ ] Eksperymentuj z `setInterval`, aby uzyskać ciągły ruch
- [ ] Spróbuj zapobiegać domyślnemu zachowaniu za pomocą `event.preventDefault()`
### 🎯 **Co Możesz Osiągnąć w Ciągu Godziny**
- [ ] Ukończ quiz po lekcji i zrozum programowanie zdarzeniowe
- [ ] Zbuduj poruszający się statek bohatera z pełną obsługą klawiatury
- [ ] Zaimplementuj płynne wzory ruchu wrogów
- [ ] Dodaj granice zapobiegające opuszczaniu ekranu przez obiekty gry
- [ ] Stwórz podstawową detekcję kolizji między obiektami gry
### 📅 **Twoja Tygodniowa Podróż Animacji**
- [ ] Ukończ pełną grę kosmiczną z dopracowanym ruchem i interakcjami
- [ ] Dodaj zaawansowane wzory ruchu, jak krzywe, przyspieszenia i fizyka
- [ ] Wprowadź płynne przejścia i funkcje easing
- [ ] Stwórz efekty cząsteczkowe i systemy informacji wizualnej
- [ ] Optymalizuj wydajność gry dla płynnej rozgrywki 60fps
- [ ] Dodaj sterowanie dotykowe i responsywny design
### 🌟 **Twój Miesięczny Interaktywny Rozwój**
- [ ] Buduj złożone aplikacje interaktywne z zaawansowanymi systemami animacji
- [ ] Ucz się bibliotek animacji takich jak GSAP lub stwórz własny silnik animacji
- [ ] Współpracuj przy open sourceowych projektach gier i animacji
- [ ] Opanuj optymalizację wydajności dla aplikacji intensywnie korzystających z grafiki
- [ ] Twórz materiały edukacyjne o tworzeniu gier i animacji
- [ ] Zbuduj portfolio prezentujące zaawansowane umiejętności programowania interaktywnego
**Zastosowania w rzeczywistości:** Twoje umiejętności animacji gier odnoszą się bezpośrednio do:
- **Interaktywnych aplikacji webowych**: Dynamiczne pulpity i interfejsy w czasie rzeczywistym
- **Wizualizacji danych**: Animowane wykresy i grafiki interaktywne
- **Oprogramowania edukacyjnego**: Symulacje interaktywne i narzędzia do nauki
- **Rozwoju mobilnego**: Gry i obsługa gestów dotykowych
- **Aplikacji desktopowych**: Aplikacje Electron z płynnymi animacjami
- **Animacji webowych**: Biblioteki animacji CSS i JavaScript
**Umiejętności zawodowe zdobyte:** Potrafisz teraz:
- **Projektować** systemy zdarzeniowe skalujące się złożonością
- **Implementować** płynne animacje za pomocą zasad matematycznych
- **Debugować** złożone systemy interakcji używając narzędzi deweloperskich przeglądarki
- **Optymalizować** wydajność gry na różne urządzenia i przeglądarki
- **Projektować** utrzymywalne struktury kodu wg sprawdzonych wzorców
**Opanowane koncepcje tworzenia gier:**
- **Zarządzanie liczbą klatek**: Zrozumienie FPS i kontroli czasu
- **Obsługa wejścia**: Wieloplatformowe systemy klawiatury i zdarzeń
- **Cykl życia obiektu**: Wzorce tworzenia, aktualizacji i niszczenia
- **Synchronizacja stanu**: Utrzymanie spójności stanu gry między klatkami
- **Architektura zdarzeń**: Luzowanie powiązań między systemami gry
**Kolejny poziom:** Jesteś gotowy dodać detekcję kolizji, systemy punktacji, efekty dźwiękowe lub zgłębić nowoczesne frameworki gier, takie jak Phaser lub Three.js!
🌟 **Osiągnięcie odblokowane**: Zbudowałeś kompletny interaktywny system gry z profesjonalnymi wzorcami architektonicznymi!
## Zadanie
[Skomentuj swój kod](assignment.md)
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**Oświadczenie**:
Niniejszy dokument został przetłumaczony za pomocą usługi tłumaczenia AI [Co-op Translator](https://github.com/Azure/co-op-translator). Mimo że dokładamy starań, aby tłumaczenie było jak najbardziej precyzyjne, prosimy pamiętać, że automatyczne tłumaczenia mogą zawierać błędy lub nieścisłości. Oryginalny dokument w języku źródłowym powinien być uważany za źródło autorytatywne. W przypadku informacji krytycznych zaleca się skorzystanie z profesjonalnego tłumaczenia wykonanego przez człowieka. Nie ponosimy odpowiedzialności za jakiekolwiek nieporozumienia lub błędne interpretacje wynikające z użycia tego tłumaczenia.
<!-- CO-OP TRANSLATOR DISCLAIMER END -->