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

При разработке игр часто возникает необходимость работать не с одиночными спрайтами, а с целыми группами однотипных объектов: пулями, врагами, бонусами. Ручное создание и управление физикой для каждого из них — утомительно и неэффективно. Phaser предлагает мощный инструмент — `PhysicsGroup`. Эта статья на практическом примере покажет, как создавать физические группы частиц (`balls`) и ящиков (`crates`), задавать их начальные свойства, случайным образом распределять по сцене и настраивать реакцию на столкновения между группами. Вы научитесь управлять десятками объектов несколькими строками кода.

Версия 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/shinyball.png');
        this.load.image('crate', 'assets/sprites/crate32.png');
    }

    create ()
    {
        //  Create a few balls

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

        //  Create a few crates

        const crates = this.physics.add.group({
            key: 'crate',
            quantity: 24,
            bounceX: 1,
            bounceY: 1,
            collideWorldBounds: true,
            velocityX: -150,
            velocityY: -300
        });

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

        this.physics.add.collider(
            balls,
            crates,
            (ball, crate) =>
            {
                ball.setAlpha(0.5);
                crate.setAlpha(0.5);
            });
    }
}

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

const game = new Phaser.Game(config);

Создание физических групп с начальными свойствами

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

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

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

В данном примере создаются две группы: 24 блестящих шара и 24 ящика. Обратите внимание, что группам заданы противоположные начальные скорости, что сразу создает динамику на сцене.

Случайное размещение объектов по области

Созданные группы изначально находятся в одной точке. Чтобы распределить их по игровому полю, используется хелпер Phaser.Actions.RandomRectangle(). Этот метод принимает массив объектов (детей группы) и прямоугольную область (Phaser.Geom.Rectangle), внутри которой будут случайным образом размещены их координаты.

Получить всех детей группы можно методом .getChildren().

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

Здесь в качестве области используется this.physics.world.bounds — границы физического мира, которые обычно совпадают с размерами сцены. Каждый шар и ящик получит уникальные случайные координаты в пределах видимой области.

Настройка коллизий между группами

Самое мощное в использовании групп — это возможность настроить взаимодействие между ними одной инструкцией. Метод this.physics.add.collider() регистрирует обработчик столкновений между двумя объектами или группами.

this.physics.add.collider(
    balls,
    crates,
    (ball, crate) =>
    {
        ball.setAlpha(0.5);
        crate.setAlpha(0.5);
    });

Первые два аргумента — это сами группы. Движок Arcade Physics автоматически проверит столкновения между каждым шаром и каждым ящиком.

Третий аргумент — опциональная callback-функция, которая выполняется в момент столкновения любой пары объектов из этих групп. В примере при столкновении шара и ящика их прозрачность (alpha) уменьшается до 0.5. Функция получает в качестве параметров конкретные объекты (ball, crate), которые столкнулись.

Конфигурация игры и сцены

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

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    parent: 'phaser-example',
    physics: {
        default: 'arcade', // Используем Arcade Physics
        arcade: {
            debug: false   // Отключаем отладочную визуализацию
        }
    },
    scene: Example        // Указываем наш класс сцены
};

const game = new Phaser.Game(config);

Класс сцены (Example) содержит методы preload() для загрузки ассетов и create() для инициализации игровых объектов и логики, которую мы разбирали выше.

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

Группы в Arcade Physics — это фундаментальный инструмент для оптимизации и структурирования кода при работе с множеством однотипных физических объектов. Они избавляют от рутины, позволяя управлять свойствами, размещением и взаимодействием десятков спрайтов как единым целым. Для экспериментов попробуйте: 1. Изменить параметры групп (упругость, скорость, количество). 2. Использовать другую область для случайного размещения, например, только левую половину экрана. 3. В коллайдере изменить свойства объектов более радикально (например, setTint(0xff0000) для окрашивания или disableBody() для временного отключения физики). 4. Добавить третью группу и настроить коллизии между всеми тремя.