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

Phaser предлагает мощный объект `RenderTexture` для динамической отрисовки графики прямо во время выполнения игры. В этой статье мы разберем, как использовать его как «холст» для компоновки множества спрайтов из атласа в один сложный кадр. Этот подход особенно полезен для создания уникальных текстур для уровней, генерации случайных фонов или предварительного рендеринга сложных статичных объектов, что может повысить производительность, сократив количество отрисовываемых объектов.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.atlas('megaset', 'assets/atlas/megaset-0.png', 'assets/atlas/megaset-0.json');
        this.load.image('uv', 'assets/pics/uv-grid-diag.png');
    }

    create ()
    {
        const rt = this.add.renderTexture(400, 300, 800, 600);

        const atlasTexture = this.textures.get('megaset');

        const frames = atlasTexture.getFrameNames();

        for (let i = 0; i < frames.length; i++)
        {
            const x = Phaser.Math.Between(0, 800);
            const y = Phaser.Math.Between(0, 600);

            rt.stamp('megaset', frames[i], x, y);
        }
        rt.render();
    }
}

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

const game = new Phaser.Game(config);

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

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

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

В нашем примере мы создадим такую текстуру и заполним ее случайными кадрами из загруженного атласа.

Загрузка ресурсов: атлас и изображение

Работа начинается с метода preload. Здесь загружаются два ресурса: атлас спрайтов и обычное изображение. Обратите внимание, что this.load.setBaseURL устанавливает базовый URL для упрощения путей к файлам.

Атлас 'megaset' — это набор множества небольших изображений (спрайтов), упакованных в один PNG-файл и описанный в JSON-файле. Загрузка такого атласа экономит количество HTTP-запросов и улучшает производительность.

Изображение 'uv' загружается, но в данном примере не используется прямо — оно служит демонстрацией того, что можно загружать и другие ресурсы.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.atlas('megaset', 'assets/atlas/megaset-0.png', 'assets/atlas/megaset-0.json');
    this.load.image('uv', 'assets/pics/uv-grid-diag.png');
}

Создание рендер-текстуры и получение кадров

В методе create происходит вся основная логика. Сначала создается сама рендер-текстура с помощью this.add.renderTexture. Ее параметры: координаты центра (400, 300) и размеры (800x600 пикселей).

Затем мы получаем ссылку на текстуру атласа через менеджер текстур: this.textures.get('megaset'). Из этой текстуры можно извлечь имена всех отдельных кадров с помощью метода getFrameNames(). Этот метод возвращает массив строк, где каждая строка — уникальный идентификатор спрайта внутри атласа.

create ()
{
    const rt = this.add.renderTexture(400, 300, 800, 600);

    const atlasTexture = this.textures.get('megaset');
    const frames = atlasTexture.getFrameNames();
    // ...
}

Рисование кадров на текстуру с помощью Stamp

Ключевой метод в этом примере — rt.stamp(). Он «штампует» (отрисовывает) указанный кадр на рендер-текстуру в заданных координатах. Мы проходим в цикле по всем именам кадров, полученным из атласа.

Для каждого кадра генерируются случайные координаты `xиyв пределах размеров рендер-текстуры с помощьюPhaser.Math.Between`. Это гарантирует, что все спрайты будут разбросаны по всей площади холста.

Важно понимать, что метод stamp принимает ключ текстуры ('megaset') и конкретное имя кадра из этой текстуры.

for (let i = 0; i < frames.length; i++)
{
    const x = Phaser.Math.Between(0, 800);
    const y = Phaser.Math.Between(0, 600);
    rt.stamp('megaset', frames[i], x, y);
}

Финализация рендера

После того как все нужные объекты отрисованы на рендер-текстуру с помощью stamp, необходимо вызвать метод render(). Этот метод финализирует процесс и делает текстуру готовой к отображению.

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

Обратите внимание, что рендер-текстура уже добавлена на сцену при создании, поэтому дополнительный вызов this.add.existing не требуется.

rt.render();

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

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