Object pooling: группы с maxSize

Object pooling — это метод управления объектами в играх, направленный на уменьшение нагрузки на сборщик мусора и повышение производительности. В Phaser 3 для реализации пулов объектов используется система групп (Phaser.GameObjects.Group), которая позволяет управлять множеством однотипных игровых объектов с возможностью их повторного использования.


Создание группы с maxSize

При создании группы можно задать максимальный размер пула (maxSize). Этот параметр ограничивает количество активных объектов и позволяет контролировать ресурсы.

const bullets = this.add.group({
    classType: Phaser.GameObjects.Image,
    maxSize: 20,
    runChildUpdate: true
});

Пояснения:

  • classType — класс объектов, которые будут содержаться в группе. В примере используется Phaser.GameObjects.Image, но можно указать любой кастомный класс.
  • maxSize — максимальное количество объектов в пуле. При попытке получить новый объект после достижения лимита метод get() вернёт null.
  • runChildUpdate — если true, метод update() каждого объекта группы будет вызываться автоматически при каждом кадре сцены.

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

Для извлечения объекта используется метод get():

const bullet = bullets.get(x, y, 'bulletTexture');
if (bullet) {
    bullet.setActive(true);
    bullet.setVisible(true);
    bullet.body.velocity.y = -300;
}

Важно:

  • Если пул достиг maxSize, get() вернёт null. Это предотвращает создание лишних объектов.
  • Объекты, возвращённые методом get(), изначально неактивны и невидимы. Для их использования необходимо явно включить видимость и активность через setActive(true) и setVisible(true).

Освобождение объектов в пул

После того как объект завершил своё назначение, его нужно вернуть в пул с помощью killAndHide() или через setActive(false)/setVisible(false):

bullet.killAndHide();
bullet.body.stop();
  • killAndHide() — удобный метод, который отключает объект и скрывает его из сцены.
  • При правильном использовании пул объектов позволяет многократно переиспользовать один и тот же экземпляр без создания новых, что минимизирует нагрузку на память и CPU.

Настройка поведения объектов в группе

Группы поддерживают следующие свойства и методы, которые облегчают работу с объектами:

  • countActive(true/false) — возвращает количество активных или неактивных объектов.
  • getFirstDead() — получает первый неактивный объект из пула.
  • preUpdate(time, delta) — вызывается автоматически при runChildUpdate: true для всех объектов группы.
  • add() / remove() — динамическое добавление и удаление объектов из группы.

Пример динамического использования:

this.time.addEvent({
    delay: 200,
    loop: true,
    callback: () => {
        const bullet = bullets.get();
        if (bullet) {
            bullet.setPosition(player.x, player.y - 20);
            bullet.setActive(true);
            bullet.setVisible(true);
            bullet.body.velocity.y = -400;
        }
    }
});

Пользовательские классы с пулом

Для более сложного поведения часто создают собственные классы объектов и передают их в classType группы. Это позволяет автоматически инициализировать объекты при извлечении из пула.

class Bullet extends Phaser.GameObjects.Image {
    constructor(scene, x, y) {
        super(scene, x, y, 'bulletTexture');
        scene.add.existing(this);
        scene.physics.add.existing(this);
        this.body.setCollideWorldBounds(true);
        this.setActive(false);
        this.setVisible(false);
    }

    fire(x, y) {
        this.setPosition(x, y);
        this.setActive(true);
        this.setVisible(true);
        this.body.velocity.y = -300;
    }

    update() {
        if (this.y < -50) {
            this.setActive(false);
            this.setVisible(false);
        }
    }
}

const bullets = this.add.group({
    classType: Bullet,
    maxSize: 30,
    runChildUpdate: true
});

Теперь пул управляет объектами полностью: каждый объект сам обрабатывает своё возвращение в пул, а сцена получает только ссылку на готовый объект через get().


Преимущества использования maxSize

  1. Контроль ресурсов — ограничение числа объектов предотвращает перегрузку памяти и физического движка.
  2. Производительность — повторное использование объектов снижает нагрузку на сборщик мусора.
  3. Удобство управления — встроенные методы групп позволяют быстро получать, активировать и возвращать объекты в пул.

Рекомендации по использованию

  • Для объектов, которые часто создаются и уничтожаются (снаряды, враги, эффекты), обязательно использовать пул с maxSize.
  • Всегда проверять результат метода get(), чтобы избежать ошибок при достижении лимита.
  • Настроить runChildUpdate: true при необходимости автоматической логики объектов.
  • Поддерживать консистентное состояние объектов: при возврате в пул сбрасывать скорость, позицию и другие параметры.

Использование групп с maxSize в Phaser является оптимальным подходом для современных 2D-игр, где важны производительность и стабильность. Пулы объектов обеспечивают эффективное управление ресурсами и позволяют создавать плавный игровой процесс даже при большом количестве динамических элементов.