О чем этот пример
В большинстве игр объекты не двигаются вечно. Будь то трение о поверхность или сопротивление воздуха, их скорость со временем падает. Phaser Arcade Physics предоставляет свойство `drag`, но оно применяется к каждой оси независимо, что не всегда реалистично. В этой статье мы разберем альтернативный подход: ручное управление сопротивлением через вектор скорости объекта. Это даст вам прямой контроль над замедлением, позволяя создавать эффекты вязкой среды, магических полей или просто более естественное трение.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
blocks;
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('block', 'assets/sprites/block.png');
}
create ()
{
this.blocks = this.physics.add.group({
bounceX: 0.5,
bounceY: 0.5,
collideWorldBounds: true
});
this.blocks.create(100, 200, 'block').setVelocity(100, 200);
this.blocks.create(500, 200, 'block').setVelocity(-100, -100);
this.blocks.create(300, 400, 'block').setVelocity(60, 100);
this.blocks.create(600, 300, 'block').setVelocity(-30, -50);
this.physics.add.collider(this.blocks);
}
update ()
{
for (const block of this.blocks.getChildren())
{
const { velocity } = block.body;
velocity.setLength(Math.floor(0.99 * velocity.length()));
}
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
physics: {
default: 'arcade',
arcade: {
debug: true,
gravity: { y: 200 }
}
},
scene: Example
};
const game = new Phaser.Game(config);
Настройка физической группы
Всё начинается с создания группы физических тел. Вместо того чтобы настраивать свойства для каждого спрайта по отдельности, мы используем this.physics.add.group. Это эффективно и позволяет легко применовать общие правила.
Ключевые параметры конфигурации группы:
* bounceX и bounceY: определяют упругость (коэффициент восстановления) при столкновениях.
* collideWorldBounds: включает столкновение с границами игрового мира.
this.blocks = this.physics.add.group({
bounceX: 0.5,
bounceY: 0.5,
collideWorldBounds: true
});
Создание и запуск спрайтов
После создания группы мы наполняем её спрайтами с помощью метода .create(). Каждый спрайт автоматически получает физическое тело Arcade.
Важный шаг — задание начальной скорости. Метод setVelocity(x, y) сообщает каждому блоку вектор начального движения. Это заставляет их разлетаться в разные стороны.
this.blocks.create(100, 200, 'block').setVelocity(100, 200);
this.blocks.create(500, 200, 'block').setVelocity(-100, -100);
this.blocks.create(300, 400, 'block').setVelocity(60, 100);
this.blocks.create(600, 300, 'block').setVelocity(-30, -50);
Чтобы спрайты в группе сталкивались друг с другом, мы создаём коллайдер.
this.physics.add.collider(this.blocks);
Принцип ручного сопротивления (Drag)
Стандартное свойство drag в Arcade Physics применяется к компонентам скорости X и Y по отдельности. Это работает, но может выглядеть неестественно, особенно при диагональном движении.
Наш подход заключается в том, чтобы каждый кадр уменьшать длину (модуль) вектора скорости на небольшой фиксированный процент. Это имитирует равномерное сопротивление, не зависящее от направления.
1. В цикле update мы перебираем всех детей группы через this.blocks.getChildren().
2. Для каждого блока получаем доступ к его вектору скорости: block.body.velocity.
3. Метод velocity.length() возвращает текущую длину вектора.
4. Умножая эту длину на коэффициент (например, 0.99), мы уменьшаем её на 1%.
5. Метод velocity.setLength() устанавливает новую длину для вектора, сохраняя его направление.
update ()
{
for (const block of this.blocks.getChildren())
{
const { velocity } = block.body;
velocity.setLength(Math.floor(0.99 * velocity.length()));
}
}
Использование Math.floor() обеспечивает целочисленное значение скорости, что может быть полезно для оптимизации, но для плавного замедления лучше использовать числа с плавающей точкой.
Конфигурация игры и физики
Для работы примера необходима правильная настройка игры. Ключевой момент — активация Arcade Physics и установка глобальной гравитации.
* default: 'arcade': Указывает, что используем Arcade Physics.
* debug: true: Включает отладочную отрисовку контуров тел, что помогает визуализировать столкновения.
* gravity: { y: 200 }: Задаёт гравитацию, направленную вниз по оси Y. В сочетании с нашим ручным сопротивлением это создаёт сложную, но предсказуемую траекторию движения объектов.
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
physics: {
default: 'arcade',
arcade: {
debug: true,
gravity: { y: 200 }
}
},
scene: Example
};
Что попробовать дальше
Ручное управление вектором скорости открывает тонкую настройку физики, недоступную при использовании только стандартного drag. Вы можете экспериментировать: заменить постоянный коэффициент на переменный (например, зависящий от типа поверхности под объектом), добавить ускорение против направления движения для эффекта «обратной тяги» или комбинировать этот подход с другими силами. Попробуйте применить его не к скорости, а к угловой скорости (body.angularVelocity) для создания трения при вращении.
