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

Класс Render Texture в Phaser позволяет создавать текстуры «на лету», комбинируя разные изображения и спрайты. Это мощный инструмент для генерации эффектов, пользовательского интерфейса или динамического контента. Однако при уничтожении Render Texture важно правильно управлять игровыми объектами, которые её используют, иначе можно столкнуться с ошибками рендеринга. Эта статья на практическом примере покажет, как безопасно удалять Render Texture и связанные с ней объекты.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    bubbles = [];
    rt;

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('bubble', 'assets/particles/bubble.png');
        this.load.image('dude', 'assets/sprites/phaser-dude.png');
    }

    create ()
    {
        this.rt = this.make.renderTexture({ width: 100, height: 100 }, false);

        this.rt.draw('bubble', 50, 50);
        this.rt.draw('dude', 50, 50);
        this.rt.render();

        const t = this.rt.saveTexture('bubbleboy');

        for (let i = 0; i < 100; i++)
        {
            const b = this.add.image(Phaser.Math.Between(100, 700), Phaser.Math.Between(100, 500), 'bubbleboy');

            this.bubbles.push(b);
        }

        this.input.on('pointerup', () =>
        {
            //  It is YOUR responsibility to remove or change the texture being used by
            //  any Game Objects that are using the Render Texture *before* you destroy it

            this.bubbles.forEach(image =>
            {
                image.destroy();
            });

            this.rt.destroy();

        });
    }
}

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

const game = new Phaser.Game(config);

Что такое Render Texture и зачем она нужна

Render Texture (текстура рендера) — это специальный тип текстуры в Phaser, которую можно создавать и модифицировать во время выполнения игры. В отличие от загруженных изображений, Render Texture генерируется программно, что позволяет рисовать на ней другие текстуры, спрайты или графику, а затем использовать результат как единое изображение.

Основные сценарии использования: * Динамическое создание комбинированных спрайтов (например, персонаж с предметом). * Генерация эффектов частиц или фонов. * Создание временных текстур для кастомного UI.

В предоставленном примере мы создадим одну Render Texture, нарисуем на ней два изображения («пузырёк» и «персонажа»), сохраним её как новую текстуру и создадим 100 игровых объектов, использующих эту сгенерированную текстуру.

Создание и наполнение Render Texture

В методе create() сцены создаётся и наполняется Render Texture. Ключевые этапы: 1. Создание Render Texture с заданными размерами (100x100 пикселей). 2. Последовательное рисование на ней двух загруженных изображений с помощью метода draw(). 3. Финализация текстуры вызовом render(). 4. Сохранение результата в текстуру кэша под именем 'bubbleboy' с помощью saveTexture().

this.rt = this.make.renderTexture({ width: 100, height: 100 }, false);
this.rt.draw('bubble', 50, 50);
this.rt.draw('dude', 50, 50);
this.rt.render();
const t = this.rt.saveTexture('bubbleboy');

После выполнения этого кода в менеджере текстур появляется новая текстура 'bubbleboy', которую можно использовать так же, как любую загруженную.

Использование сгенерированной текстуры

Как только текстура сохранена в кэше, её можно применять для создания игровых объектов. В примере создаётся 100 объектов Image, которые используют текстуру 'bubbleboy' и случайно размещаются на сцене.

for (let i = 0; i < 100; i++)
{
    const b = this.add.image(Phaser.Math.Between(100, 700), Phaser.Math.Between(100, 500), 'bubbleboy');
    this.bubbles.push(b);
}

Все созданные изображения добавляются в массив bubbles для последующего управления. Важно понимать, что теперь эти объекты жестко ссылаются на данные текстуры 'bubbleboy', которая, в свою очередь, зависит от исходной Render Texture this.rt.

Безопасное уничтожение Render Texture

Это ключевой момент. Нельзя просто взять и удалить (destroy) Render Texture, пока её данные используются какими-либо игровыми объектами. Если это сделать, объекты попытаются отрисовать несуществующую текстуру, что приведёт к ошибке.

Порядок безопасного удаления, показанный в обработчике клика: 1. Уничтожить (или изменить текстуру у) все игровые объекты, которые используют сгенерированную текстуру. 2. Только после этого уничтожить саму Render Texture.

this.input.on('pointerup', () =>
{
    //  Уничтожаем все объекты, использующие текстуру, ПЕРЕД удалением Render Texture
    this.bubbles.forEach(image =>
    {
        image.destroy();
    });
    this.rt.destroy();
});

Комментарий в коде прямо указывает: это ВАША ответственность — удалить или изменить текстуру у всех игровых объектов, использующих Render Texture, *до* её уничтожения.

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

Работа с Render Texture открывает возможности для динамического создания контента, но требует внимательного управления жизненным циклом. Главное правило: сначала освободите все зависимые объекты, затем удаляйте саму текстуру. Для экспериментов попробуйте: изменить порядок удаления (сначала rt.destroy()), чтобы увидеть ошибку; не уничтожать объекты, а сменить им текстуру на другую перед удалением rt; или создать несколько Render Texture и переключать их у объектов в реальном времени.