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

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

Версия 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('mask', 'assets/pics/mask-test2.png');
        this.load.image('pic', 'assets/pics/hotshot-chaos-in-tokyo.png');
    }

    create ()
    {
        const texture = this.textures.addDynamicTexture('maskedPic', 368, 290);

        const pic = this.make.image({ key: 'pic', origin: { x: 0, y: 0 }, add: true });
        const maskImage = this.make.image({ key: 'mask', origin: { x: 0, y: 0 }, add: false });

        pic.enableFilters().filters.external.addMask(maskImage);

        texture.draw(pic).render();

        this.add.sprite(560, 300, 'maskedPic');
    }
}

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

const game = new Phaser.Game(config);

Загрузка ресурсов и подготовка сцены

Работа начинается с загрузки двух изображений: основного изображения (pic) и изображения, которое будет выступать в роли маски (mask). Важно, чтобы маска была изображением в градациях серого, где белые области будут пропускать изображение, а черные — скрывать его.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('mask', 'assets/pics/mask-test2.png');
    this.load.image('pic', 'assets/pics/hotshot-chaos-in-tokyo.png');
}

Метод setBaseURL задает базовый путь для загрузки, что упрощает указание относительных путей к файлам.

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

Динамическая текстура — это специальный тип текстуры в Phaser, который можно рисовать программно. Мы создаем её с помощью метода this.textures.addDynamicTexture. Первым аргументом передается ключ, по которому текстура будет доступна в кэше текстур, затем — её ширина и высота.

const texture = this.textures.addDynamicTexture('maskedPic', 368, 290);

Размеры текстуры (368x290) в данном примере соответствуют размеру изображения-маски, что гарантирует корректное наложение.

Подготовка изображений и наложение маски

Далее мы создаем два игровых объекта Image из загруженных текстур. Ключевой момент — объект pic создается с флагом add: true, что автоматически добавляет его на сцену, а объект maskImage — с add: false, так как он нужен только как источник данных для маски и не должен отображаться сам по себе.

const pic = this.make.image({ key: 'pic', origin: { x: 0, y: 0 }, add: true });
const maskImage = this.make.image({ key: 'mask', origin: { x: 0, y: 0 }, add: false });

Затем для изображения pic активируется система фильтров, и к нему добавляется внешняя маска, источником которой выступает maskImage.

pic.enableFilters().filters.external.addMask(maskImage);

Метод enableFilters() включает поддержку фильтров для объекта, а filters.external.addMask() применяет маску. Теперь pic будет отображаться только в тех областях, которые определяет маска.

Рисование в текстуру и рендеринг результата

Сама по себе динамическая текстура — это чистый холст. Метод draw позволяет нарисовать на этом холсте любой отображаемый объект (в нашем случае — изображение pic с уже примененной маской). Однако, просто вызвав draw, мы лишь записываем команду. Чтобы визуализировать изменения и сделать текстуру готовой к использованию, необходимо вызвать метод render.

texture.draw(pic).render();

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

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

Теперь созданную динамическую текстуру можно использовать как любую другую загруженную текстуру. В примере из неё создается спрайт и добавляется в центр сцены.

this.add.sprite(560, 300, 'maskedPic');

Это демонстрирует основное преимущество подхода: один раз создав сложный составной графический элемент (изображение + маска), мы можем использовать его как единый, готовый ассет в любом месте игры, без повторных вычислений.

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

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