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

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

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


class Example extends Phaser.Scene
{
    constructor ()
    {
        super();
    }

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.spritesheet('diamonds', 'assets/sprites/diamonds32x5.png', { frameWidth: 64 });
    }

    create ()
    {
        //  Create 108 sprites in a grid so we can see them vanish easily
        const group = this.make.group({
            key: 'diamonds',
            frame: [ 0, 1, 2, 3, 4 ],
            frameQuantity: 22,
            max: 108
        });

        Phaser.Actions.GridAlign(group.getChildren(), {
            width: 12,
            height: 9,
            cellWidth: 64,
            cellHeight: 64,
            x: 48,
            y: 32
        });

        //  Remove one child from the display list every half a second
        const timedEvent = this.time.addEvent({
            delay: 500,
            callback: this.onEvent,
            callbackScope: this,
            loop: true
        });
    }

    onEvent ()
    {
        const child = this.children.getRandom();
        if (child)
        {
            this.children.remove(child);
        }
    }
}

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

const game = new Phaser.Game(config);

Создание группы спрайтов для наглядности

Прежде чем удалять объекты, нужно их создать. В примере мы создаем 108 спрайтов в виде сетки, чтобы визуально наблюдать их исчезновение. Для этого используется группа (Phaser.GameObjects.Group), которая упрощает управление множеством однотипных объектов.

const group = this.make.group({
    key: 'diamonds',
    frame: [ 0, 1, 2, 3, 4 ],
    frameQuantity: 22,
    max: 108
});

Здесь this.make.group создает группу на основе спрайтшита diamonds. Параметры frame и frameQuantity определяют, какие кадры спрайтшита использовать и сколько раз их повторить (всего 5 кадров × 22 повторения = 110, но max ограничивает до 108). Группа автоматически добавляет созданные спрайты в список детей сцены (this.children).

После создания спрайты выравниваются в сетку с помощью Phaser.Actions.GridAlign, что делает их удаление более наглядным.

Phaser.Actions.GridAlign(group.getChildren(), {
    width: 12,
    height: 9,
    cellWidth: 64,
    cellHeight: 64,
    x: 48,
    y: 32
});

Таймер для периодического удаления объектов

Чтобы демонстрировать удаление в реальном времени, в примере используется таймер (Phaser.Time.TimerEvent). Он вызывает функцию обратного вызова каждые 500 миллисекунд (полсекунды), что создает эффект постепенного исчезновения спрайтов.

const timedEvent = this.time.addEvent({
    delay: 500,
    callback: this.onEvent,
    callbackScope: this,
    loop: true
});

Ключевые параметры: - delay: интервал между вызовами в миллисекундах. - callback: функция, которая будет выполняться (this.onEvent). - callbackScope: контекст this для callback (здесь — текущая сцена). - loop: если true, событие повторяется бесконечно.

Этот подход полезен для игровых механик, например, для автоматического удаления снарядов через время или исчезновения эффектов.

Удаление случайного спрайта из списка детей

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

onEvent ()
{
    const child = this.children.getRandom();
    if (child)
    {
        this.children.remove(child);
    }
}
Разберем код:
- `this.children.getRandom()` — метод, возвращающий случайный дочерний объект сцены (из всех, включая спрайты, тексты и другие игровые объекты). В нашем случае это один из 108 созданных спрайтов.
- `this.children.remove(child)` — удаляет выбранный объект из списка отображения сцены. После этого спрайт больше не рендерится и не обновляется, но он остается в памяти как объект JavaScript.

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

Конфигурация игры и особенности

Пример использует стандартную конфигурацию Phaser с некоторыми настройками для визуальной четкости.

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

Особенности: - type: Phaser.WEBGL — использует WebGL-рендерер для лучшей производительности. - pixelArt: true — включает сглаживание для пиксельной графики, что важно для спрайтов в стиле ретро. - scene: Example — указывает класс сцены, где происходит вся логика.

После создания экземпляра игры (new Phaser.Game(config)) автоматически запускается сцена, инициализируя прелоад, создание объектов и таймер.

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

Удаление объектов из списка отображения через this.children.remove — это эффективный способ управления видимостью без полного уничтожения. Этот подход оптимизирует производительность, особенно в динамичных играх с множеством объектов. Для экспериментов попробуйте

  1. Удалять объекты по клику мыши
  2. Восстанавливать удаленные спрайты через this.children.add
  3. Использовать destroy вместо remove для полной очистки памяти
  4. Добавить анимацию исчезновения перед удалением