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

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

Версия 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('card', 'assets/pics/slug.png');
    }

    create ()
    {
        //  card image is 256 x 256 in size
        const area1 = this.textures.addDynamicTexture('area1', 128, 128);
        const area2 = this.textures.addDynamicTexture('area2', 128, 128);
        const area3 = this.textures.addDynamicTexture('area3', 128, 128);
        const area4 = this.textures.addDynamicTexture('area4', 128, 128);

        area1.fill(0x00ff00);
        area2.fill(0x00ff00);
        area3.fill(0x00ff00);
        area4.fill(0x00ff00);

        area1.stamp('card', null, 128, 128);
        area2.stamp('card', null, 128, 128);
        area3.stamp('card', null, 128, 128);
        area4.stamp('card', null, 128, 128);

        area1.render();
        area2.render();
        area3.render();
        area4.render();

        this.add.sprite(100, 100, 'area1').setOrigin(0);
        this.add.sprite(100 + 129, 100, 'area2').setOrigin(0);
        this.add.sprite(100, 100 + 129, 'area3').setOrigin(0);
        this.add.sprite(100 + 129, 100 + 129, 'area4').setOrigin(0);

        this.add.sprite(400, 100, 'card').setOrigin(0);
    }
}

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

const game = new Phaser.Game(config);

Что такое DynamicTexture?

DynamicTexture – это текстура, создаваемая в памяти во время выполнения игры. Она ведет себя как обычный ключ текстуры (например, загруженное изображение), но ее пиксели можно программно изменять.

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

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

Создание и настройка динамических текстур

Первым делом, в методе preload, загружается исходное изображение card. Далее, в create, создаются четыре динамические текстуры.

Ключевой метод – this.textures.addDynamicTexture(key, width, height). Он регистрирует новую текстуру в менеджере текстур с заданным ключом и размерами.

const area1 = this.textures.addDynamicTexture('area1', 128, 128);
const area2 = this.textures.addDynamicTexture('area2', 128, 128);
const area3 = this.textures.addDynamicTexture('area3', 128, 128);
const area4 = this.textures.addDynamicTexture('area4', 128, 128);

Каждая текстура имеет размер 128x128 пикселей. После создания, методом fill мы заливаем каждую текстуру сплошным зеленым цветом (0x00ff00). Это отличный способ задать фон или очистить текстуру перед работой.

area1.fill(0x00ff00);

Метод stamp: копирование изображений

Самый интересный этап – перенос содержимого одной текстуры на другую. Для этого используется метод stamp.

area1.stamp('card', null, 128, 128);

Разберем его параметры: 1. 'card' – ключ исходной текстуры (загруженное изображение). 2. null – здесь можно передать индекс кадра или имя кадра, если используется спрайтшит. В нашем случае это не требуется. 3. 128, 128 – координаты X и Y **на динамической текстуре**, куда будет помещен **левый верхний угол** копируемого изображения.

Важный нюанс: исходное изображение card имеет размер 256x256. Метод stamp не масштабирует изображение автоматически. Он копирует пиксели из исходной текстуры в целевую, начиная с заданных координат. Если исходное изображение больше целевой текстуры, оно будет обрезано. В нашем случае, так как координаты установлены в (128, 128), а размер динамической текстуры тоже 128x128, мы скопируем только нижний правый квадрант (четверть) исходной картинки в каждый из наших зеленых квадратов.

Рендеринг и отображение на сцене

После всех операций рисования динамическую текстуру необходимо отрендерить в ее внутренний буфер. Для этого вызывается метод render().

area1.render();
area2.render();
area3.render();
area4.render();

Без этого вызова изменения (в нашем случае fill и stamp) не будут применены к финальному изображению текстуры.

Теперь текстуры готовы к использованию как обычные ресурсы. Мы создаем спрайты, указывая ключи наших динамических текстур ('area1', 'area2' и т.д.). Метод setOrigin(0) устанавливает точку привязки спрайта в его левый верхний угол, что упрощает позиционирование в сетке.

this.add.sprite(100, 100, 'area1').setOrigin(0);
this.add.sprite(100 + 129, 100, 'area2').setOrigin(0);
this.add.sprite(100, 100 + 129, 'area3').setOrigin(0);
this.add.sprite(100 + 129, 100 + 129, 'area4').setOrigin(0);

Смещение на 129 пикселей (128 размер + 1 пиксель промежуток) создает аккуратную сетку 2x2. Для сравнения, справа отображается оригинальное изображение card.

this.add.sprite(400, 100, 'card').setOrigin(0);

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

DynamicTexture – это фундаментальный инструмент для продвинутой работы с графикой в Phaser. Мы научились создавать текстуры, заливать их цветом, копировать части других изображений и отображать результат. Этот механизм лежит в основе создания динамических спрайтшитов, рисования интерфейсов, генерации ландшафтов и многих других задач. **Идеи для экспериментов:** 1. Измените координаты в методе stamp (например, на 0, 0), чтобы копировать разные части исходного изображения. 2. Используйте fill с разными цветами для каждой текстуры перед штамповкой. 3. Создайте одну большую DynamicTexture и соберите на ней целую карту из маленьких тайлов. 4. Исследуйте другие методы, такие как draw(frame, x, y) для рисования примитивов или clear() для очистки.