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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    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++)
        {
            this.add.image(Phaser.Math.Between(100, 700), Phaser.Math.Between(100, 500), 'bubbleboy');
        }
    }
}

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

const game = new Phaser.Game(config);

Что такое Render Texture?

Класс Phaser.GameObjects.RenderTexture — это специальный игровой объект, который выступает в роли холста или буфера в памяти. Вы можете отрисовывать на нём другие игровые объекты (изображения, спрайты, текст), но сам он невидим для игрока. Его основная цель — служить промежуточным этапом для создания графики.

В нашем примере мы создаём Render Texture размером 100x100 пикселей. Ключевой параметр false в конструкторе указывает, что текстура не будет добавлена на дисплейный список сцены автоматически. Она существует только в памяти.

Рисование и сохранение текстуры

После создания буфера мы наполняем его содержимым, используя метод .draw(). Этот метод принимает ключ текстуры и координаты для отрисовки относительно левого верхнего угла Render Texture.

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

Здесь мы сначала рисуем изображение пузыря (bubble), а затем поверх него — спрайт персонажа (dude). Оба рисуются в центре нашей текстуры (50, 50).

Метод .render() финализирует все операции рисования в буфере. После этого критически важный шаг — метод .saveTexture(). Он берёт текущее содержимое Render Texture и регистрирует его в текстовом менеджере игры (this.textures) под новым, заданным вами ключом. Теперь к этому составному изображению можно обращаться как к любой другой загруженной текстуре.

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

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

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

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

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

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

Код примера завершается стандартной для Phaser конфигурацией. Обратите внимание, что фон сцены (backgroundColor) задан тёмно-синим, чтобы созданные белые изображения хорошо контрастировали.

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

const game = new Phaser.Game(config);

Инициализация игры с этой конфигурацией запускает нашу сцену Example, где и происходит вся магия с Render Texture.

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

Render Texture с последующим сохранением в saveTexture — это мощный дуэт для динамического создания ресурсов игры прямо во время её выполнения. Вы можете комбинировать анимации, текст, эффекты и сохранять результат как статичную текстуру для повторного использования. **Идеи для экспериментов:** 1. Попробуйте рисовать на Render Texture не статичные изображения, а анимированные спрайты (this.rt.drawFrame('spriteKey', frameName)), чтобы запечатлеть определённый кадр. 2. Используйте графические примитивы (this.rt.fillRect, this.rt.strokeCircle) из контекста RenderTexture для программного создания текстур интерфейса (кнопок, панелей). 3. Поэкспериментируйте с альфа-каналом и blend-режимами при рисовании, чтобы создавать сложные полупрозрачные комбинации.