~3 мин чтения

Физика Arcade

Быстрая AABB-физика для платформеров, шутеров и большинства аркадных игр — тела, столкновения, группы и подводные камни, на которые натыкаются новички.

Arcade — это быстрая система физики Phaser 4. Она моделирует всё как ограничивающие прямоугольники, выровненные по осям (AABB) или окружности — без вращения, без стопок твёрдых тел, без соединений. Взамен вы получаете систему, достаточно быструю, чтобы обсчитывать сотни тел за кадр на телефоне.

Когда использовать Arcade

Используйте Arcade, когда выполняются все условия:

  • Вам не нужна вращательная физика («наклонённый ящик лежит на склоне»).
  • Вам не нужна укладка стопкой («ящики правильно складываются в кучу»).
  • Вам не нужны соединения, ограничения или составные тела.

Подходит большинство платформеров, приключений с видом сверху, шутеров, арканоидов и казуальных головоломок. Если вы ловите себя на борьбе с Arcade ради поведения твёрдых тел, переходите на Matter — не пытайтесь это имитировать.

Включение Arcade

Задаётся в конфигурации игры:

new Phaser.Game({
	type: Phaser.AUTO,
	width: 800,
	height: 600,
	physics: {
		default: 'arcade',
		arcade: {
			gravity: { y: 300 },
			debug: false,
		},
	},
	scene: { /* ... */ },
});

Внутри сцены this.physics — это мир; this.physics.add — фабрика.

Создание тел

Два вида: динамические (двигаются, сталкиваются, реагируют на гравитацию) и статические (неподвижные; существуют для того, чтобы с ними сталкивались).

// Динамическое — имеет скорость, ускорение, гравитацию.
const player = this.physics.add.sprite(100, 100, 'player');

// Статическое — закреплено на месте. Дёшево обсчитывать столкновения с ним.
const platform = this.physics.add.staticImage(400, 568, 'ground');

Превратить обычный игровой объект в физический постфактум можно через this.physics.add.existing(obj, isStatic).

Движение

Движением управляют три параметра:

player.setVelocity(120, 0);           // пикселей в секунду
player.setAcceleration(0, 400);       // пикселей в секунду^2
player.setDrag(100);                  // затухание скорости в секунду
player.setMaxVelocity(300, 800);      // ограничение

Предпочитайте setVelocity прямому изменению x — прямая запись позиции обходит разрешение столкновений, и вы будете проходить сквозь стены.

Столкновения против пересечений

Два отношения с разной семантикой:

  • Collider — Phaser разделяет тела и не даёт им взаимно проникать друг в друга. Используйте для стен, полов, врагов, которые должны физически толкать игрока.
  • Overlap — Phaser обнаруживает пересечение, но позволяет телам проходить насквозь. Используйте для подбираемых предметов, триггеров, зон поражения.
this.physics.add.collider(player, platforms);
this.physics.add.overlap(player, coins, (_p, coin) => coin.destroy());

Оба принимают необязательный callback обработки (верните false, чтобы пропустить разрешение для этой пары) и контекст.

Группы

Группа — это типизированный пул объектов с включённой физикой. Используйте её для любого набора объектов, которые вы будете обрабатывать на столкновения как единое целое:

const enemies = this.physics.add.group({
	defaultKey: 'enemy',
	maxSize: 50,
	collideWorldBounds: true,
});

enemies.get(400, 0).setVelocityY(80);
this.physics.add.collider(enemies, platforms);
this.physics.add.overlap(player, enemies, this.onHit, undefined, this);

group.get() повторно использует неактивного участника, если такой есть, или создаёт нового вплоть до maxSize. Дёшево.

Живой пример

Три прыгающих мяча сталкиваются друг с другом и с границами мира.

Arcade collisions Phaser 4 · sandboxed

Обратите внимание на вызов setCircle — по умолчанию Arcade использует прямоугольное тело даже для круглых спрайтов. Вызов setCircle после создания переключает тело на окружность заданного радиуса. Для круглых спрайтов это почти всегда то, что нужно.

Распространённые ошибки

Туннелирование. Быстро движущееся тело может за один кадр проскочить сквозь тонкую стену. В физике Arcade нет непрерывного обнаружения столкновений, поэтому меры противодействия геометрические:

  • Делайте стены толще максимального перемещения тела за кадр (speed × delta).
  • Ограничивайте максимальную скорость (setMaxVelocity) для объектов, которым не нужно быть сколь угодно быстрыми.
  • Для пулеподобных объектов замените тело на overlap с callback обработки, который трассирует лучом путь пули каждый кадр.

Субпиксельное дрожание. Смешивание setVelocity с ручной записью x вызывает мерцание. Выберите один подход и придерживайтесь его на протяжении жизни тела.

Смещение тела против смещения спрайта. Ограничивающий прямоугольник тела может не совпадать с видимыми границами спрайта (представьте персонажа с длинным хвостом волос). Используйте body.setSize(w, h) и body.setOffset(x, y), чтобы подогнать прямоугольник.

Итерация по группе во время столкновения. Изменять группу изнутри callback столкновения (например, порождать нового участника) безопасно; итерировать группу циклом for, когда callback того же цикла её изменяет, — нет. Предпочитайте group.children.iterate(...).

Отладочная отрисовка

Установите arcade.debug: true в конфигурации, чтобы рисовать контуры тел и векторы скорости. Незаменимо при настройке столкновений.

Связанные материалы