Сбор предметов, враги и счёт
Столкнуться или пересечься
В прошлой теме collider не давал телам войти друг в друга. Но монету не нужно «отталкивать» — её
нужно подобрать. Для этого есть overlap: тела проходят друг сквозь друга, а Phaser вызывает
функцию, когда они пересеклись.
this.physics.add.overlap(this.player, this.coin, this.collect, null, this);
collect(player, coin) {
coin.destroy();
this.score += 10;
this.scoreText.setText(`Score: ${this.score}`);
}
Аргументы overlap (и collider тоже):
- первый объект или группа;
- второй объект или группа;
- функция-обработчик — получит два столкнувшихся объекта;
- функция-фильтр (
null, если не нужна) — может вернутьfalse, чтобы пропустить столкновение; - контекст — что будет
thisвнутри обработчика. Передаёмthis(сцену), иначе внутри методаthis.scoreне найдётся.
Можно и стрелкой — тогда this берётся из create сам:
this.physics.add.overlap(this.player, this.coins, (player, coin) => {
coin.destroy();
this.score += 10;
});
Группы предметов
Монет, врагов, пуль много, и все ведут себя одинаково. Их собирают в группу и проверяют столкновения сразу со всей группой:
this.coins = this.physics.add.group({ allowGravity: false });
for (let i = 0; i < 6; i++) {
this.coins.add(this.add.circle(50 + i * 60, 250, 8, 0xffd54f));
}
this.physics.add.overlap(this.player, this.coins, this.collect, null, this);
Обработчик получит конкретную монету из группы, которую задел игрок. Полезные методы групп:
this.coins.getChildren(); // массив объектов
this.coins.countActive(); // сколько ещё активных
this.coins.clear(true, true); // удалить все
Победа и поражение
Условия конца игры проверяют там, где меняется состояние, — прямо в обработчике:
if (this.coins.countActive() === 0) {
this.win = true;
this.add.text(200, 150, "You win!").setOrigin(0.5);
}
Остановить игру можно, поставив физику на паузу: this.physics.pause() — все тела замрут.
Перезапуск сцены — this.scene.restart() (подробнее в следующей теме).
Враги: сверху или сбоку
Классика платформеров: прыгнул врагу на голову — он побеждён, врезался сбоку — теряешь жизнь. Отличить одно от другого помогают свойства тела:
body.velocity.y > 0— игрок падает (а не взлетает);body.bottom— нижний край тела игрока,body.top— верхний край врага.
hitEnemy(player, enemy) {
const stomp = player.body.velocity.y > 0 && player.body.bottom <= enemy.body.top + 8;
if (stomp) {
enemy.destroy();
player.body.setVelocityY(-250); // отскок от врага
this.score += 50;
} else {
this.lives -= 1;
player.body.reset(50, 200); // на старт с нулевой скоростью
}
}
Запас в 8 пикселей нужен потому, что за один кадр игрок успевает немного «войти» во врага, — без запаса прыжок сверху иногда засчитывался бы как удар сбоку.
Враги ходят сами
Самый простой враг ходит туда-сюда: скорость по x и полный отскок от стен.
const enemy = this.add.rectangle(200, 265, 30, 30, 0xe53935);
this.enemies.add(enemy);
enemy.body.setVelocityX(-60).setBounce(1, 0).setCollideWorldBounds(true);
Методы тела возвращают само тело, поэтому их можно писать цепочкой.
Итог
overlap(a, b, обработчик, null, this)— пересечение без отталкивания;collider— с отталкиванием.- Группы: одна проверка столкновений на все объекты,
countActive()— сколько осталось. - Конец игры — флаг +
this.physics.pause(). - Прыжок на врага:
velocity.y > 0иbody.bottom <= enemy.body.top + запас.