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

В Phaser игровые объекты автоматически добавляются в сцену при создании через фабричные методы типа `this.add.image()`. Но что, если нам нужно создать объект через `new` и вручную управлять его появлением? В этой статье мы разберем метод `sprite.addToDisplayList()`, который позволяет добавлять созданные вручную спрайты в систему отображения сцены. Это полезно для продвинутого управления жизненным циклом объектов, их пулинга или интеграции с кастомными системами.

Версия 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.image('beer', 'assets/sprites/beer.png');
        this.load.image('watermelon', 'assets/sprites/watermelon.png');
        this.load.image('cake', 'assets/sprites/cake.png');
    }

    create ()
    {
        this.add.text(10, 10, 'Click to add a Sprite').setDepth(1);

        const size = this.add.text(10, 32, 'Display List size: 2').setDepth(1);

        this.input.on('pointerdown', pointer => {

            const x = pointer.worldX;
            const y = pointer.worldY;

            const sprite = new Phaser.GameObjects.Sprite(this, x, y, 'watermelon');

            sprite.addToDisplayList();

            size.setText('Display List size: ' + this.children.length);

        });
    }
}

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

const game = new Phaser.Game(config);

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

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

// Стандартный способ. Объект сразу виден.
const sprite = this.add.image(100, 100, 'beer');

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

// Ручное создание. Объект НЕ добавлен в сцену и не отображается.
const sprite = new Phaser.GameObjects.Sprite(this, 100, 100, 'watermelon');

Волшебный метод addToDisplayList()

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

// Создаем объект вручную
const sprite = new Phaser.GameObjects.Sprite(this, x, y, 'watermelon');
// Добавляем его в системы отображения и обновления сцены
sprite.addToDisplayList();

После вызова этого метода спрайт начинает отрисовываться и получать вызовы update (если он активен). В примере из статьи мы делаем это по клику мыши, добавляя новый спрайт в позиции курсора.

Порядок и проверка добавления

Метод addToDisplayList() можно вызывать только у объектов, которые еще не добавлены в сцену. Повторный вызов не даст эффекта. После добавления объект попадает в массив this.children, который хранит все дочерние объекты сцены.

// Обновляем текст, чтобы показать размер списка детей сцены
size.setText('Display List size: ' + this.children.length);

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

Практическое применение: пул объектов

Главная практическая польза addToDisplayList() и его парного метода removeFromDisplayList() — создание пулов (pools) объектов. Это оптимизирует производительность, позволяя переиспользовать объекты вместо их постоянного создания и удаления.

// Примерный каркас для пула
class SpritePool {
    constructor(scene, texture) {
        this.scene = scene;
        this.texture = texture;
        this.pool = []; // Массив неактивных спрайтов
    }

    get(x, y) {
        let sprite = this.pool.pop();
        if (!sprite) {
            // Создаем новый объект вручную
            sprite = new Phaser.GameObjects.Sprite(this.scene, x, y, this.texture);
        } else {
            // Переиспользуем существующий
            sprite.setPosition(x, y);
            sprite.setActive(true).setVisible(true);
            sprite.addToDisplayList(); // Возвращаем в сцену
        }
        return sprite;
    }

    release(sprite) {
        sprite.removeFromDisplayList(); // Убираем из сцены
        sprite.setActive(false).setVisible(false);
        this.pool.push(sprite); // Отправляем в пул
    }
}

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

Метод addToDisplayList() — это ключ к низкоуровневому контролю над игровыми объектами в Phaser. Он открывает дорогу для оптимизаций через пулы объектов, сложных композиций и кастомных систем управления сценой. Для экспериментов попробуйте создать систему частиц, где каждая частица будет создаваться через new и добавляться в сцену по мере необходимости, или реализуйте сложный интерфейс с динамически собираемыми из компонентов элементами.