О чем этот пример

В Arcade Physics Phaser'а по умолчанию вращение спрайта (`setAngularVelocity`) никак не связано с его линейной скоростью. Это выглядит неестественно, когда объект, например, мяч, движется, но не вращается. В примере из официальной коллекции показан элегантный способ синхронизировать угловую скорость спрайта с его перемещением по поверхности, создавая убедительный эффект качения. Этот приём оживит ваши платформеры, пинболы или любые игры, где важна визуальная связь движения и вращения.

Версия 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('ball', 'assets/sprites/wizball.png');
    }

    create ()
    {
        const wheel = this.physics.add.image(50, 300, 'ball')
            .setAccelerationX(100)
            .setBounce(1)
            .setCollideWorldBounds(true);

        this.physics.world.on('worldstep', () =>
        {
            wheel.setAngularVelocity(
                Phaser.Math.RadToDeg(wheel.body.velocity.x / wheel.body.halfWidth)
            );
        });
    }
}

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    parent: 'phaser-example',
    physics: {
        default: 'arcade',
        arcade: { debug: true }
    },
    scene: Example
};

const game = new Phaser.Game(config);

Подготовка сцены и загрузка ассетов

Как и в любом проекте Phaser, начинаем с базовой настройки сцены. В методе preload указываем базовый URL для загрузки ресурсов и загружаем одно изображение — спрайт мяча.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('ball', 'assets/sprites/wizball.png');
}

Создание физического тела и настройка его поведения

В методе create мы создаём физический спрайт — наш "мяч". Ключевой момент: мы сразу настраиваем его физические свойства, используя методы цепочки (chaining).

- setAccelerationX(100) — задаёт постоянное ускорение по оси X, заставляя мяч разгоняться вправо. - setBounce(1) — устанавливает коэффициент упругости (реституции) в 1. Это означает идеально упругий отскок без потерь энергии при столкновении. - setCollideWorldBounds(true) — включает столкновение тела с границами мира (краями игрового поля). В комбинации с setBounce(1) это заставляет мяч отскакивать от стен.

const wheel = this.physics.add.image(50, 300, 'ball')
    .setAccelerationX(100)
    .setBounce(1)
    .setCollideWorldBounds(true);

Магия синхронизации: событие `worldstep`

Сердце примера — обработка события worldstep, которое генерируется физическим движком Arcade на каждом шаге симуляции. Именно здесь происходит расчёт и синхронизация угловой скорости.

Внутри обработчика события мы берём текущую линейную скорость тела по оси X (wheel.body.velocity.x) и делим её на радиус спрайта. Поскольку в Arcade Physics тело спрайта является AABB (axis-aligned bounding box), его "радиус" для расчётов — это половина ширины (body.halfWidth).

Полученное значение (скорость в пикселях в секунду, делённая на радиус в пикселях) даёт нам угловую скорость в радианах в секунду. Метод Phaser.Math.RadToDeg конвертирует её в градусы в секунду, что является требуемым форматом для метода setAngularVelocity.

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

this.physics.world.on('worldstep', () =>
{
    wheel.setAngularVelocity(
        Phaser.Math.RadToDeg(wheel.body.velocity.x / wheel.body.halfWidth)
    );
});

Конфигурация игры и запуск

Для работы примера необходима стандартная конфигурация игры с включённым физическим движком Arcade. Обратите внимание на параметр debug: true — он отрисует контуры физических тел, что полезно для визуализации и отладки.

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    parent: 'phaser-example',
    physics: {
        default: 'arcade',
        arcade: { debug: true }
    },
    scene: Example
};

const game = new Phaser.Game(config);

Что попробовать дальше

Используя событие worldstep для связи линейной и угловой скорости, вы можете легко создавать правдоподобно катящиеся объекты в своих играх. Для экспериментов попробуйте изменить ускорение, добавить трение (setDrag), применить формулу к оси Y для моделирования спуска с горки или привязать расчёт к спрайтам разного размера. Этот подход открывает путь к более сложной и приятной глазу физике взаимодействий.