17 KiB
Izgradnja svemirske igre, 1. dio: Uvod
Baš kao što NASA-ina kontrola misije koordinira više sustava tijekom lansiranja svemirske letjelice, mi ćemo izgraditi svemirsku igru koja pokazuje kako različiti dijelovi programa mogu besprijekorno surađivati. Dok stvarate nešto što zapravo možete igrati, naučit ćete osnovne programerske koncepte koji se primjenjuju na bilo koji softverski projekt.
Istražit ćemo dva temeljna pristupa organizaciji koda: nasljeđivanje i kompoziciju. Ovo nisu samo akademski koncepti – to su isti obrasci koji pokreću sve, od videoigara do bankarskih sustava. Također ćemo implementirati komunikacijski sustav nazvan pub/sub koji funkcionira poput komunikacijskih mreža koje se koriste u svemirskim letjelicama, omogućujući različitim komponentama da dijele informacije bez stvaranja ovisnosti.
Do kraja ove serije, razumjet ćete kako izgraditi aplikacije koje se mogu skalirati i razvijati – bilo da razvijate igre, web aplikacije ili bilo koji drugi softverski sustav.
Kviz prije predavanja
Nasljeđivanje i kompozicija u razvoju igara
Kako projekti postaju složeniji, organizacija koda postaje ključna. Ono što započne kao jednostavan skript može postati teško za održavanje bez odgovarajuće strukture – slično kao što su Apollo misije zahtijevale pažljivu koordinaciju između tisuća komponenti.
Istražit ćemo dva temeljna pristupa organizaciji koda: nasljeđivanje i kompoziciju. Svaki od njih ima svoje prednosti, a razumijevanje oba pomaže vam odabrati pravi pristup za različite situacije. Ove koncepte ćemo demonstrirati kroz našu svemirsku igru, gdje junaci, neprijatelji, pojačanja i drugi objekti moraju učinkovito međusobno djelovati.
✅ Jedna od najpoznatijih knjiga o programiranju odnosi se na obrasce dizajna.
U svakoj igri postoje objekti igre – interaktivni elementi koji ispunjavaju vaš svijet igre. Junaci, neprijatelji, pojačanja i vizualni efekti su svi objekti igre. Svaki od njih postoji na određenim koordinatama ekrana koristeći vrijednosti x i y, slično kao što se točke postavljaju na koordinatni sustav.
Unatoč njihovim vizualnim razlikama, ovi objekti često dijele osnovna ponašanja:
- Postoje negdje – Svaki objekt ima x i y koordinate kako bi igra znala gdje ga nacrtati
- Mnogi se mogu kretati – Junaci trče, neprijatelji jure, meci lete preko ekrana
- Imaju životni vijek – Neki ostaju zauvijek, drugi (poput eksplozija) pojavljuju se nakratko i nestaju
- Reagiraju na stvari – Kada se sudare, pojačanja se prikupljaju, trake zdravlja se ažuriraju
✅ Razmislite o igri poput Pac-Mana. Možete li identificirati četiri vrste objekata navedene gore u ovoj igri?
Izražavanje ponašanja kroz kod
Sada kada razumijete zajednička ponašanja koja dijele objekti igre, istražimo kako implementirati ta ponašanja u JavaScriptu. Ponašanje objekta možete izraziti kroz metode pridružene klasama ili pojedinačnim objektima, a postoji nekoliko pristupa koje možete odabrati.
Pristup temeljen na klasama
Klase i nasljeđivanje pružaju strukturirani pristup organizaciji objekata igre. Kao taksonomski sustav klasifikacije koji je razvio Carl Linnaeus, započinjete s osnovnom klasom koja sadrži zajedničke značajke, a zatim stvarate specijalizirane klase koje nasljeđuju te osnove dok dodaju specifične sposobnosti.
✅ Nasljeđivanje je važan koncept za razumijevanje. Saznajte više u MDN-ovom članku o nasljeđivanju.
Evo kako možete implementirati objekte igre koristeći klase i nasljeđivanje:
// Step 1: Create the base GameObject class
class GameObject {
constructor(x, y, type) {
this.x = x;
this.y = y;
this.type = type;
}
}
Razložimo ovo korak po korak:
- Stvaramo osnovni predložak koji svaki objekt igre može koristiti
- Konstruktor sprema gdje se objekt nalazi (
x,y) i kakva je to stvar - Ovo postaje temelj na kojem će se graditi svi vaši objekti igre
// Step 2: Add movement capability through inheritance
class Movable extends GameObject {
constructor(x, y, type) {
super(x, y, type); // Call parent constructor
}
// Add the ability to move to a new position
moveTo(x, y) {
this.x = x;
this.y = y;
}
}
U gore navedenom, učinili smo sljedeće:
- Proširili klasu GameObject kako bismo dodali funkcionalnost kretanja
- Pozvali konstruktor roditelja koristeći
super()za inicijalizaciju naslijeđenih svojstava - Dodali metodu
moveTo()koja ažurira poziciju objekta
// Step 3: Create specific game object types
class Hero extends Movable {
constructor(x, y) {
super(x, y, 'Hero'); // Set type automatically
}
}
class Tree extends GameObject {
constructor(x, y) {
super(x, y, 'Tree'); // Trees don't need movement
}
}
// Step 4: Use your game objects
const hero = new Hero(0, 0);
hero.moveTo(5, 5); // Hero can move!
const tree = new Tree(10, 15);
// tree.moveTo() would cause an error - trees can't move
Razumijevanje ovih koncepata:
- Stvara specijalizirane vrste objekata koji nasljeđuju odgovarajuća ponašanja
- Pokazuje kako nasljeđivanje omogućuje selektivno uključivanje značajki
- Prikazuje da se junaci mogu kretati dok drveće ostaje nepomično
- Ilustrira kako hijerarhija klasa sprječava neprimjerene radnje
✅ Odvojite nekoliko minuta da ponovno osmislite junaka iz Pac-Mana (na primjer, Inky, Pinky ili Blinky) i kako bi bio napisan u JavaScriptu.
Pristup kompozicijom
Kompozicija slijedi filozofiju modularnog dizajna, slično kao što inženjeri dizajniraju svemirske letjelice s izmjenjivim komponentama. Umjesto nasljeđivanja od roditeljske klase, kombinirate specifična ponašanja kako biste stvorili objekte s točno onim funkcionalnostima koje su im potrebne. Ovaj pristup nudi fleksibilnost bez krutih hijerarhijskih ograničenja.
// Step 1: Create base behavior objects
const gameObject = {
x: 0,
y: 0,
type: ''
};
const movable = {
moveTo(x, y) {
this.x = x;
this.y = y;
}
};
Evo što ovaj kod radi:
- Definira osnovni
gameObjects svojstvima pozicije i tipa - Stvara zaseban objekt ponašanja
movables funkcionalnošću kretanja - Razdvaja brige tako da podaci o poziciji i logika kretanja ostanu neovisni
// Step 2: Compose objects by combining behaviors
const movableObject = { ...gameObject, ...movable };
// Step 3: Create factory functions for different object types
function createHero(x, y) {
return {
...movableObject,
x,
y,
type: 'Hero'
};
}
function createStatic(x, y, type) {
return {
...gameObject,
x,
y,
type
};
}
U gore navedenom, učinili smo sljedeće:
- Kombinirali osnovna svojstva objekta s ponašanjem kretanja koristeći spread sintaksu
- Stvorili funkcije tvornice koje vraćaju prilagođene objekte
- Omogućili fleksibilno stvaranje objekata bez krutih hijerarhija klasa
- Dopustili objektima da imaju točno ona ponašanja koja su im potrebna
// Step 4: Create and use your composed objects
const hero = createHero(10, 10);
hero.moveTo(5, 5); // Works perfectly!
const tree = createStatic(0, 0, 'Tree');
// tree.moveTo() is undefined - no movement behavior was composed
Ključne točke za zapamtiti:
- Komponira objekte miješanjem ponašanja umjesto nasljeđivanja
- Pruža više fleksibilnosti od krutih hijerarhija nasljeđivanja
- Dopustite objektima da imaju točno one značajke koje su im potrebne
- Koristi modernu JavaScript spread sintaksu za čisto kombiniranje objekata
**Which Pattern Should You Choose?**
> 💡 **Pro Tip**: Both patterns have their place in modern JavaScript development. Classes work well for clearly defined hierarchies, while composition shines when you need maximum flexibility.
>
**Here's when to use each approach:**
- **Choose** inheritance when you have clear "is-a" relationships (a Hero *is-a* Movable object)
- **Select** composition when you need "has-a" relationships (a Hero *has* movement abilities)
- **Consider** your team's preferences and project requirements
- **Remember** that you can mix both approaches in the same application
## Communication Patterns: The Pub/Sub System
As applications grow complex, managing communication between components becomes challenging. The publish-subscribe pattern (pub/sub) solves this problem using principles similar to radio broadcasting – one transmitter can reach multiple receivers without knowing who's listening.
Consider what happens when a hero takes damage: the health bar updates, sound effects play, visual feedback appears. Rather than coupling the hero object directly to these systems, pub/sub allows the hero to broadcast a "damage taken" message. Any system that needs to respond can subscribe to this message type and react accordingly.
✅ **Pub/Sub** stands for 'publish-subscribe'
### Understanding the Pub/Sub Architecture
The pub/sub pattern keeps different parts of your application loosely coupled, meaning they can work together without being directly dependent on each other. This separation makes your code more maintainable, testable, and flexible to changes.
**The key players in pub/sub:**
- **Messages** – Simple text labels like `'PLAYER_SCORED'` that describe what happened (plus any extra info)
- **Publishers** – The objects that shout out "Something happened!" to anyone who's listening
- **Subscribers** – The objects that say "I care about that event" and react when it happens
- **Event System** – The middleman that makes sure messages get to the right listeners
### Building an Event System
Let's create a simple but powerful event system that demonstrates these concepts:
```javascript
// Step 1: Create the EventEmitter class
class EventEmitter {
constructor() {
this.listeners = {}; // Store all event listeners
}
// Register a listener for a specific message type
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
}
this.listeners[message].push(listener);
}
// Send a message to all registered listeners
emit(message, payload = null) {
if (this.listeners[message]) {
this.listeners[message].forEach(listener => {
listener(message, payload);
});
}
}
}
Razlaganje što se ovdje događa:
- Stvara centralni sustav za upravljanje događajima koristeći jednostavnu klasu
- Pohranjuje slušatelje u objekt organiziran prema vrsti poruke
- Registrira nove slušatelje koristeći metodu
on() - Emitira poruke svim zainteresiranim slušateljima koristeći
emit() - Podržava opcionalne podatkovne pakete za prijenos relevantnih informacija
Sve na jednom mjestu: Praktičan primjer
U redu, pogledajmo kako to funkcionira! Izgradit ćemo jednostavan sustav kretanja koji pokazuje kako čist i fleksibilan pub/sub može biti:
// Step 1: Define your message types
const Messages = {
HERO_MOVE_LEFT: 'HERO_MOVE_LEFT',
HERO_MOVE_RIGHT: 'HERO_MOVE_RIGHT',
ENEMY_SPOTTED: 'ENEMY_SPOTTED'
};
// Step 2: Create your event system and game objects
const eventEmitter = new EventEmitter();
const hero = createHero(0, 0);
Evo što ovaj kod radi:
- Definira objekt konstanti kako bi se spriječile pogreške u nazivima poruka
- Stvara instancu emitera događaja za upravljanje svim komunikacijama
- Inicijalizira junaka na početnoj poziciji
// Step 3: Set up event listeners (subscribers)
eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
hero.moveTo(hero.x - 5, hero.y);
console.log(`Hero moved to position: ${hero.x}, ${hero.y}`);
});
eventEmitter.on(Messages.HERO_MOVE_RIGHT, () => {
hero.moveTo(hero.x + 5, hero.y);
console.log(`Hero moved to position: ${hero.x}, ${hero.y}`);
});
U gore navedenom, učinili smo sljedeće:
- Registrirali slušatelje događaja koji reagiraju na poruke o kretanju
- Ažurirali poziciju junaka na temelju smjera kretanja
- Dodali zapisivanje u konzolu za praćenje promjena pozicije junaka
- Razdvojili logiku kretanja od obrade unosa
// Step 4: Connect keyboard input to events (publishers)
window.addEventListener('keydown', (event) => {
switch(event.key) {
case 'ArrowLeft':
eventEmitter.emit(Messages.HERO_MOVE_LEFT);
break;
case 'ArrowRight':
eventEmitter.emit(Messages.HERO_MOVE_RIGHT);
break;
}
});
Razumijevanje ovih koncepata:
- Povezuje unos s tipkovnice s događajima u igri bez čvrstog povezivanja
- Omogućuje sustavu unosa da neizravno komunicira s objektima igre
- Dopušta više sustava da reagiraju na iste događaje s tipkovnice
- Olakšava promjenu tipki ili dodavanje novih metoda unosa
💡 Savjet: Ljepota ovog obrasca je fleksibilnost! Možete lako dodati zvučne efekte, podrhtavanje ekrana ili čestične efekte jednostavnim dodavanjem više slušatelja događaja – nema potrebe za izmjenom postojećeg koda za tipkovnicu ili kretanje.
Zašto ćete voljeti ovaj pristup:
- Dodavanje novih značajki postaje vrlo jednostavno – samo slušajte događaje koji vas zanimaju
- Više stvari može reagirati na isti događaj bez međusobnog ometanja
- Testiranje postaje puno jednostavnije jer svaki dio radi neovisno
- Kada nešto ne radi, točno znate gdje tražiti problem
Zašto se Pub/Sub učinkovito skalira
Obrazac pub/sub održava jednostavnost dok aplikacije postaju složenije. Bilo da upravljate desecima neprijatelja, dinamičkim ažuriranjima korisničkog sučelja ili zvučnim sustavima, obrazac podnosi povećanu složenost bez promjena u arhitekturi. Nove značajke se integriraju u postojeći sustav događaja bez utjecaja na postojeću funkcionalnost.
⚠️ Uobičajena pogreška: Nemojte stvarati previše specifičnih vrsta poruka na početku. Započnite sa širokim kategorijama i precizirajte ih kako se potrebe vaše igre budu jasnije oblikovale.
Najbolje prakse koje treba slijediti:
- Grupirajte povezane poruke u logičke kategorije
- Koristite opisne nazive koji jasno ukazuju na ono što se dogodilo
- Održavajte podatkovne pakete poruka jednostavnima i fokusiranima
- Dokumentirajte vrste poruka za suradnju tima
Izazov GitHub Copilot Agent 🚀
Koristite način rada Agent za dovršavanje sljedećeg izazova:
Opis: Stvorite jednostavan sustav objekata igre koristeći i nasljeđivanje i obrazac pub/sub. Implementirat ćete osnovnu igru u kojoj različiti objekti mogu komunicirati putem događaja bez da izravno znaju jedni za druge.
Zadatak: Stvorite sustav igre u JavaScriptu s sljedećim zahtjevima: 1) Stvorite osnovnu klasu GameObject s koordinatama x, y i svojstvom tipa. 2) Stvorite klasu Hero koja proširuje GameObject i može se kretati. 3) Stvorite klasu Enemy koja proširuje GameObject i može pratiti junaka. 4) Implementirajte klasu EventEmitter za obrazac pub/sub. 5) Postavite slušatelje događaja tako da kada se junak pomakne, obližnji neprijatelji prime događaj 'HERO_MOVED' i ažuriraju svoju poziciju kako bi se približili junaku. Uključite console.log izjave kako biste prikazali komunikaciju između objekata.
Saznajte više o načinu rada agenta ovdje.
🚀 Izazov
Razmislite kako obrazac pub-sub može poboljšati arhitekturu igre. Identificirajte koje komponente bi trebale emitirati događaje i kako bi sustav trebao reagirati. Osmislite koncept igre i mapirajte komunikacijske obrasce između njezinih komponenti.
Kviz nakon predavanja
Pregled i samostalno učenje
Saznajte više o Pub/Sub obrascu čitajući o njemu.
Zadatak
Odricanje od odgovornosti:
Ovaj dokument je preveden pomoću AI usluge za prevođenje Co-op Translator. Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati autoritativnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane čovjeka. Ne preuzimamo odgovornost za nesporazume ili pogrešna tumačenja koja proizlaze iz korištenja ovog prijevoda.
