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

При создании игр часто требуется применять нестандартные визуальные эффекты к изображениям, например, маскировать их определённой формой. Phaser предоставляет мощный, но не часто используемый API для создания пользовательских режимов наложения (Blend Modes), работающий поверх WebGL. Эта статья покажет, как с помощью этого API и графического примитива создать эффект круглого выреза на изображении, открывая путь к реализации сложных масок и переходов. Освоив этот механизм, вы сможете выйти за рамки встроенных режимов наложения и программировать собственные визуальные взаимодействия между графикой и текстурами, что особенно полезно для создания уникальных 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('turkey', 'assets/pics/turkey-1985086.jpg');
    }

    create ()
    {
        //  WebGL only:
        const gl = this.sys.game.renderer.gl;

        const renderer = this.sys.game.renderer;

        const modeIndex = renderer.addBlendMode([ gl.ZERO, gl.SRC_COLOR ], gl.FUNC_ADD);

        const graphics = this.add.graphics();

        const color = 0xffffff;
        const alpha = 1;

        graphics.fillStyle(color, alpha);

        graphics.fillCircle(400, 300, 256);

        this.add.image(400, 300, 'turkey').setBlendMode(modeIndex);
    }
}

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

const game = new Phaser.Game(config);


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

Как и в любом проекте на Phaser, работа начинается с создания сцены и загрузки ресурсов. В данном примере мы загружаем одно тестовое изображение.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('turkey', 'assets/pics/turkey-1985086.jpg');
}

Ключевой момент: пример использует Phaser.WEBGL в конфигурации, так как пользовательские режимы наложения — это функция рендерера WebGL. Рендерер Canvas их не поддерживает.

Получение доступа к WebGL-контексту

Для создания кастомного режима наложения нам нужен прямой доступ к низкоуровневому WebGL-контексту рендерера Phaser. Это мост между высокоуровневым API фреймворка и нативными возможностями WebGL.

create ()
{
    //  WebGL only:
    const gl = this.sys.game.renderer.gl;
    const renderer = this.sys.game.renderer;
}

Переменная gl хранит ссылку на объект WebGLRenderingContext. renderer — это экземпляр рендерера Phaser (WebGLRenderer), который предоставляет метод addBlendMode для регистрации нового режима.

Создание пользовательского режима наложения

Сердце примера — создание нового математического правила для смешивания пикселей. Метод renderer.addBlendMode() принимает массив из двух элементов (коэффициенты для источника и назначения) и функцию смешивания.

const modeIndex = renderer.addBlendMode([ gl.ZERO, gl.SRC_COLOR ], gl.FUNC_ADD);

Здесь используется формула: (0 * source) + (source_color * destination). gl.ZERO обнуляет цвет исходного пикселя (того, что будет наложено). gl.SRC_COLOR использует цвет исходного пикселя как множитель для цвета пикселя назначения (фона). gl.FUNC_ADD указывает, что результаты перемножения нужно сложить. В итоге, там, где исходный объект (наш круг) имеет цвет, будет виден фон, а где он прозрачен — ничего не изменится. Метод возвращает индекс нового режима, который мы используем позже.

Рисование маски-круга

В качестве «источника» для смешивания мы рисуем белый круг с помощью Graphics API. Его цвет и форма определят, какая часть фонового изображения будет видна.

const graphics = this.add.graphics();
const color = 0xffffff;
const alpha = 1;
graphics.fillStyle(color, alpha);
graphics.fillCircle(400, 300, 256);

Создается объект graphics, ему задается стиль заливки (белый цвет, полная непрозрачность), и рисуется круг в центре сцены. Белый цвет (0xffffff) важен, так как в формуле смешивания gl.SRC_COLOR будет использовать именно его значение (1,1,1 в нормализованном виде), что не исказит цвет фона.

Применение режима к изображению

Финальный шаг — добавление фонового изображения и применение к нему созданного режима наложения.

this.add.image(400, 300, 'turkey').setBlendMode(modeIndex);

Изображение индейки добавляется на те же координаты, что и круг. Вызов .setBlendMode(modeIndex) говорит рендереру: «При отрисовке этого изображения используй наш специальный режим смешивания под номером modeIndex». В результате формула применяется к каждому пикселю: изображение отрисовывается только там, где под ним находится белый круг, создавая иллюзию, что изображение обрезано по форме круга. Порядок добавления объектов (сначала графика, потом изображение) в данном случае не важен, так как смешивание рассчитывается в момент отрисовки кадра.

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

Вы научились создавать и использовать пользовательские режимы наложения WebGL в Phaser для реализации эффекта маскирования изображения произвольной графической формой. Это мощный низкоуровневый инструмент для создания уникальных визуальных эффектов. Для экспериментов попробуйте: 1. Изменить форму маски, используя другие методы graphics (например, fillRect или fillTriangle). 2. Изменить коэффициенты в addBlendMode. Использование [gl.DST_COLOR, gl.ZERO] даст инвертированный эффект. 3. Анимировать свойства графического объекта (позицию, размер, цвет) для создания динамической маски, которая следует за курсором или персонажем. 4. Применить режим не к статическому изображению, а к группе спрайтов или тайлмапу.