# Vytvorenie vesmírnej hry, časť 4: Pridanie lasera a detekcia kolízií ## Kvíz pred prednáškou [Kvíz pred prednáškou](https://ff-quizzes.netlify.app/web/quiz/35) V tejto lekcii sa naučíte, ako strieľať lasery pomocou JavaScriptu! Do našej hry pridáme dve veci: - **Laser**: tento laser sa vystrelí z lode hrdinu a pohybuje sa vertikálne nahor - **Detekcia kolízií**, ako súčasť implementácie schopnosti *strieľať*, pridáme aj niekoľko pravidiel hry: - **Laser zasiahne nepriateľa**: Nepriateľ zomrie, ak ho zasiahne laser - **Laser zasiahne hornú časť obrazovky**: Laser sa zničí, ak zasiahne hornú časť obrazovky - **Kolízia nepriateľa a hrdinu**: Nepriateľ aj hrdina sa zničia, ak sa zrazia - **Nepriateľ zasiahne spodnú časť obrazovky**: Nepriateľ aj hrdina sa zničia, ak nepriateľ zasiahne spodnú časť obrazovky Stručne povedané, vy -- *hrdina* -- musíte zasiahnuť všetkých nepriateľov laserom skôr, než sa dostanú na spodnú časť obrazovky. ✅ Urobte si malý prieskum o úplne prvej počítačovej hre, ktorá bola kedy napísaná. Aká bola jej funkčnosť? Buďme hrdinami spolu! ## Detekcia kolízií Ako vykonáme detekciu kolízií? Musíme si predstaviť naše herné objekty ako obdĺžniky, ktoré sa pohybujú. Prečo? Pretože obrázok použitý na vykreslenie herného objektu je obdĺžnik: má `x`, `y`, `šírku` a `výšku`. Ak sa dva obdĺžniky, napríklad hrdina a nepriateľ, *pretínajú*, máte kolíziu. Čo by sa malo stať, závisí od pravidiel hry. Na implementáciu detekcie kolízií potrebujete nasledovné: 1. Spôsob, ako získať obdĺžnikovú reprezentáciu herného objektu, niečo takéto: ```javascript rectFromGameObject() { return { top: this.y, left: this.x, bottom: this.y + this.height, right: this.x + this.width } } ``` 2. Porovnávaciu funkciu, ktorá môže vyzerať takto: ```javascript function intersectRect(r1, r2) { return !(r2.left > r1.right || r2.right < r1.left || r2.top > r1.bottom || r2.bottom < r1.top); } ``` ## Ako ničíme objekty Na zničenie objektov v hre musíte dať hre vedieť, že tento objekt už nemá byť vykreslený v hernej slučke, ktorá sa spúšťa v určitých intervaloch. Spôsob, ako to urobiť, je označiť herný objekt ako *mŕtvy*, keď sa niečo stane, napríklad takto: ```javascript // collision happened enemy.dead = true ``` Potom môžete vyradiť *mŕtve* objekty pred opätovným vykreslením obrazovky, napríklad takto: ```javascript gameObjects = gameObject.filter(go => !go.dead); ``` ## Ako vystreliť laser Vystrelenie lasera znamená reagovať na udalosť stlačenia klávesy a vytvoriť objekt, ktorý sa pohybuje určitým smerom. Preto musíme vykonať nasledujúce kroky: 1. **Vytvoriť objekt lasera**: z vrchnej časti lode hrdinu, ktorý sa po vytvorení začne pohybovať nahor smerom k hornej časti obrazovky. 2. **Pripojiť kód k udalosti stlačenia klávesy**: musíme vybrať kláves na klávesnici, ktorý bude predstavovať hráča strieľajúceho laser. 3. **Vytvoriť herný objekt, ktorý vyzerá ako laser**, keď je kláves stlačený. ## Časový odstup pre laser Laser musí vystreliť vždy, keď stlačíte kláves, napríklad *medzerník*. Aby sme zabránili hre vytvárať príliš veľa laserov v krátkom čase, musíme to opraviť. Oprava spočíva v implementácii tzv. *časového odstupu*, časovača, ktorý zabezpečí, že laser môže byť vystrelený len v určitých intervaloch. Môžete to implementovať nasledovne: ```javascript class Cooldown { constructor(time) { this.cool = false; setTimeout(() => { this.cool = true; }, time) } } class Weapon { constructor { } fire() { if (!this.cooldown || this.cooldown.cool) { // produce a laser this.cooldown = new Cooldown(500); } else { // do nothing - it hasn't cooled down yet. } } } ``` ✅ Pozrite si lekciu 1 zo série vesmírnych hier, aby ste si pripomenuli *časové odstupy*. ## Čo vytvoriť Vezmete existujúci kód (ktorý by ste mali vyčistiť a refaktorovať) z predchádzajúcej lekcie a rozšírite ho. Buď začnite s kódom z časti II, alebo použite kód na [Časť III - štartovací](../../../../6-space-game/4-collision-detection/your-work). > tip: laser, s ktorým budete pracovať, je už vo vašom priečinku s prostriedkami a je referencovaný vaším kódom - **Pridajte detekciu kolízií**, keď laser narazí na niečo, mali by platiť nasledujúce pravidlá: 1. **Laser zasiahne nepriateľa**: nepriateľ zomrie, ak ho zasiahne laser 2. **Laser zasiahne hornú časť obrazovky**: laser sa zničí, ak zasiahne hornú časť obrazovky 3. **Kolízia nepriateľa a hrdinu**: nepriateľ aj hrdina sa zničia, ak sa zrazia 4. **Nepriateľ zasiahne spodnú časť obrazovky**: nepriateľ aj hrdina sa zničia, ak nepriateľ zasiahne spodnú časť obrazovky ## Odporúčané kroky Vyhľadajte súbory, ktoré boli pre vás vytvorené v podpriečinku `your-work`. Mali by obsahovať nasledovné: ```bash -| assets -| enemyShip.png -| player.png -| laserRed.png -| index.html -| app.js -| package.json ``` Svoj projekt spustíte v priečinku `your_work` zadaním: ```bash cd your-work npm start ``` Vyššie uvedené spustí HTTP server na adrese `http://localhost:5000`. Otvorte prehliadač a zadajte túto adresu, momentálne by sa mal zobraziť hrdina a všetci nepriatelia, nič sa však ešte nehýbe :). ### Pridajte kód 1. **Nastavte obdĺžnikovú reprezentáciu vášho herného objektu na spracovanie kolízií** Nasledujúci kód umožňuje získať obdĺžnikovú reprezentáciu `GameObject`. Upraviť triedu GameObject tak, aby ju rozšírila: ```javascript rectFromGameObject() { return { top: this.y, left: this.x, bottom: this.y + this.height, right: this.x + this.width, }; } ``` 2. **Pridajte kód, ktorý kontroluje kolízie** Toto bude nová funkcia, ktorá testuje, či sa dva obdĺžniky pretínajú: ```javascript function intersectRect(r1, r2) { return !( r2.left > r1.right || r2.right < r1.left || r2.top > r1.bottom || r2.bottom < r1.top ); } ``` 3. **Pridajte schopnosť vystreliť laser** 1. **Pridajte správu o udalosti stlačenia klávesy**. Kláves *medzerník* by mal vytvoriť laser tesne nad loďou hrdinu. Pridajte tri konštanty do objektu Messages: ```javascript KEY_EVENT_SPACE: "KEY_EVENT_SPACE", COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER", COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO", ``` 1. **Spracujte kláves medzerník**. Upraviť funkciu `window.addEventListener` pre stlačenie klávesy, aby spracovala medzerník: ```javascript } else if(evt.keyCode === 32) { eventEmitter.emit(Messages.KEY_EVENT_SPACE); } ``` 1. **Pridajte poslucháčov**. Upraviť funkciu `initGame()`, aby hrdina mohol strieľať, keď je stlačený medzerník: ```javascript eventEmitter.on(Messages.KEY_EVENT_SPACE, () => { if (hero.canFire()) { hero.fire(); } ``` a pridajte novú funkciu `eventEmitter.on()`, aby ste zabezpečili správanie, keď nepriateľ narazí na laser: ```javascript eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => { first.dead = true; second.dead = true; }) ``` 1. **Pohyb objektu**, Zabezpečte, aby sa laser postupne pohyboval k hornej časti obrazovky. Vytvoríte novú triedu Laser, ktorá rozširuje `GameObject`, ako ste to už urobili predtým: ```javascript class Laser extends GameObject { constructor(x, y) { super(x,y); (this.width = 9), (this.height = 33); this.type = 'Laser'; this.img = laserImg; let id = setInterval(() => { if (this.y > 0) { this.y -= 15; } else { this.dead = true; clearInterval(id); } }, 100) } } ``` 1. **Spracujte kolízie**, Implementujte pravidlá kolízií pre laser. Pridajte funkciu `updateGameObjects()`, ktorá testuje objekty na kolízie: ```javascript function updateGameObjects() { const enemies = gameObjects.filter(go => go.type === 'Enemy'); const lasers = gameObjects.filter((go) => go.type === "Laser"); // laser hit something lasers.forEach((l) => { enemies.forEach((m) => { if (intersectRect(l.rectFromGameObject(), m.rectFromGameObject())) { eventEmitter.emit(Messages.COLLISION_ENEMY_LASER, { first: l, second: m, }); } }); }); gameObjects = gameObjects.filter(go => !go.dead); } ``` Uistite sa, že ste pridali `updateGameObjects()` do hernej slučky v `window.onload`. 4. **Implementujte časový odstup** pre laser, aby mohol byť vystrelený len v určitých intervaloch. Nakoniec upravte triedu Hero tak, aby mala časový odstup: ```javascript class Hero extends GameObject { constructor(x, y) { super(x, y); (this.width = 99), (this.height = 75); this.type = "Hero"; this.speed = { x: 0, y: 0 }; this.cooldown = 0; } fire() { gameObjects.push(new Laser(this.x + 45, this.y - 10)); this.cooldown = 500; let id = setInterval(() => { if (this.cooldown > 0) { this.cooldown -= 100; } else { clearInterval(id); } }, 200); } canFire() { return this.cooldown === 0; } } ``` V tomto bode má vaša hra určitú funkčnosť! Môžete sa pohybovať pomocou šípok, strieľať laser pomocou medzerníka a nepriatelia zmiznú, keď ich zasiahnete. Skvelá práca! --- ## 🚀 Výzva Pridajte explóziu! Pozrite sa na herné prostriedky v [repozitári Space Art](../../../../6-space-game/solution/spaceArt/readme.txt) a skúste pridať explóziu, keď laser zasiahne mimozemšťana. ## Kvíz po prednáške [Kvíz po prednáške](https://ff-quizzes.netlify.app/web/quiz/36) ## Prehľad a samostatné štúdium Experimentujte s intervalmi vo vašej hre doteraz. Čo sa stane, keď ich zmeníte? Prečítajte si viac o [časových udalostiach v JavaScripte](https://www.freecodecamp.org/news/javascript-timing-events-settimeout-and-setinterval/). ## Zadanie [Preskúmajte kolízie](assignment.md) --- **Upozornenie**: Tento dokument bol preložený pomocou služby na automatický preklad [Co-op Translator](https://github.com/Azure/co-op-translator). Hoci sa snažíme o presnosť, upozorňujeme, že automatické preklady môžu obsahovať chyby alebo nepresnosti. Pôvodný dokument v jeho pôvodnom jazyku by mal byť považovaný za autoritatívny zdroj. Pre kritické informácie sa odporúča profesionálny ľudský preklad. Nezodpovedáme za akékoľvek nedorozumenia alebo nesprávne interpretácie vyplývajúce z použitia tohto prekladu.