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

При работе с физикой в играх важно понимать, как именно взаимодействуют объекты. Система физики Arcade в Phaser предоставляет два ключевых набора флагов: `touching` и `blocked`. Эта статья покажет, как их визуализировать и интерпретировать, что поможет вам точнее настраивать поведение объектов при столкновениях и отскоках, а также отлаживать сложные физические взаимодействия.

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


class Example extends Phaser.Scene
{
    text;
    graphics;
    block;
    atari;

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('block', 'assets/sprites/block.png');
        this.load.image('flower', 'assets/sprites/flower-exo.png');
        this.load.image('atari', 'assets/sprites/atari800.png');
    }

    create ()
    {
        this.atari = this.physics.add.image(400, 300, 'atari');
        this.atari.body.setAllowGravity(false);
        this.atari.body.setImmovable(true);

        this.block = this.physics.add.image(0, 0, 'block');
        this.block.setVelocity(200, 200);
        this.block.setBounce(1, 1);
        this.block.setCollideWorldBounds(true);

        this.graphics = this.add.graphics();

        this.text = this.add.text(0, 0, '-');

        // During a Body vs. Body collision or overlap both become 'touching'.
        // It's easier to see during an overlap:
        this.physics.add.overlap(this.atari, this.block);
    }

    update ()
    {
        this.graphics.clear();

        this.draw(this.atari);
        this.draw(this.block);

        this.text.setText([
            'Box:',
            '',
            JSON.stringify(
                Phaser.Utils.Objects.Pick(this.block.body, [ 'blocked', 'touching', 'embedded' ]),
                null,
                2
            )
        ]);
    }

    draw (gameObject)
    {
        this.graphics.lineStyle(5, 0xffff00, 0.8);

        this.drawEdges(gameObject.body, gameObject.body.touching);

        this.graphics.lineStyle(5, 0xff0000, 0.8);

        this.drawEdges(gameObject.body, gameObject.body.blocked);
    }

    drawEdges (body, edges)
    {
        if (edges.left)
        {
            this.graphics.lineBetween(body.left, body.top, body.left, body.bottom);
        }

        if (edges.up)
        {
            this.graphics.lineBetween(body.left, body.top, body.right, body.top);
        }

        if (edges.right)
        {
            this.graphics.lineBetween(body.right, body.top, body.right, body.bottom);
        }

        if (edges.down)
        {
            this.graphics.lineBetween(body.left, body.bottom, body.right, body.bottom);
        }
    }
}

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

const game = new Phaser.Game(config);

Суть флагов touching и blocked

В физическом движке Arcade каждый спрайт с телом (Body) имеет объекты touching и blocked. Оба содержат свойства для четырех сторон: up, down, left, right.

- **touching** указывает, какая грань тела *контактирует* с другим телом в данный момент. Этот флаг актуален как при простом касании, так и при наложении (overlap) объектов. - **blocked** указывает, движение в каком направлении было *остановлено* другим телом или границей мира в течение последнего шага физики. Это ключевой флаг для обработки отскоков и блокировки движения.

В примере используется overlap вместо collider, чтобы контакт был постоянным и флаги touching всегда были видны.

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

В методе create() создаются два основных спрайта с физикой и настраивается их поведение.

this.atari = this.physics.add.image(400, 300, 'atari');
this.atari.body.setAllowGravity(false);
this.atari.body.setImmovable(true);

Спрайт atari становится неподвижной статической преградой. Вызов setImmovable(true) означает, что при столкновении двигаться будет только другой объект, а atari останется на месте.

this.block = this.physics.add.image(0, 0, 'block');
this.block.setVelocity(200, 200);
this.block.setBounce(1, 1);
this.block.setCollideWorldBounds(true);

Спрайт block — динамическое тело. Оно получает начальную скорость, идеальный отскок (bounce: 1) и будет сталкиваться с границами холста.

this.physics.add.overlap(this.atari, this.block);

Регистрация перекрытия (overlap) между объектами. Это заставляет движок проверять их взаимодействие каждый кадр и обновлять флаги touching.

Визуализация граней в реальном времени

Ядро примера — метод draw(), который вызывается в update() для отрисовки линий по краям тел.

draw (gameObject)
{
    this.graphics.lineStyle(5, 0xffff00, 0.8);
    this.drawEdges(gameObject.body, gameObject.body.touching);

    this.graphics.lineStyle(5, 0xff0000, 0.8);
    this.drawEdges(gameObject.body, gameObject.body.blocked);
}

- **Желтым цветом (0xffff00)** рисуются грани, которые входят в контакт (touching). - **Красным цветом (0xff0000)** рисуются грани, движение которых заблокировано (blocked).

Метод drawEdges() проверяет флаги в объекте edges и рисует линию вдоль соответствующей стороны bounding box тела.

if (edges.left)
{
    this.graphics.lineBetween(body.left, body.top, body.left, body.bottom);
}

Вывод информации и интерпретация

В update() также обновляется текстовое поле, куда выводятся актуальные состояния тела движущегося блока.

this.text.setText([
    'Box:',
    '',
    JSON.stringify(
        Phaser.Utils.Objects.Pick(this.block.body, [ 'blocked', 'touching', 'embedded' ]),
        null,
        2
    )
]);

Утилита Phaser.Utils.Objects.Pick создает новый объект, выбрав из тела блока только нужные свойства: blocked, touching и embedded. Это удобно для отладки.

Наблюдая за движением блока, вы увидите: 1. Когда блок касается неподвижного atari или границы мира, на этой грани загорится желтая линия (touching). 2. Если блок пытается двигаться в направлении, но встречает препятствие (например, прижимается к atari), на этой грани появится красная линия (blocked). При идеальном отскоке (bounce: 1) флаг blocked может мелькать очень быстро.

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

Визуализация touching и blocked дает четкое понимание того, как движок физики видит столкновения. Это мощный инструмент для отладки. Поэкспериментируйте: измените bounce на значение меньше 1, отключите setImmovable у atari или замените overlap на collider с callback-функцией. Посмотрите, как изменяется поведение флагов при разных типах взаимодействий.