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

Создание и уничтожение игровых объектов в реальном времени может быть ресурсоемкой операцией, особенно при частых вызовах. Phaser предлагает элегантное решение — пулы объектов на основе групп (Group). Используя пулы, вы можете переиспользовать уже созданные, но временно неактивные объекты, что значительно снижает нагрузку на сборщик мусора и ускоряет работу игры. Эта статья покажет, как создать простой пул спрайтов с ограничением по размеру и управлять его состоянием.

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

    create ()
    {
        const info = this.add.text(0, 0, 'Click to add objects', { fill: '#00ff00' });

        //  Our pool - essentially a Group that takes advantage of maxSize

        //  Setting the maxSize property limits the amount of objects allowed in this pool

        const cans = this.add.group({
            defaultKey: 'cokecan',
            maxSize: 10
        });

        let x = 60;

        this.input.on('pointerdown', () =>
        {

            //  Pluck an entry from the pool. If it doesn't already exist, create it.
            cans.get(x, 300);

            x += 74;

            info.setText([
                `Used: ${cans.getTotalUsed()}`,
                `Free: ${cans.getTotalFree()}`
            ]);

        });
    }
}

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    backgroundColor: '#2d2d2d',
    parent: 'phaser-example',
    scene: Example
};

const game = new Phaser.Game(config);

Что такое пул объектов?

Пул (англ. pool) — это шаблон проектирования, который предполагает заблаговременное создание набора (пула) переиспользуемых объектов. Вместо того чтобы постоянно создавать новые объекты (например, пули, врагов, эффекты) и удалять их, вы активируете объект из пула, когда он нужен, и деактивируете, возвращая в пул, когда его работа завершена.

В Phaser за это отвечает класс Group. Его свойство maxSize превращает обычную группу в пул с фиксированной вместимостью. Все объекты создаются заранее или по требованию, но их количество никогда не превысит установленный лимит.

Создание пула с помощью группы

В методе create сцены мы инициализируем пул. Ключевой параметр — maxSize. Также мы указываем defaultKey — текстуру, которая будет использоваться для автоматического создания объектов в пуле.

const cans = this.add.group({
    defaultKey: 'cokecan',
    maxSize: 10
});

Этот код создает группу-пул cans. Параметр defaultKey: 'cokecan' говорит Phaser, что новые объекты в этой группе должны быть спрайтами с текстурой 'cokecan'. Важнее всего maxSize: 10 — он ограничивает общее количество спрайтов в этой группе (как активных, так и неактивных) десятью. Phaser не сможет создать одиннадцатый объект.

Извлечение объекта из пула

Когда требуется новый объект (например, по клику мыши), мы не создаем его с нуля, а "достаем" из пула с помощью метода get().

cans.get(x, 300);

Метод group.get(x, y) — это сердце механизма пула. Phaser действует так: 1. Проверяет, есть ли в группе неактивный (выключенный и невидимый) объект. 2. Если есть — он активируется (включается, становится видимым) и помещается в указанные координаты (x, y). 3. Если неактивных объектов нет, но общее количество объектов в группе (total) меньше maxSize, Phaser создает новый объект с использованием defaultKey, активирует его и помещает в (x, y). 4. Если группа заполнена (все объекты активны), метод вернет undefined. В нашем примере этого не произойдет, так как мы просто увеличиваем координату `x` для следующего спрайта.

Мониторинг состояния пула

Для отладки и балансировки игры полезно знать, сколько объектов пула сейчас занято, а сколько свободно. Группа с maxSize предоставляет для этого два метода.

info.setText([
    `Used: ${cans.getTotalUsed()}`,
    `Free: ${cans.getTotalFree()}`
]);

* group.getTotalUsed(): Возвращает количество активных (используемых) объектов в группе. * group.getTotalFree(): Возвращает количество неактивных (свободных) объектов, готовых к переиспользованию.

Сумма этих двух значений всегда будет равна текущему общему количеству объектов в группе (group.getLength()), которое не может превысить maxSize. В интерфейсе это отображается как динамически обновляемый текст.

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

Использование групп с ограничением maxSize — это простой и эффективный способ внедрить паттерн "Пул объектов" в вашу игру на Phaser. Он минимизирует затраты на создание и уничтожение объектов, что критически важно для производительности в сценах с большим количеством однотипных сущностей (стрельба, частицы, враги). **Идеи для экспериментов:** 1. Реализуйте систему стрельбы: создайте пул для пуль с maxSize: 30. При выстреле "доставайте" пулю из пула, а при столкновении с целью или выходе за границы экрана — деактивируйте ее с помощью bullet.setActive(false).setVisible(false), возвращая в пул. 2. Сымитируйте нехватку ресурсов: установите очень маленький maxSize (например, 3) и обработайте ситуацию, когда group.get() возвращает undefined. 3. Используйте пул для анимаций частиц, создавая эффекты взрывов или подбора предметов без просадок FPS.