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/cs/6-space-game/1-introduction/README.md

17 KiB

Vytvořte vesmírnou hru, část 1: Úvod

Animace vesmírné hry ukazující hratelnost

Stejně jako řídící středisko NASA koordinuje různé systémy během vesmírného startu, vytvoříme vesmírnou hru, která ukáže, jak různé části programu mohou spolupracovat bez problémů. Při tvorbě něčeho, co si skutečně můžete zahrát, se naučíte základní programovací koncepty, které se uplatní v každém softwarovém projektu.

Prozkoumáme dva základní přístupy k organizaci kódu: dědičnost a kompozici. Nejde jen o akademické koncepty jsou to stejné vzory, které pohánějí vše od videoher po bankovní systémy. Také implementujeme komunikační systém nazvaný pub/sub, který funguje jako komunikační sítě používané ve vesmírných lodích, umožňující různým komponentám sdílet informace bez vytváření závislostí.

Na konci této série pochopíte, jak vytvářet aplikace, které mohou růst a vyvíjet se ať už vyvíjíte hry, webové aplikace nebo jakýkoli jiný softwarový systém.

Kvíz před přednáškou

Kvíz před přednáškou

Dědičnost a kompozice ve vývoji her

Jak projekty rostou na složitosti, organizace kódu se stává klíčovou. Co začíná jako jednoduchý skript, se může stát obtížně udržovatelným bez správné struktury podobně jako mise Apollo vyžadovaly pečlivou koordinaci mezi tisíci komponentami.

Prozkoumáme dva základní přístupy k organizaci kódu: dědičnost a kompozici. Každý má své specifické výhody a pochopení obou vám pomůže vybrat správný přístup pro různé situace. Tyto koncepty ukážeme prostřednictvím naší vesmírné hry, kde hrdinové, nepřátelé, power-upy a další objekty musí efektivně spolupracovat.

Jedna z nejslavnějších programovacích knih vůbec se týká designových vzorů.

V každé hře máte herní objekty interaktivní prvky, které obývají váš herní svět. Hrdinové, nepřátelé, power-upy a vizuální efekty jsou všechny herní objekty. Každý z nich existuje na konkrétních souřadnicích obrazovky pomocí hodnot x a y, podobně jako při vykreslování bodů na souřadnicové rovině.

Navzdory jejich vizuálním rozdílům tyto objekty často sdílejí základní chování:

  • Existují někde Každý objekt má souřadnice x a y, aby hra věděla, kde ho vykreslit
  • Mnohé se mohou pohybovat Hrdinové běhají, nepřátelé pronásledují, střely létají po obrazovce
  • Mají životnost Některé zůstávají navždy, jiné (jako exploze) se objeví krátce a zmizí
  • Reagují na věci Když se věci srazí, power-upy se sbírají, ukazatele zdraví se aktualizují

Zamyslete se nad hrou jako Pac-Man. Dokážete identifikovat čtyři typy objektů uvedené výše v této hře?

Vyjádření chování prostřednictvím kódu

Nyní, když rozumíte běžnému chování, které herní objekty sdílejí, pojďme prozkoumat, jak toto chování implementovat v JavaScriptu. Chování objektů můžete vyjádřit prostřednictvím metod připojených buď ke třídám, nebo jednotlivým objektům, a existuje několik přístupů, ze kterých si můžete vybrat.

Přístup založený na třídách

Třídy a dědičnost poskytují strukturovaný přístup k organizaci herních objektů. Podobně jako taxonomický klasifikační systém vyvinutý Carlem Linnéem začínáte základní třídou obsahující běžné vlastnosti, poté vytváříte specializované třídy, které tyto základy dědí a přidávají specifické schopnosti.

Dědičnost je důležitý koncept, který je třeba pochopit. Více se dozvíte v článku MDN o dědičnosti.

Takto můžete implementovat herní objekty pomocí tříd a dědičnosti:

// Step 1: Create the base GameObject class
class GameObject {
  constructor(x, y, type) {
    this.x = x;
    this.y = y;
    this.type = type;
  }
}

Rozložme si to krok za krokem:

  • Vytváříme základní šablonu, kterou může použít každý herní objekt
  • Konstruktor ukládá, kde se objekt nachází (x, y) a jaký typ věci to je
  • Toto se stává základem, na kterém budou stavět všechny vaše herní objekty
// 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;
  }
}

V uvedeném jsme:

  • Rozšířili třídu GameObject, abychom přidali funkci pohybu
  • Zavolali rodičovský konstruktor pomocí super(), abychom inicializovali zděděné vlastnosti
  • Přidali metodu moveTo(), která aktualizuje pozici objektu
// 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

Pochopení těchto konceptů:

  • Vytváří specializované typy objektů, které dědí vhodné chování
  • Ukazuje, jak dědičnost umožňuje selektivní zahrnutí funkcí
  • Ukazuje, že hrdinové se mohou pohybovat, zatímco stromy zůstávají nehybné
  • Ilustruje, jak hierarchie tříd zabraňuje nevhodným akcím

Věnujte pár minut přemýšlení o hrdinovi z Pac-Mana (například Inky, Pinky nebo Blinky) a o tom, jak by byl napsán v JavaScriptu.

Přístup založený na kompozici

Kompozice následuje modulární designovou filozofii, podobně jako inženýři navrhují vesmírné lodě s vyměnitelnými komponenty. Místo dědění z rodičovské třídy kombinujete specifická chování, abyste vytvořili objekty s přesně tou funkcionalitou, kterou potřebují. Tento přístup nabízí flexibilitu bez rigidních hierarchických omezení.

// Step 1: Create base behavior objects
const gameObject = {
  x: 0,
  y: 0,
  type: ''
};

const movable = {
  moveTo(x, y) {
    this.x = x;
    this.y = y;
  }
};

Co tento kód dělá:

  • Definuje základní gameObject s vlastnostmi pozice a typu
  • Vytváří samostatný objekt chování movable s funkcí pohybu
  • Odděluje záležitosti tím, že udržuje data o pozici a logiku pohybu nezávislé
// 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
  };
}

V uvedeném jsme:

  • Kombinovali základní vlastnosti objektu s chováním pohybu pomocí syntaxe spread
  • Vytvořili tovární funkce, které vracejí přizpůsobené objekty
  • Umožnili flexibilní tvorbu objektů bez rigidních hierarchií tříd
  • Umožnili, aby objekty měly přesně ta chování, která potřebují
// 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

Klíčové body k zapamatování:

  • Kombinuje objekty mícháním chování místo jejich dědění
  • Poskytuje větší flexibilitu než rigidní hierarchie dědičnosti
  • Umožňuje, aby objekty měly přesně ty funkce, které potřebují
  • Používá moderní syntaxi spread v JavaScriptu pro čistou kombinaci objektů

**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);
      });
    }
  }
}

Rozložení toho, co se zde děje:

  • Vytváří centrální systém správy událostí pomocí jednoduché třídy
  • Ukládá posluchače do objektu organizovaného podle typu zprávy
  • Registruje nové posluchače pomocí metody on()
  • Vysílá zprávy všem zainteresovaným posluchačům pomocí emit()
  • Podporuje volitelné datové balíčky pro předávání relevantních informací

Spojení všeho dohromady: Praktický příklad

Dobře, podívejme se na to v praxi! Vytvoříme jednoduchý systém pohybu, který ukáže, jak čistý a flexibilní může být pub/sub:

// 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);

Co tento kód dělá:

  • Definuje objekt konstant, aby se zabránilo překlepům v názvech zpráv
  • Vytváří instanci emitera událostí pro zpracování veškeré komunikace
  • Inicializuje objekt hrdiny na výchozí pozici
// 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}`);
});

V uvedeném jsme:

  • Registrovali posluchače událostí, kteří reagují na zprávy o pohybu
  • Aktualizovali pozici hrdiny na základě směru pohybu
  • Přidali logování do konzole pro sledování změn pozice hrdiny
  • Oddělili logiku pohybu od zpracování vstupu
// 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;
  }
});

Pochopení těchto konceptů:

  • Spojuje vstup z klávesnice s herními událostmi bez těsného propojení
  • Umožňuje, aby systém vstupu komunikoval s herními objekty nepřímo
  • Umožňuje, aby více systémů reagovalo na stejné události z klávesnice
  • Usnadňuje změnu klávesových vazeb nebo přidání nových metod vstupu

💡 Tip: Krása tohoto vzoru spočívá ve flexibilitě! Můžete snadno přidat zvukové efekty, otřesy obrazovky nebo částicové efekty jednoduše přidáním dalších posluchačů událostí není třeba upravovat stávající kód pro klávesnici nebo pohyb.

Proč si tento přístup zamilujete:

  • Přidávání nových funkcí je velmi snadné stačí poslouchat události, které vás zajímají
  • Více věcí může reagovat na stejnou událost, aniž by si navzájem překážely
  • Testování je mnohem jednodušší, protože každá část funguje nezávisle
  • Když se něco pokazí, přesně víte, kde hledat problém

Proč je pub/sub efektivní při škálování

Vzor pub/sub udržuje jednoduchost, i když aplikace rostou na složitosti. Ať už spravujete desítky nepřátel, dynamické aktualizace uživatelského rozhraní nebo zvukové systémy, tento vzor zvládá zvýšené nároky bez nutnosti změn v architektuře. Nové funkce se integrují do stávajícího systému událostí, aniž by ovlivnily již zavedenou funkcionalitu.

⚠️ Častá chyba: Nevytvářejte příliš mnoho specifických typů zpráv hned na začátku. Začněte se širokými kategoriemi a upravujte je podle potřeb vaší hry.

Doporučené postupy:

  • Skupiny souvisejících zpráv do logických kategorií
  • Používání popisných názvů, které jasně naznačují, co se stalo
  • Udržování datových balíčků jednoduchých a zaměřených
  • Dokumentování typů zpráv pro spolupráci v týmu

Výzva GitHub Copilot Agent 🚀

Použijte režim Agent k dokončení následující výzvy:

Popis: Vytvořte jednoduchý systém herních objektů pomocí dědičnosti a vzoru pub/sub. Implementujte základní hru, kde různé objekty mohou komunikovat prostřednictvím událostí, aniž by o sobě přímo věděly.

Zadání: Vytvořte systém herních objektů v JavaScriptu s následujícími požadavky: 1) Vytvořte základní třídu GameObject s souřadnicemi x, y a vlastností typu. 2) Vytvořte třídu Hero, která rozšiřuje GameObject a může se pohybovat. 3) Vytvořte třídu Enemy, která rozšiřuje GameObject a může pronásledovat hrdinu. 4) Implementujte třídu EventEmitter pro vzor pub/sub. 5) Nastavte posluchače událostí tak, aby při pohybu hrdiny blízcí nepřátelé obdrželi událost 'HERO_MOVED' a aktualizovali svou pozici, aby se přiblížili k hrdinovi. Zahrňte příkazy console.log, které ukazují komunikaci mezi objekty.

Více o režimu agent se dozvíte zde.

🚀 Výzva

Zvažte, jak může vzor pub-sub zlepšit architekturu hry. Identifikujte, které komponenty by měly vysílat události a jak by měl systém reagovat. Navrhněte koncept hry a zmapujte komunikační vzory mezi jejími komponentami.

Kvíz po přednášce

Kvíz po přednášce

Přehled & Samostudium

Zjistěte více o Pub/Sub čtením o něm.

Zadání

Navrhněte hru


Prohlášení:
Tento dokument byl přeložen pomocí služby AI pro překlady Co-op Translator. Ačkoli se snažíme o přesnost, mějte prosím na paměti, že automatizované překlady mohou obsahovat chyby nebo nepřesnosti. Původní dokument v jeho původním jazyce by měl být považován za autoritativní zdroj. Pro důležité informace se doporučuje profesionální lidský překlad. Neodpovídáme za žádná nedorozumění nebo nesprávné interpretace vyplývající z použití tohoto překladu.