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

Создание сложных визуальных эффектов, таких как вырезание части изображения по сложной форме, часто требует подготовки масок в графических редакторах. В Phaser 3 вы можете генерировать маски динамически прямо во время выполнения игры, используя `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('bg', 'assets/skies/underwater1.png');
        this.load.image('bg2', 'assets/skies/toxic.png');
        this.load.image('skull', 'assets/pics/skull.png');
    }

    create ()
    {
        this.add.image(400, 300, 'bg');

        const banner = this.textures.addDynamicTexture('skullTexture', 800, 600)

        banner.stamp('skull', null, 400, 300).render();

        const image = this.add.image(400, 300, 'bg2');
        image.enableFilters();
        image.filters.external.addMask('skullTexture');
    }
}

const config = {
    type: Phaser.WEBGL,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    scene: Example
};

const game = new Phaser.Game(config);

Основная идея: маска из динамической текстуры

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

В предоставленном примере создается динамическая текстура, на которую "штампуется" изображение черепа. Затем эта текстура применяется как внешняя маска-фильтр к другому фоновому изображению. В результате второе изображение (bg2) видно только в тех пикселях, где на текстуре-маске есть череп.

Создание и наполнение DynamicTexture

Первый шаг — создание пустой "холста" — динамической текстуры заданного размера. Для этого используется метод this.textures.addDynamicTexture(key, width, height).

Затем на эту текстуру нужно что-то нанести, чтобы она не была пустой. В примере используется метод .stamp(), который помещает изображение в центр текстуры. Важно вызвать .render(), чтобы изменения применились.

const banner = this.textures.addDynamicTexture('skullTexture', 800, 600);
banner.stamp('skull', null, 400, 300).render();

- 'skullTexture' — это уникальный ключ, по которому мы позже обратимся к текстуре для маски. - stamp('skull', null, 400, 300) — размещает изображение с ключом 'skull' в центре текстуры (координаты 400x300). Второй параметр null означает, что используется весь кадр изображения (frame). - .render() — финализирует операцию и делает текстуру готовой к использованию.

Применение текстуры как маски для фильтра

Чтобы применить созданную текстуру как маску, нам нужен игровой объект, поддерживающий фильтры, например Image. Сначала нужно активировать для него систему фильтров с помощью метода .enableFilters().

Затем мы обращаемся к коллекции фильтров объекта image.filters и добавляем внешнюю маску. Ключевой метод здесь — filters.external.addMask(key).

const image = this.add.image(400, 300, 'bg2');
image.enableFilters();
image.filters.external.addMask('skullTexture');

- image.enableFilters() — включает поддержку фильтров WebGL для этого конкретного объекта. - image.filters.external.addMask('skullTexture') — указывает, что в качестве маски для фильтра объекта image должна использоваться текстура с ключом 'skullTexture'. Теперь изображение bg2 будет обрезано по форме черепа, нарисованного на этой текстуре.

Важно: исходный код примера загружает два фона. Первый (bg) добавляется обычным образом и служит нижним слоем. Второй (bg2) с маской добавляется поверх, создавая эффект "окна" в форме черепа, через которое виден первый фон.

Конфигурация проекта и сцена

Для работы с фильтрами и динамическими текстурами требуется рендерер WebGL. Это задается в конфигурации игры.

const config = {
    type: Phaser.WEBGL, // Обязательно WEBGL, а не CANVAS
    parent: 'phaser-example',
    width: 800,
    height: 600,
    scene: Example
};
const game = new Phaser.Game(config);

Класс сцены Example в своем методе create выполняет всю описанную логику: создает фон, динамическую текстуру-маску и применяет ее ко второму изображению. Метод preload загружает необходимые статические ресурсы с удаленного URL.

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

Использование DynamicTexture в качестве битмаск-маски открывает путь к созданию нестатичных визуальных эффектов. Вы можете менять содержимое текстуры во время игры (добавлять новые штампы, рисовать примитивы), и маска будет обновляться в реальном времени. **Идеи для экспериментов:** 1. Анимируйте маску: изменяйте текстуру в цикле update, рисуя на ней окружности или прямоугольники с помощью banner.fillRect или banner.stamp с другими изображениями. 2. Создайте интерактивную маску: очищайте текстуру (banner.clear()) и штампуйте изображение в позиции курсора мыши. 3. Комбинируйте несколько масок или используйте текстуру, сгенерированную из части тайловой карты (Tilemap).