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

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

Версия 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('banner', 'assets/atlas/banners.png', 'assets/atlas/banners.json');
        this.load.image('bubble', 'assets/particles/bubble.png');
        this.load.image('birdie', 'assets/pics/birdy.png');
    }

    create ()
    {
        const graphics = this.add.graphics();
        graphics.lineStyle(1, 0xff0000);

        const scale = 0.2;
        const birdie1 = this.add.sprite(0, 0, 'birdie')
            .setScale(scale)
            .setOrigin(0, 0);

        const glowController = birdie1.preFX.addGlow(0xffff00, 4, 0, false, 0.1, 32);
        // const blurController = birdie1.preFX.addBlur();
        // const barrelController = birdie1.preFX.addBarrel(2);
        // const bloomController = birdie1.preFX.addBloom(0xffffff, 1, 1, 2, 1.2);
        // const cmController = birdie1.preFX.addColorMatrix().night();
        // const displacementController = birdie1.preFX.addDisplacement('distort', -0.03, -0.03);
        // const gradientController = birdie1.preFX.addGradient(0x0000ff, 0x00ff00, 0);
        // const pixelateController = birdie1.preFX.addPixelate(5);
        // const shadowController = birdie1.preFX.addShadow(0, 0, 0.006, 2, 0x999999, 10);
        // const vignetteController = birdie1.preFX.addVignette(0.5, 0.5, 0.3, 0.5);
        // const shineController = birdie1.postFX.addShine(1, .2, 5);
        // console.log(glowController);
        
        const texture1 = this.textures.addDynamicTexture("birdie_glow1", 800, 600); //500, 334
        texture1.draw(birdie1, 0, 0);

        const texture2 = this.textures.addDynamicTexture("birdie_glow2", birdie1.width * scale, birdie1.height * scale);
        texture2.stamp('birdie_glow1', null, 400, 300);

        const clone1 = this.add.sprite(100, 100, "birdie_glow1").setOrigin(0);
        const bounds1 = clone1.getBounds();
        graphics.strokeRectShape(bounds1);

        const clone2 = this.add.sprite(400, 300, "birdie_glow2")
        const bounds2 = clone2.getBounds();
        graphics.strokeRectShape(bounds2);

        birdie1.destroy();
    }
}

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

const game = new Phaser.Game(config);

Подготовка ресурсов и создание базового спрайта

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

Затем создаётся спрайт birdie1 с оригинальным изображением. Он масштабируется и выравнивается по левому верхнему углу. Важный момент — к этому спрайту сразу применяется эффект предварительного рендеринга (Pre-FX) в виде свечения.

const glowController = birdie1.preFX.addGlow(0xffff00, 4, 0, false, 0.1, 32);

Метод addGlow объекта preFX добавляет жёлтое свечение с внешним радиусом 4, качеством 32 и интенсивностью 0.1. Контроллер glowController позволяет позднее управлять параметрами эффекта. Остальные закомментированные эффекты (размытие, бочкообразное искажение и т.д.) демонстрируют богатые возможности системы FX Phaser.

Создание динамической текстуры из спрайта с эффектами

Динамическая текстура — это CanvasTexture, которую можно создавать и изменять во время выполнения игры. В примере создаётся первая текстура birdie_glow1 с фиксированными размерами 800x600 пикселей.

const texture1 = this.textures.addDynamicTexture("birdie_glow1", 800, 600);
texture1.draw(birdie1, 0, 0);

Метод draw текстуры рендерит спрайт birdie1 с уже применённым эффектом свечения в координаты (0, 0) этой текстуры. Таким образом, мы «запекаем» спрайт вместе с его визуальными эффектами в отдельное растровое изображение.

Зачем это нужно? Рендеринг FX-эффектов на каждом кадре требует вычислений. Если объект статичен или эффект не меняется, эффективнее отрендерить его один раз в текстуру и затем использовать её как лёгкий спрайт.

Копирование и штамповка между текстурами

Phaser позволяет не только рисовать в текстуру, но и копировать данные из одной текстуры в другую с помощью метода stamp. Создаётся вторая динамическая текстура birdie_glow2. Её размеры вычисляются на основе исходных размеров спрайта и его масштаба.

const texture2 = this.textures.addDynamicTexture("birdie_glow2", birdie1.width * scale, birdie1.height * scale);
texture2.stamp('birdie_glow1', null, 400, 300);

Метод stamp копирует содержимое текстуры birdie_glow1 в текстуру birdie_glow2. Параметры 400, 300 задают точку в текстуре-источнике, откуда будет взято изображение для копирования. По сути, это вырезка части большой текстуры. Второй параметр null означает, что используется весь кадр текстуры-источника (полезно при работе с атласами).

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

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

const clone1 = this.add.sprite(100, 100, "birdie_glow1").setOrigin(0);
const clone2 = this.add.sprite(400, 300, "birdie_glow2");

Спрайт clone1 использует всю большую текстуру 800x600. Спрайт clone2 использует маленькую текстуру, которая является вырезкой. Графический объект graphics обводит границы (bounds) этих спрайтов красными прямоугольниками, что наглядно показывает их реальные размеры на сцене.

Исходный спрайт birdie1 с эффектами после этого уничтожается (destroy()), так как его визуальное представление уже «запечено» в текстуры и он больше не нужен. Это освобождает ресурсы.

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

Динамические текстуры в Phaser — это мощный инструмент для оптимизации рендеринга статичных или редко меняющихся визуальных элементов, особенно с применением дорогостоящих FX-эффектов. Вы можете создать библиотеку предварительно обработанных изображений прямо во время выполнения игры. **Идеи для экспериментов:** 1. Анимируйте параметры эффекта свечения через glowController и периодически перерисовывайте динамическую текстуру, создавая пульсирующий объект. 2. Используйте stamp для сборки сложного спрайта (например, персонажа с экипировкой) из нескольких текстур атласа в одну. 3. Примените другие PostFX или PreFX эффекты к спрайту перед рендерингом в текстуру, чтобы увидеть, как комбинируются разные фильтры.