О чем этот пример
Работа с группами (Group) — основа управления множеством игровых объектов в Phaser 3. Однако их уничтожение может привести к неочевидным ошибкам, если не понимать внутреннюю логику движка. Этот пример наглядно демонстрирует, как метод `destroy()` работает с группой и её дочерними элементами, и почему это важно для предотвращения утечек памяти и корректного завершения работы сцены.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
var config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000000',
parent: 'phaser-example',
scene: {
preload: preload,
create: create
}
};
var game = new Phaser.Game(config);
function preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.spritesheet('invader2', 'assets/tests/invaders/invader2.png', { frameWidth: 44, frameHeight: 32 });
}
function create ()
{
var invaders = this.add.group([
{ key: 'invader2', frame: 0, repeat: 10, setXY: { x: 32, y: 148, stepX: 52 } },
{ key: 'invader2', frame: 0, repeat: 10, setXY: { x: 32, y: 148 + 48, stepX: 52 } }
]);
invaders.runChildUpdate = true;
Phaser.Actions.IncX(invaders.getChildren(), 100);
Phaser.Actions.SetTint(invaders.getChildren(), 0x00ff00);
this.input.once('pointerdown', () => {
console.log('Group destroyed');
invaders.destroy();
});
}
Создание группы инопланетных захватчиков
В примере создаётся группа (Group) спрайтов на основе одного листа спрайтов. Конфигурация группы позволяет сразу создать и расположить несколько объектов.
var invaders = this.add.group([
{ key: 'invader2', frame: 0, repeat: 10, setXY: { x: 32, y: 148, stepX: 52 } },
{ key: 'invader2', frame: 0, repeat: 10, setXY: { x: 32, y: 148 + 48, stepX: 52 } }
]);
Каждый объект в массиве конфигурации создаёт ряд спрайтов. Параметр repeat: 10 указывает, что нужно создать 11 спрайтов (оригинал + 10 повторов). setXY автоматически расставляет их с заданным шагом по X. В итоге группа invaders содержит 22 дочерних спрайта.
Модификация группы и её детей
После создания мы настраиваем группу и применяем действия ко всем её членам.
invaders.runChildUpdate = true;
Установка runChildUpdate в true гарантирует, что метод update() будет вызываться для каждого дочернего спрайта в группе на каждом кадре, даже если у них нет собственной логики обновления.
Phaser.Actions.IncX(invaders.getChildren(), 100);
Phaser.Actions.SetTint(invaders.getChildren(), 0x00ff00);
Phaser.Actions.IncX сдвигает всех детей группы на 100 пикселей по оси X. Phaser.Actions.SetTint применяет зелёный оттенок (0x00ff00) ко всем спрайтам. Метод invaders.getChildren() возвращает массив всех дочерних объектов, с которым и работают эти действия.
Критический момент: уничтожение группы
По клику мыши группа полностью уничтожается.
this.input.once('pointerdown', () => {
console.log('Group destroyed');
invaders.destroy();
});
Вызов invaders.destroy() — ключевой момент. Этот метод выполняет несколько важных действий:
1. Рекурсивно вызывает destroy() для каждого дочернего объекта в группе. Это освобождает текстуры, отключает физические тела, удаляет слушатели событий и убирает объекты из диспетчера обновления сцены.
2. Очищает внутренние массивы и ссылки внутри самой группы.
3. Удаляет группу из родительского контейнера (в данном случае из сцены).
Важно понимать, что после этого вызова группа и все её дети больше не существуют в игровом мире. Любые последующие попытки обратиться к invaders или её детям приведут к ошибкам.
Почему `destroy()`, а не `clear()` или `removeAll()`?
Phaser предоставляет несколько методов для работы с содержимым группы:
- clear(): Удаляет всех детей из группы, но **не уничтожает их**. Объекты остаются в памяти и на сцене, просто перестают быть частью этой группы.
- removeAll(): Аналогично clear(), но с возможностью передать параметр destroyChild. Если destroyChild равен false (по умолчанию), объекты лишь удаляются из группы.
- destroy(): Полностью уничтожает саму группу и, что критично, **всех её детей**.
В данном примере цель — полная очистка, поэтому используется destroy(). Использование clear() без последующего уничтожения детей привело бы к утечке памяти, так как 22 спрайта остались бы висеть на сцене без возможности получить к ним ссылку для последующего удаления.
Что попробовать дальше
Метод destroy() для группы — это правильный способ полностью очистить ресурсы, когда группа объектов больше не нужна. Для экспериментов попробуйте заменить invaders.destroy() на invaders.clear() и проверьте, останутся ли спрайты на экране. Или используйте invaders.removeAll(true), где true заставит метод уничтожить детей, но сама группа останется жива и сможет быть повторно использована для добавления новых объектов.
