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

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

Версия 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('bg', 'assets/skies/space2.png');
        this.load.spritesheet('ball', 'assets/sprites/balls.png', { frameWidth: 17, frameHeight: 17 });
    }

    create ()
    {
        this.add.image(400, 300, 'bg');

        const balls = this.physics.add.group({
            key: 'ball',
            frameQuantity: 48,
            bounceX: 1,
            bounceY: 1,
            collideWorldBounds: true,
            velocityX: 200,
            velocityY: 150
        });

        Phaser.Actions.RandomRectangle(balls.getChildren(), this.physics.world.bounds);

        for (const ball of balls.getChildren())
        {
            ball.body.onWorldBounds = true;
        }

        this.physics.world.on('worldbounds', (body) =>
        {
            const ball = body.gameObject;

            ball.setFrame((ball.frame.name + 1) % 5);
        });
    }
}

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

const game = new Phaser.Game(config);

Настройка физики и загрузка ассетов

Для работы с событием worldbounds необходимо активировать Arcade Physics в конфигурации игры. Это делается в объекте config. Мы также загружаем два изображения: фон (bg) и спрайтшит с шариками (ball).

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

В методе preload сцена загружает необходимые изображения. Обратите внимание, что для спрайтшита ball указан размер одного кадра (17x17 пикселей), что позволит позже переключаться между ними.

preload ()
{
    this.load.image('bg', 'assets/skies/space2.png');
    this.load.spritesheet('ball', 'assets/sprites/balls.png', { frameWidth: 17, frameHeight: 17 });
}

Создание физической группы объектов

Вместо создания множества одинаковых физических тел по одному, эффективнее использовать физическую группу (PhysicsGroup). Она создаётся через this.physics.add.group. Группа позволяет задать общие свойства для всех её членов.

const balls = this.physics.add.group({
    key: 'ball',
    frameQuantity: 48,
    bounceX: 1,
    bounceY: 1,
    collideWorldBounds: true,
    velocityX: 200,
    velocityY: 150
});

Ключевые параметры здесь: * key: Текстура для спрайтов. * frameQuantity: Количество создаваемых объектов (48 шариков). * bounceX, bounceY: Коэффициент упругости (1 — идеальный отскок). * collideWorldBounds: Включает столкновение тел с границами мира. * velocityX, velocityY: Начальная скорость для всех объектов группы.

Далее объекты группы случайным образом распределяются по всей площади игрового мира с помощью Phaser.Actions.RandomRectangle.

Phaser.Actions.RandomRectangle(balls.getChildren(), this.physics.world.bounds);

Активация события для каждого тела

По умолчанию физическое тело не генерирует событие worldbounds при столкновении с границей. Чтобы его получать, для каждого тела в группе необходимо явно установить свойство body.onWorldBounds в true. Мы проходим по всем детям группы в цикле for...of.

for (const ball of balls.getChildren())
{
    ball.body.onWorldBounds = true;
}

Важно: это свойство устанавливается на объект body физического тела, которое является частью игрового объекта (gameObject).

Обработка события worldbounds

Теперь можно подписаться на глобальное событие worldbounds, которое испускает this.physics.world. Обработчик события получает в качестве аргумента объект body — физическое тело, которое коснулось границы.

this.physics.world.on('worldbounds', (body) =>
{
    const ball = body.gameObject;
    ball.setFrame((ball.frame.name + 1) % 5);
});

В данном примере при каждом столкновении с миром мы берём игровой объект (body.gameObject) и меняем его текущий кадр на следующий по циклу (по модулю 5, так как в спрайтшите 5 кадров). Это создаёт простую визуальную обратную связь.

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

Событие worldbounds в Arcade Physics — это мощный и простой механизм для реакции на касание объектами границ экрана. Вы можете использовать его не только для смены анимации, но и для воспроизведения звука удара, нанесения урона объекту или активации игровой логики (например, если мяч вылетел за пределы поля). Для экспериментов попробуйте изменить реакцию на событие: добавляйте частицы в точке столкновения, меняйте не кадр, а tint (цвет) объекта или реализуйте счётчик, сколько раз каждый шарик ударился о стенку.