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

При создании игр с множеством однотипных объектов, например, шаров или ящиков, управлять их взаимодействием поодиночке неэффективно. Phaser предоставляет мощный инструмент — физические группы (Physics Group). Эта статья покажет, как быстро создать группу из множества объектов с физикой и настроить их столкновения друг с другом, используя метод `this.physics.add.collider`. Мы разберем два подхода: стандартное столкновение и столкновение с пользовательской функцией обратного вызова, которая позволяет реагировать на каждый факт соударения.

Версия 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');
    }

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

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

        this.physics.add.collider(crates);

        // OR

        // this.physics.add.collider(
        //     crates,
        //     undefined,
        //     function (crate1, crate2)
        //     {
        //         crate1.setAlpha(0.5);
        //         crate2.setAlpha(0.5);
        //     }
        // );
    }
}

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

const game = new Phaser.Game(config);

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

Вместо ручного создания и настройки каждого спрайта с физикой, Phaser позволяет использовать группы. Метод this.physics.add.group создает и возвращает PhysicsGroup — особый тип группы, все дочерние элементы которой уже являются физическими телами Arcade.

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

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

Сразу после создания все 24 шара ('ball') уже имеют тела Arcade Physics с заданной упругостью, скоростью и будут отскакивать от границ игрового мира.

Распределение объектов и простой коллайдер

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

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

Теперь, чтобы объекты группы сталкивались друг с другом, используется метод this.physics.add.collider. Самый простой способ — передать одну и ту же группу в оба аргумента. Это создаст коллайдер для парного столкновения всех тел внутри этой группы.

this.physics.add.collider(crates);

В этом случае метод автоматически подставляет группу и в первый (объект A), и во второй (объект B) параметры, что эквивалентно this.physics.add.collider(crates, crates). Теперь все шары будут реалистично отскакивать друг от друга.

Коллайдер с пользовательским callback

Часто при столкновении нужно выполнить дополнительную логику: проиграть звук, изменить внешний вид объектов, нанести урон. Для этого this.physics.add.collider принимает третий аргумент — функцию обратного вызова (callback).

В примере ниже callback-функция делает оба столкнувшихся шара полупрозрачными. Важно: функция вызывается для каждой пары столкнувшихся тел.

this.physics.add.collider(
    crates,
    undefined,
    function (crate1, crate2)
    {
        crate1.setAlpha(0.5);
        crate2.setAlpha(0.5);
    }
);

Здесь есть важный нюанс. Мы передаем группу crates как первый объект для столкновений, а вторым аргументом указываем undefined. Это означает: "Проверять столкновения объектов из группы crates... ни с чем другим?" На самом деле, когда второй аргумент не указан (или undefined), система по умолчанию подставляет ту же самую группу, что и в первом аргументе. Таким образом, эта запись также настраивает столкновения внутри группы, но уже с кастомной логикой при каждом соударении.

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

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

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

Именно параметр default: 'arcade' дает нам доступ к объекту this.physics внутри сцены и ко всем его методам, таким как add.group и add.collider. Гравитация, заданная здесь, будет влиять на все физические тела в сцене, включая наши шары.

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

Использование PhysicsGroup в связке с this.physics.add.collider — это основа для создания эффективных симуляций множества взаимодействующих объектов. Для экспериментов попробуйте: 1. Изменить параметры группы: bounceY на 0, чтобы шары не отскакивали от пола, или dragX для создания эффекта трения. 2. В callback-функции коллайдера изменить не alpha, а, например, setTint для окрашивания сталкивающихся шаров. 3. Создать две разные группы (например, balls и boxes) и настроить коллайдер между ними с уникальной логикой взаимодействия.