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

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

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

    create ()
    {
        this.physics.add.image(100, 100, 'block').setVelocityX(100).setDragX(10);
        this.physics.add.image(100, 200, 'block').setVelocityX(100).setDragX(20);
        this.physics.add.image(100, 300, 'block').setVelocityX(100).setDragX(50);
        this.physics.add.image(100, 400, 'block').setVelocityX(100).setDragX(100);
        this.physics.add.image(100, 500, 'block').setVelocityX(100).setDragX(1000);

        const bodies = Array.from(this.physics.world.bodies);
        for (const body of bodies)
        {
            const { drag, velocity } = body;

            console.log('Body will stop after %.3f seconds', velocity.x / drag.x);
        }
    }

    update ()
    {
        const bodies = Array.from(this.physics.world.bodies);
        for (const body of bodies)
        {
            body.debugBodyColor = body.speed > 0 ? 0x00ff00 : 0xff0000;
        }
    }
}

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);

Что такое сопротивление (Drag) в Arcade Physics?

В Arcade Physics сопротивление (drag) — это постоянная сила, которая действует противоположно направлению движения тела, постепенно уменьшая его скорость. Это не трение о поверхность, а скорее линейное сопротивление среды (например, воздуха или воды).

Ключевая особенность: сила сопротивления не зависит от скорости тела. Она постоянна. Это значит, что тело будет замедляться линейно, пока его скорость не достигнет нуля. Методы setDragX(drag) и setDragY(drag) устанавливают значение этого сопротивления по соответствующим осям. Чем больше значение, тем быстрее объект остановится.

Настройка сцены и создание физических тел

В примере создается одна сцена, которая загружает спрайт и в методе create() создает пять идентичных блоков с разным сопротивлением. Конфигурация игры включает Arcade Physics с включенным режимом отладки (debug: true), что позволяет видеть хитбоксы тел.

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

В методе create() создаются пять физических тел — спрайты блока. Каждому задается одинаковая начальная скорость по оси X (100 пикселей в секунду), но разное сопротивление (drag.x).

create ()
{
    this.physics.add.image(100, 100, 'block').setVelocityX(100).setDragX(10);
    this.physics.add.image(100, 200, 'block').setVelocityX(100).setDragX(20);
    this.physics.add.image(100, 300, 'block').setVelocityX(100).setDragX(50);
    this.physics.add.image(100, 400, 'block').setVelocityX(100).setDragX(100);
    this.physics.add.image(100, 500, 'block').setVelocityX(100).setDragX(1000);
}

Расчет времени до полной остановки

Поскольку сопротивление постоянно, а начальная скорость известна, можно легко вычислить время, через которое тело остановится. Формула проста: время = скорость / сопротивление. В коде примера это демонстрируется путем перебора всех тел в физическом мире и вывода результата в консоль.

const bodies = Array.from(this.physics.world.bodies);
for (const body of bodies)
{
    const { drag, velocity } = body;
    console.log('Body will stop after %.3f seconds', velocity.x / drag.x);
}

Для первого тела (скорость 100, сопротивление 10) расчет будет: 100 / 10 = 10 секунд. Для последнего (сопротивление 1000) — всего 0.1 секунды. Этот расчет работает, потому что в Arcade Physics скорость уменьшается на значение сопротивления каждую секунду.

Визуализация состояния в реальном времени

Метод update() вызывается на каждом кадре и используется для визуальной обратной связи. В данном примере он меняет цвет отладочной обводки тела в зависимости от того, движется ли оно.

Свойство body.speed хранит текущую общую скорость тела (скаляр). Если скорость больше нуля, цвет устанавливается зеленым (0x00ff00), если тело остановилось — красным (0xff0000). Свойство debugBodyColor управляет цветом отладочной отрисовки для Arcade Physics.

update ()
{
    const bodies = Array.from(this.physics.world.bodies);
    for (const body of bodies)
    {
        body.debugBodyColor = body.speed > 0 ? 0x00ff00 : 0xff0000;
    }
}

Запустив пример, вы увидите, как блоки с меньшим сопротивлением (верхние) будут дольше оставаться зелеными и проедут дальше, в то время как нижний блок почти мгновенно станет красным.

Практическое применение и настройки

Сопротивление — отличный инструмент для моделирования различных материалов и сред. * **Низкие значения (1-50):** Подходят для имитации движения по льду, в воздухе или для очень массивных объектов с большой инерцией. * **Высокие значения (100-500):** Используются для быстрой остановки, создания эффекта "трения" о землю или движения в густой среде (например, под водой). * **Очень высокие значения (1000+):** Фактически мгновенная остановка, может использоваться для резкого прекращения движения по команде.

Важно помнить, что сопротивление применяется отдельно по осям. Объект, движущийся по диагонали, будет замедляться с разной скоростью по осям X и Y, если заданы разные drag.x и drag.y. Для равномерного замедления устанавливайте одинаковые значения.

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

Сопротивление (drag) в Phaser — это простой, но мощный механизм для управления замедлением объектов. Используя линейную модель, он позволяет легко предсказывать поведение тел и настраивать ощущения от игры. Для экспериментов попробуйте

  1. Применить setDragY() для падающих объектов, чтобы создать эффект парашюта или вязкой жидкости
  2. Динамически менять сопротивление в update() в зависимости от поверхности, по которой движется персонаж
  3. Скомбинировать сопротивление с ускорением (setAcceleration) для создания сложных кривых движения