О чем этот пример
Движок Arcade Physics в Phaser предоставляет базовые и эффективные алгоритмы для обработки столкновений. Но что, если стандартное поведение «отталкивания» не подходит для вашей игровой механики? В этой статье мы разберем пример, где отключается встроенный физический расчет по вертикали и реализуется своя логика взаимодействия объектов. Это полезно для создания нестандартных симуляций, например, падающих блоков, которые не отскакивают, а «укладываются» друг на друга, или для любых сценариев, где нужно полное управление процессом коллизии.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('crate', 'assets/sprites/crate32.png');
}
create ()
{
this.physics.world.checkCollision.up = false;
const group = this.physics.add.group({
bounceY: 0.5,
collideWorldBounds: true,
dragY: 30,
frameQuantity: 18,
key: 'crate',
setXY: { x: 200, y: 0, stepX: 16, stepY: -64 },
velocityY: 300
});
group.shuffle();
for (const crate of group.getChildren())
{
crate.body.customSeparateY = true;
}
this.physics.add.collider(group, group, function (gameObject1, gameObject2)
{
const b1 = gameObject1.body;
const b2 = gameObject2.body;
if (b1.y > b2.y)
{
b2.y += (b1.top - b2.bottom);
b2.stop();
}
else
{
b1.y += (b2.top - b1.bottom);
b1.stop();
}
});
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
physics: {
default: 'arcade',
arcade: {
debug: false,
fps: 60,
gravity: { y: 600 }
}
},
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и отключение стандартной логики
В методе create сцены, прежде всего, отключается проверка коллизий с верхней границей мира. Это нужно, чтобы объекты, появившиеся сверху, не «залипали» у границы.
this.physics.world.checkCollision.up = false;
Далее создается физическая группа (Physics Group) — мощный инструмент для управления множеством однотипных объектов с физикой. В конфигурации задаются ключевые параметры: упругость по Y, столкновение с границами мира, сопротивление (drag) для плавного падения, количество спрайтов, их начальное положение и вертикальная скорость.
const group = this.physics.add.group({
bounceY: 0.5,
collideWorldBounds: true,
dragY: 30,
frameQuantity: 18,
key: 'crate',
setXY: { x: 200, y: 0, stepX: 16, stepY: -64 },
velocityY: 300
});
Метод group.shuffle() случайным образом меняет порядок элементов в группе, что добавляет визуальной разнородности при их создании.
Включение кастомного разделения по вертикали
Самый важный шаг — указать движку, что для каждого тела в группе стандартный алгоритм разделения (separate) по оси Y должен быть заменен нашим. Это делается путем установки флага customSeparateY в true для тела каждого спрайта в группе.
for (const crate of group.getChildren())
{
crate.body.customSeparateY = true;
}
После этой настройки Arcade Physics будет по-прежнему обнаруживать столкновения между объектами группы (так как collider добавлен), но не будет автоматически раздвигать их по вертикали. Вся ответственность за определение их конечной позиции ложится на нашу функцию-обработчик.
Логика обработчика коллизий
Между всеми объектами группы добавляется коллайдер. Его callback-функция получает два столкнувшихся игровых объекта. Внутри мы вручную рассчитываем, как они должны расположиться после столкновения.
this.physics.add.collider(group, group, function (gameObject1, gameObject2)
{
const b1 = gameObject1.body;
const b2 = gameObject2.body;
if (b1.y > b2.y)
{
b2.y += (b1.top - b2.bottom);
b2.stop();
}
else
{
b1.y += (b2.top - b1.bottom);
b1.stop();
}
});
Алгоритм прост: определяется, какой из объектов находится ниже (имеет большую координату `y). Затем верхний объект «поднимается» ровно настолько, чтобы его нижняя грань (bottom) коснулась верхней грани (top) нижнего объекта. Методbody.stop()` мгновенно обнуляет скорость тела, чтобы оно не проваливалось дальше под действием гравитации. В результате создается эффект аккуратной укладки ящиков друг на друга без отскока.
Настройка игры и физики
Конфигурация игры стандартна, но с важными параметрами физики. Устанавливается Arcade Physics как движок по умолчанию. Отладочный режим выключен (debug: false). Сильная гравитация (gravity: { y: 600 }) заставляет объекты быстро падать, что наглядно демонстрирует работу нашей кастомной логики укладки.
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
physics: {
default: 'arcade',
arcade: {
debug: false,
fps: 60,
gravity: { y: 600 }
}
},
scene: Example
};
Что попробовать дальше
Использование customSeparateY открывает тонкую настройку физических взаимодействий, когда стандартного поведения Arcade Physics недостаточно. Вы получаете контроль над тем, что происходит в момент столкновения. Для экспериментов попробуйте изменить логику в обработчике: например, заставить объекты не останавливаться, а лишь замедляться, или реализовать «цепную реакцию» падения при достижении определенного веса. Можно также поэкспериментировать с флагом customSeparateX для создания нестандартного бокового взаимодействия, например, скользящего толчка.
