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/ru/6-space-game/4-collision-detection/README.md

309 lines
17 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.

<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "2e83e38c35dc003f046d7cc0bbfd4920",
"translation_date": "2025-08-25T22:21:01+00:00",
"source_file": "6-space-game/4-collision-detection/README.md",
"language_code": "ru"
}
-->
# Создание космической игры, часть 4: добавление лазера и обнаружение столкновений
## Викторина перед лекцией
[Викторина перед лекцией](https://ff-quizzes.netlify.app/web/quiz/35)
В этом уроке вы научитесь стрелять лазерами с помощью JavaScript! Мы добавим две вещи в нашу игру:
- **Лазер**: этот лазер выстреливается из корабля героя вертикально вверх.
- **Обнаружение столкновений**: в рамках реализации возможности *стрелять* мы также добавим несколько интересных игровых правил:
- **Лазер попадает в врага**: враг уничтожается, если его задевает лазер.
- **Лазер достигает верхней части экрана**: лазер уничтожается, если он достигает верхней части экрана.
- **Столкновение врага и героя**: враг и герой уничтожаются, если сталкиваются друг с другом.
- **Враг достигает нижней части экрана**: враг и герой уничтожаются, если враг достигает нижней части экрана.
Короче говоря, вы — *герой* — должны уничтожить всех врагов лазером, прежде чем они доберутся до нижней части экрана.
✅ Проведите небольшое исследование о самой первой компьютерной игре, которая была написана. Какова была её функциональность?
Давайте будем героями вместе!
## Обнаружение столкновений
Как мы можем обнаруживать столкновения? Нам нужно представить игровые объекты как прямоугольники, которые движутся. Почему, спросите вы? Потому что изображение, используемое для отображения игрового объекта, является прямоугольником: у него есть `x`, `y`, `width` и `height`.
Если два прямоугольника, например, герой и враг, *пересекаются*, происходит столкновение. Что должно произойти в этом случае, зависит от правил игры. Чтобы реализовать обнаружение столкновений, вам нужно следующее:
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. **Создать игровой объект, который выглядит как лазер**, когда клавиша нажата.
## Задержка стрельбы лазером
Лазер должен выстреливаться каждый раз, когда вы нажимаете клавишу, например, *пробел*. Чтобы предотвратить создание слишком большого количества лазеров за короткое время, нужно это исправить. Решение реализовать так называемую *задержку*, таймер, который гарантирует, что лазер может выстреливаться только через определённые интервалы. Это можно сделать следующим образом:
```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, либо используйте код из [Part III- starter](../../../../../../../../../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. **Добавьте сообщение о событии нажатия клавиши**. Клавиша *пробел* должна создавать лазер прямо над кораблём героя. Добавьте три константы в объект Messages:
```javascript
KEY_EVENT_SPACE: "KEY_EVENT_SPACE",
COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER",
COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",
```
1. **Обработайте клавишу пробел**. Измените функцию `window.addEventListener` для обработки нажатия пробела:
```javascript
} else if(evt.keyCode === 32) {
eventEmitter.emit(Messages.KEY_EVENT_SPACE);
}
```
1. **Добавьте слушатели событий**. Измените функцию `initGame()`, чтобы герой мог стрелять при нажатии пробела:
```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 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). Несмотря на наши усилия обеспечить точность, автоматические переводы могут содержать ошибки или неточности. Оригинальный документ на его родном языке следует считать авторитетным источником. Для получения критически важной информации рекомендуется профессиональный перевод человеком. Мы не несем ответственности за любые недоразумения или неправильные интерпретации, возникающие в результате использования данного перевода.