Object pooling: реализация на скриптах

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

В PlayCanvas object pooling может быть реализован с использованием стандартных возможностей движка и кастомных скриптов. Наиболее важной частью реализации является создание системы, которая будет управлять пулом объектов, поддерживать их состояние и обеспечивать быструю очистку.

Основы реализации object pooling в PlayCanvas

  1. Класс для пула объектов Чтобы создать пул объектов, нужно начать с реализации класса, который будет управлять всеми необходимыми операциями, такими как создание объектов, их активация и деактивация.
var ObjectPool = function (prefab, size) {
    this.prefab = prefab;
    this.size = size;
    this.pool = [];
    
    // Инициализация пула объектов
    for (var i = 0; i < size; i++) {
        var entity = this.prefab.clone();
        entity.enabled = false; // Скрываем объект, чтобы не рендерить его сразу
        this.pool.push(entity);
    }
};

ObjectPool.prototype.getObject = function () {
    for (var i = 0; i < this.pool.length; i++) {
        var obj = this.pool[i];
        if (!obj.enabled) {
            obj.enabled = true; // Активируем объект
            return obj;
        }
    }
    return null; // Если нет свободных объектов, возвращаем null
};

ObjectPool.prototype.returnObject = function (object) {
    object.enabled = false; // Деактивируем объект и возвращаем в пул
};
  1. Использование пула объектов Создание объекта из пула происходит через метод getObject(), который возвращает объект, если он доступен. Когда объект больше не нужен, его можно вернуть в пул с помощью returnObject().
var pool = new ObjectPool(myPrefab, 10);

// Получаем объект из пула
var object = pool.getObject();
if (object) {
    object.setPosition(0, 0, 0); // Размещение объекта в сцене
    // Выполнение других операций с объектом
}

// Возвращаем объект в пул
pool.returnObject(object);

Расширенная реализация с учётом различных состояний объектов

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

var EnemyPool = function (prefab, size) {
    this.prefab = prefab;
    this.size = size;
    this.pool = [];
    
    for (var i = 0; i < size; i++) {
        var entity = this.prefab.clone();
        entity.enabled = false;
        entity.hp = 100; // Стартовое здоровье
        entity.speed = 5; // Стартовая скорость
        this.pool.push(entity);
    }
};

EnemyPool.prototype.getObject = function () {
    for (var i = 0; i < this.pool.length; i++) {
        var obj = this.pool[i];
        if (!obj.enabled) {
            obj.enabled = true;
            obj.hp = 100; // Восстановление начального состояния
            obj.speed = 5;
            return obj;
        }
    }
    return null;
};

EnemyPool.prototype.returnObject = function (object) {
    object.enabled = false; // Отключаем объект
    // Важно сбросить параметры, чтобы объект был готов для следующего использования
    object.hp = 100;
    object.speed = 5;
};

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

  1. Уменьшение нагрузки на сборщик мусора Одним из основных преимуществ object pooling является то, что объекты не создаются и не уничтожаются при каждом использовании. Это снижает нагрузку на сборщик мусора, который обычно выполняет дорогостоящие операции по удалению объектов, что может приводить к фризакам и снижению производительности, особенно в мобильных играх или играх с высоким требованием к FPS.

  2. Быстрое повторное использование объектов Когда объект возвращается в пул, его состояние может быть сброшено или обновлено. Это позволяет быстро перераспределить объект для нового использования, а не тратить время на его создание.

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

Оптимизация пула объектов

  1. Динамическое расширение пула Иногда размер пула может быть недостаточен, если количество объектов в сцене превышает первоначально выделенное число. Для этого можно добавить возможность динамически расширять пул, если текущий размер не вмещает всех объектов.
ObjectPool.prototype.expandPool = function (additionalSize) {
    for (var i = 0; i < additionalSize; i++) {
        var entity = this.prefab.clone();
        entity.enabled = false;
        this.pool.push(entity);
    }
};
  1. Отложенная активация объектов В некоторых случаях можно отложить активацию объектов, чтобы избежать загрузки при старте. Это можно реализовать через таймеры или условия, когда объект должен быть активирован только по мере необходимости.
ObjectPool.prototype.getObject = function () {
    for (var i = 0; i < this.pool.length; i++) {
        var obj = this.pool[i];
        if (!obj.enabled) {
            setTimeout(() => obj.enabled = true, 100); // Отложенная активация
            return obj;
        }
    }
    return null;
};

Влияние на производительность

Реализация object pooling в PlayCanvas в значительной степени может повлиять на производительность приложения или игры. В ситуациях с частыми созданиями и уничтожениями объектов, таких как управление снарядами или врагами, использование пула объектов позволяет существенно снизить нагрузку на систему. Однако для малых и простых приложений, где создание и уничтожение объектов происходит редко, использование пула может не дать значительного выигрыша в производительности.

Оптимальное использование пулинга объектов возможно в случае, когда количество объектов в сцене динамически меняется и объекты требуют частой переработки.

Заключение

PlayCanvas предоставляет гибкие возможности для реализации различных паттернов, включая object pooling. Благодаря правильно реализованной системе пула объектов можно эффективно управлять памятью, снижать накладные расходы на создание и уничтожение объектов и, как следствие, повышать производительность игры или приложения.