# Направите свемирску игру, део 4: Додавање ласера и детекција судара ## Квиз пре предавања [Квиз пре предавања](https://ff-quizzes.netlify.app/web/quiz/35) У овој лекцији ћете научити како да пуцате ласере помоћу JavaScript-а! Додаћемо две ствари у нашу игру: - **Ласер**: овај ласер се испаљује из херојског брода и креће вертикално нагоре - **Детекција судара**, као део имплементације способности *пуцања*, додаћемо нека занимљива правила игре: - **Ласер погоди непријатеља**: Непријатељ умире ако га погоди ласер - **Ласер погоди врх екрана**: Ласер се уништава ако погоди горњи део екрана - **Судар непријатеља и хероја**: Непријатељ и херој се уништавају ако се сударе - **Непријатељ погоди дно екрана**: Непријатељ и херој се уништавају ако непријатељ стигне до дна екрана Укратко, ви -- *херој* -- морате погодити све непријатеље ласером пре него што стигну до дна екрана. ✅ Урадите мало истраживање о првој рачунарској игри икада написаној. Каква је била њена функционалност? Хајде да будемо херојски заједно! ## Детекција судара Како да извршимо детекцију судара? Морамо размишљати о нашим објектима у игри као о правоугаоницима који се крећу. Зашто, питате се? Па, слика која се користи за цртање објекта у игри је правоугаоник: има `x`, `y`, `ширину` и `висину`. Ако се два правоугаоника, тј. херој и непријатељ *пресеку*, имате судар. Шта би требало да се догоди након тога зависи од правила игре. Да бисте имплементирали детекцију судара, потребно је следеће: 1. Начин да добијете правоугаоник који представља објекат игре, нешто овако: ```javascript rectFromGameObject() { return { top: this.y, left: this.x, bottom: this.y + this.height, right: this.x + this.width } } ``` 2. Функцију за поређење, која може изгледати овако: ```javascript function intersectRect(r1, r2) { return !(r2.left > r1.right || r2.right < r1.left || r2.top > r1.bottom || r2.bottom < r1.top); } ``` ## Како уништавамо ствари Да бисте уништили ствари у игри, морате обавестити игру да више не треба да црта тај предмет у петљи игре која се покреће у одређеним интервалима. Један начин да то урадите је да означите објекат игре као *мртав* када се нешто догоди, овако: ```javascript // collision happened enemy.dead = true ``` Затим можете издвојити *мртве* објекте пре него што поново нацртате екран, овако: ```javascript gameObjects = gameObject.filter(go => !go.dead); ``` ## Како испаљујемо ласер Испаљивање ласера подразумева реаговање на догађај притискања тастера и креирање објекта који се креће у одређеном правцу. Због тога треба да извршите следеће кораке: 1. **Креирајте ласерски објекат**: из врха херојског брода, који по креирању почиње да се креће нагоре ка врху екрана. 2. **Повежите код са догађајем притискања тастера**: треба да изаберемо тастер на тастатури који представља пуцање ласера. 3. **Креирајте објекат игре који изгледа као ласер** када се притисне тастер. ## Време чекања за ласер Ласер треба да се испаљује сваки пут када притиснете тастер, на пример *space*. Да бисмо спречили игру да производи превише ласера у кратком времену, морамо то поправити. Поправка се врши имплементацијом такозваног *времена чекања*, тајмера, који осигурава да ласер може бити испаљен само у одређеним интервалима. То можете имплементирати на следећи начин: ```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. } } } ``` ✅ Погледајте лекцију 1 у серији свемирских игара да се подсетите на *време чекања*. ## Шта треба направити Узећете постојећи код (који сте требали очистити и рефакторисати) из претходне лекције и проширити га. Можете почети са кодом из дела II или користити код из [Део III - почетак](../../../../../../../../../your-work). > савет: ласер са којим ћете радити већ се налази у вашој фасцикли са ресурсима и референциран је у вашем коду - **Додајте детекцију судара**, када ласер удари нешто, треба да се примењују следећа правила: 1. **Ласер погоди непријатеља**: непријатељ умире ако га погоди ласер 2. **Ласер погоди врх екрана**: ласер се уништава ако погоди горњи део нашег екрана 3. **Судар непријатеља и хероја**: непријатељ и херој се уништавају ако се сударе 4. **Непријатељ погоди дно екрана**: непријатељ и херој се уништавају ако непријатељ стигне до дна екрана ## Препоручени кораци Пронађите датотеке које су креиране за вас у подфасцикли `your-work`. Требало би да садрже следеће: ```bash -| assets -| enemyShip.png -| player.png -| laserRed.png -| index.html -| app.js -| package.json ``` Започните свој пројекат у фасцикли `your_work` тако што ћете укуцати: ```bash cd your-work npm start ``` Горенаведено ће покренути HTTP сервер на адреси `http://localhost:5000`. Отворите прегледач и унесите ту адресу, тренутно би требало да приказује хероја и све непријатеље, ништа се још не помера :). ### Додајте код 1. **Подесите правоугаоник који представља ваш објекат игре, за обраду судара** Следећи код вам омогућава да добијете правоугаоник који представља `GameObject`. Уредите вашу GameObject класу да је проширите: ```javascript rectFromGameObject() { return { top: this.y, left: this.x, bottom: this.y + this.height, right: this.x + this.width, }; } ``` 2. **Додајте код који проверава судар** Ово ће бити нова функција која тестира да ли се два правоугаоника пресеку: ```javascript function intersectRect(r1, r2) { return !( r2.left > r1.right || r2.right < r1.left || r2.top > r1.bottom || r2.bottom < r1.top ); } ``` 3. **Додајте способност испаљивања ласера** 1. **Додајте поруку за догађај притискања тастера**. Тастер *space* треба да креира ласер непосредно изнад херојског брода. Додајте три константе у Messages објекат: ```javascript KEY_EVENT_SPACE: "KEY_EVENT_SPACE", COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER", COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO", ``` 1. **Обрадите тастер space**. Уредите функцију `window.addEventListener` за keyup да обради тастер space: ```javascript } else if(evt.keyCode === 32) { eventEmitter.emit(Messages.KEY_EVENT_SPACE); } ``` 1. **Додајте слушаоце**. Уредите функцију `initGame()` да осигурате да херој може пуцати када се притисне тастер space: ```javascript eventEmitter.on(Messages.KEY_EVENT_SPACE, () => { if (hero.canFire()) { hero.fire(); } ``` и додајте нову `eventEmitter.on()` функцију да осигурате понашање када непријатељ удари ласер: ```javascript eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => { first.dead = true; second.dead = true; }) ``` 1. **Померајте објекат**, Осигурајте да ласер постепено иде ка врху екрана. Креираћете нову Laser класу која проширује `GameObject`, као што сте већ радили: ```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. **Обрадите сударе**, Имплементирајте правила судара за ласер. Додајте функцију `updateGameObjects()` која тестира објекте који се сударају: ```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); } ``` Осигурајте да додате `updateGameObjects()` у вашу петљу игре у `window.onload`. 4. **Имплементирајте време чекања** за ласер, тако да може бити испаљен само у одређеним интервалима. На крају, уредите Hero класу тако да може да има време чекања: ```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; } } ``` У овом тренутку, ваша игра има неку функционалност! Можете се кретати стрелицама, пуцати ласером помоћу тастера space, а непријатељи нестају када их погодите. Браво! --- ## 🚀 Изазов Додајте експлозију! Погледајте ресурсе игре у [репозиторијуму Space Art](../../../../6-space-game/solution/spaceArt/readme.txt) и покушајте да додате експлозију када ласер погоди ванземаљца. ## Квиз после предавања [Квиз после предавања](https://ff-quizzes.netlify.app/web/quiz/36) ## Преглед и самостално учење Експериментишите са интервалима у вашој игри до сада. Шта се дешава када их промените? Прочитајте више о [JavaScript временским догађајима](https://www.freecodecamp.org/news/javascript-timing-events-settimeout-and-setinterval/). ## Задатак [Истражите сударе](assignment.md) --- **Одрицање од одговорности**: Овај документ је преведен коришћењем услуге за превођење помоћу вештачке интелигенције [Co-op Translator](https://github.com/Azure/co-op-translator). Иако настојимо да обезбедимо тачност, молимо вас да имате у виду да аутоматски преводи могу садржати грешке или нетачности. Оригинални документ на његовом изворном језику треба сматрати меродавним извором. За критичне информације препоручује се професионални превод од стране људи. Не преузимамо одговорност за било каква погрешна тумачења или неспоразуме који могу настати услед коришћења овог превода.