О чем этот пример
При создании динамичных игр важно понимать, как движутся физические тела от кадра к кадру. Встроенные методы `body.deltaXFinal()` и `body.deltaYFinal()` в Phaser 3 позволяют получить точное смещение тела с учётом всех сил — гравитации, скорости, трения и столкновений. Эта статья покажет, как визуализировать эти векторы, чтобы отлаживать физику и создавать более предсказуемое поведение объектов. На примере с леммингами, платформами и шипастыми шарами мы разберём, как рассчитать и отрисовать итоговое смещение тела за кадр. Этот приём полезен не только для визуальной отладки, но и для реализации сложной логики, зависящей от точного перемещения объектов.
Версия 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('platform', 'assets/sprites/platform.png');
this.load.image('lemming', 'assets/sprites/lemming.png');
this.load.image('spikedball', 'assets/sprites/spikedball.png');
}
create ()
{
const platforms = this.physics.add.group({
key: 'platform',
frameQuantity: 3,
setXY: { x: 400, y: 150, stepY: 150 },
velocityX: 60,
immovable: true
});
const [ platform1, platform2, platform3 ] = platforms.getChildren();
platform1.setFrictionX(1);
platform2.setFrictionX(0.5);
platform3.setFrictionX(0);
const lemmings = this.physics.add.group({ gravityY: 600 });
lemmings.createMultiple([
{
key: 'lemming',
repeat: 3,
setXY: { x: 250, y: 0, stepX: 100 }
},
{
key: 'lemming',
repeat: 3,
setXY: { x: 250, y: 200, stepX: 100 }
},
{
key: 'lemming',
repeat: 3,
setXY: { x: 250, y: 350, stepX: 100 }
}
]);
this.physics.add.group({
key: 'spikedball',
frameQuantity: 6,
setXY: { x: 0, y: 625, stepX: 150 },
angularVelocity: 60
});
this.physics.add.collider(lemmings, platforms);
const graphics = this.add.graphics({ lineStyle: { color: 0xffff00 } });
this.events.on('postupdate', () =>
{
graphics.clear();
const bodies = Array.from(this.physics.world.bodies);
for (const body of bodies)
{
const { x, y } = body.center;
graphics.lineBetween(x, y, x + 100 * body.deltaXFinal(), y + 100 * body.deltaYFinal());
}
});
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
physics: { default: 'arcade' },
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и создание физических групп
В методе preload загружаются три спрайта: платформа, лемминг и шипастый шар. Основная работа происходит в create. Здесь создаются три ключевые физические группы.
Группа платформ настраивается с начальной горизонтальной скоростью (velocityX: 60) и свойством immovable: true, что делает их статичными при столкновениях. Три платформы размещаются вертикально с шагом в 150 пикселей.
const platforms = this.physics.add.group({
key: 'platform',
frameQuantity: 3,
setXY: { x: 400, y: 150, stepY: 150 },
velocityX: 60,
immovable: true
});
Группа леммингов создаётся с вертикальной гравитацией (gravityY: 600). Лемминги размещаются тремя рядами с помощью createMultiple.
const lemmings = this.physics.add.group({ gravityY: 600 });
lemmings.createMultiple([...]);
Группа шипастых шаров добавляется в нижнюю часть экрана с постоянной угловой скоростью (angularVelocity: 60).
Настройка трения и столкновений
Каждой платформе задаётся разный коэффициент трения по оси X с помощью метода setFrictionX(). Это напрямую влияет на итоговое смещение объектов, которые с ней сталкиваются.
platform1.setFrictionX(1);
platform2.setFrictionX(0.5);
platform3.setFrictionX(0);
Значение `1— максимальное трение (объект почти не скользит),0` — его отсутствие. Это демонстрируется на леммингах: на нижней платформе они будут двигаться вместе с ней, а на верхних — отставать или соскальзывать.
Коллайдер регистрирует столкновения между группой леммингов и группой платформ.
this.physics.add.collider(lemmings, platforms);
Визуализация финального смещения (deltaFinal)
Самый важный элемент примера — визуализация векторов финального смещения тел. Создаётся объект Graphics для рисования линий.
const graphics = this.add.graphics({ lineStyle: { color: 0xffff00 } });
Затем, на событие postupdate (которое срабатывает после всех физических расчётов), вешается обработчик. В нём сначала очищается холст (graphics.clear()), а затем для каждого физического тела в мире (this.physics.world.bodies) рисуется линия.
this.events.on('postupdate', () => {
graphics.clear();
const bodies = Array.from(this.physics.world.bodies);
for (const body of bodies) {
const { x, y } = body.center;
graphics.lineBetween(x, y, x + 100 * body.deltaXFinal(), y + 100 * body.deltaYFinal());
}
});
Методы body.deltaXFinal() и body.deltaYFinal() возвращают смещение тела за последний шаг физики с учётом всех корректировок от столкновений. Мы умножаем эти значения на 100 для наглядности визуализации. Линия показывает направление и относительную величину этого финального смещения.
Что показывают векторы на практике
В запущенном примере можно наблюдать:
1. **У платформ** векторы направлены строго вправо и имеют постоянную длину, так как их движение задано только скоростью velocityX, а трение и гравитация на них не действуют.
2. **У леммингов** векторы сложнее. На них действует гравитация, поэтому у падающих леммингов векторы направлены вниз. При приземлении на движущуюся платформу вектор становится горизонтальным, но его длина различается в зависимости от трения платформы.
3. **У шипастых шаров** векторы очень короткие или нулевые, так как они лишь вращаются на месте (angularVelocity), но не имеют линейного перемещения в пространстве.
Это наглядный способ убедиться, что физические расчёты, особенно связанные с трением и компенсацией столкновений, работают именно так, как вы ожидаете.
Что попробовать дальше
Использование deltaXFinal() и deltaYFinal() открывает тонкий контроль над движением. Вы можете использовать эти значения не только для отладки, но и в игровой логике — например, для определения реального перемещения персонажа за кадр, расчёта силы удара или создания следов.
**Идеи для экспериментов:**
1. Измените гравитацию леммингов или скорость платформ и понаблюдайте за изменением векторов.
2. Попробуйте применить силу (setVelocity) к леммингам и посмотрите, как вектор смещения мгновенно меняет направление.
3. Используйте длину вектора (рассчитанную через Math.hypot) для включения визуальных или звуковых эффектов только при значительном перемещении объекта.
