Уничтожение объектов и очистка памяти

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


Методы удаления объектов

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

let sprite = this.add.sprite(100, 100, 'player');
sprite.destroy(); // Полное удаление спрайта

Особенности метода destroy():

  • Удаляет объект из сцены, если он добавлен через this.add.
  • Освобождает слушатели событий и связи с другими объектами.
  • Позволяет передавать параметр { children: true } для рекурсивного удаления вложенных объектов.
let group = this.add.group();
let childSprite = this.add.sprite(50, 50, 'enemy');
group.add(childSprite);

group.destroy(true); // Уничтожает группу и все дочерние объекты

Уничтожение спрайтов и текстур

Спрайты в Phaser используют текстуры, загруженные через this.load. Удаление спрайта не освобождает текстуру из памяти. Для полной очистки используется метод removeTexture объекта TextureManager.

this.textures.remove('enemyTexture'); // Удаляет текстуру из памяти

Важно учитывать:

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

Группы и контейнеры

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

Пример с группой:

let enemies = this.add.group();

for (let i = 0; i < 10; i++) {
    let enemy = this.add.sprite(100 * i, 50, 'enemy');
    enemies.add(enemy);
}

// Уничтожение всех врагов
enemies.clear(true, true); // Первый параметр — удалить детей, второй — вызвать destroy()

Отличие Group от Container:

  • Group — логическая организация объектов, влияет на управление, но не на позиционирование.
  • Container — визуальная организация, все дочерние объекты наследуют трансформации контейнера.
let container = this.add.container(200, 200);
let sprite1 = this.add.sprite(0, 0, 'star');
let sprite2 = this.add.sprite(50, 50, 'star');

container.add([sprite1, sprite2]);
container.destroy(true); // Уничтожение контейнера и всех вложенных спрайтов

Очистка слушателей и событий

Объекты Phaser могут быть связаны с событиями через on, once, или setInteractive. При удалении объектов важно сбрасывать подписки, иначе объекты будут удерживаться в памяти.

let sprite = this.add.sprite(100, 100, 'player');

sprite.setInteractive();
sprite.on('pointerdown', () => console.log('Click'));

sprite.destroy(); // Все интерактивные события удаляются автоматически

Если событие было привязано к внешнему объекту, его нужно вручную отключить через off:

this.events.on('gameOver', endGameHandler);
this.events.off('gameOver', endGameHandler);

Управление текстурами и кешем

Phaser использует кеширование текстур и аудио для быстрого рендеринга. Для удаления ненужных ресурсов применяются методы remove на менеджерах:

this.textures.remove('spriteKey');
this.cache.audio.remove('soundKey');

Особенности:

  • Textures: освобождают память GPU.
  • Audio: освобождают память RAM и дескрипторы звукового устройства.
  • Регулярная очистка кеша важна для уровней с большим количеством динамических ресурсов.

Использование Pool для повторного использования объектов

Для динамических объектов с частой генерацией и уничтожением (снаряды, враги) рекомендуется использовать object pooling вместо постоянного destroy() и create(). Phaser не имеет встроенного пула, но его можно реализовать через Group:

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

function fireBullet(x, y) {
    let bullet = bullets.get(x, y, 'bullet');
    if (!bullet) return; // если пул исчерпан
    bullet.setActive(true).setVisible(true);
}

Преимущества:

  • Снижение нагрузки на сборщик мусора.
  • Повышение производительности при массовых объектах.
  • Легкость управления активными и неактивными объектами через setActive и setVisible.

Рекомендации по оптимизации памяти

  • Уничтожать объекты, которые больше не используются на сцене.
  • Отвязывать события и интерактивные элементы.
  • Использовать clear(true, true) для массового удаления дочерних объектов.
  • Контролировать кеш текстур и аудио, удаляя ненужные ресурсы.
  • Применять пул объектов для часто создаваемых и уничтожаемых объектов.

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