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

Режимы наложения (Blend Modes) — мощный инструмент для создания визуальных эффектов в 2D-графике. Они позволяют контролировать, как пиксели одного графического объекта (спрайта, изображения) взаимодействуют с пикселями под ним. В этой статье мы разберем пример использования режима `DIFFERENCE` в Phaser.js и научимся управлять визуальным слоем игры, создавая динамичные и стильные сцены. Это полезно для выделения UI-элементов, создания неоновых эффектов, частиц с «вычитающим» свечением или просто для добавления глубины игровому миру.

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


// Difference is Canvas Blend Modes
class Example extends Phaser.Scene
{
    constructor ()
    {
        super();
        this.sprites = [];
    }

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('bg', 'assets/skies/space1.png');
        this.load.image('particle', 'assets/particles/yellow.png');
        this.load.image('logo', 'assets/sprites/phaser2.png');
    }

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

        //  Create the particles
        for (var i = 0; i < 300; i++)
        {
            const x = Phaser.Math.Between(-64, 800);
            const y = Phaser.Math.Between(-64, 600);

            const image = this.add.image(x, y, 'particle');

            //  Canvas and WebGL:

            // NORMAL
            // ADD
            // MULTIPLY
            // SCREEN

            //  Canvas only:

            // OVERLAY
            // DARKEN
            // LIGHTEN
            // COLOR_DODGE
            // COLOR_BURN
            // HARD_LIGHT
            // SOFT_LIGHT
            // DIFFERENCE
            // EXCLUSION
            // HUE
            // SATURATION
            // COLOR
            // LUMINOSITY

            // image.setBlendMode(Phaser.BlendModes.OVERLAY);
            image.setBlendMode(Phaser.BlendModes.ADD);

            this.sprites.push({ s: image, r: 2 + Math.random() * 6 });
        }

        this.add.image(400, 300, 'logo').setBlendMode(Phaser.BlendModes.DIFFERENCE);
    }

    update ()
    {
        for (var i = 0; i < this.sprites.length; i++)
        {
            const sprite = this.sprites[i].s;
            sprite.y -= this.sprites[i].r;
            if (sprite.y < -256)
            {
                sprite.y = 700;
            }
        }
    }

}

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

const game = new Phaser.Game(config);

Что такое Blend Modes и зачем они нужны?

Режимы наложения определяют математическую формулу, по которой цвет рисуемого пикселя (источник) комбинируется с цветом пикселя, уже находящегося в буфере кадра (цель). В отличие от простой прозрачности (alpha), они могут создавать более сложные и интересные визуальные взаимодействия: сложение цветов (ADD), умножение (MULTIPLY), наложение экрана (SCREEN) и многие другие.

Phaser поддерживает набор режимов, общих для Canvas и WebGL рендерера, а также дополнительные, доступные только для Canvas. Режим DIFFERENCE, который мы рассмотрим, входит в последнюю группу. Он вычитает значение цвета источника из цвета цели или наоборот (берется абсолютное значение), что часто приводит к инверсному, «негативному» эффекту, особенно на контрастных областях.

Разбор примера: структура сцены и загрузка

Код начинается с объявления класса сцены, который наследуется от Phaser.Scene. В конструкторе инициализируется массив this.sprites для хранения данных о наших частицах.

В методе preload загружаются три изображения: фон (bg), текстура частицы (particle) и логотип (logo). Обратите внимание на использование setBaseURL — это удобно, чтобы не писать полный URL для каждого ресурса.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('bg', 'assets/skies/space1.png');
    this.load.image('particle', 'assets/particles/yellow.png');
    this.load.image('logo', 'assets/sprites/phaser2.png');
}

Создание частиц с режимом наложения ADD

В методе create сначала отрисовывается фоновое изображение. Затем в цикле создается 300 частиц — обычных изображений (this.add.image), размещенных в случайных позициях за пределами экрана.

Ключевой момент — установка режима наложения для каждой частицы с помощью метода setBlendMode. В примере для частиц используется Phaser.BlendModes.ADD. Этот режим складывает значения RGB источника и цели, делая цвета ярче и создавая эффект светящихся, накладывающихся друг на друга точек. Это классический прием для симуляции свечения, огня или звезд.

Каждая частица и её скорость (хранящаяся в свойстве `r) помещаются в массивthis.sprites` для дальнейшей анимации.

const image = this.add.image(x, y, 'particle');
image.setBlendMode(Phaser.BlendModes.ADD);
this.sprites.push({ s: image, r: 2 + Math.random() * 6 });

Эффект DIFFERENCE для основного объекта

После создания частиц на сцену добавляется логотип Phaser. Для него устанавливается режим наложения Phaser.BlendModes.DIFFERENCE.

Режим DIFFERENCE работает по принципу |фон - лого|. На темном фоне космоса белое лого будет отображаться почти без изменений (разница между черным и белым велика). Однако когда под логотипом пролетают желтые частицы в режиме ADD, их цвет вычитается из цвета логотипа (или наоборот). Поскольку DIFFERENCE берет абсолютное значение разности, это приводит к резкому инвертированию цвета в зоне пересечения: желтая частица под белым лого даст синеватый оттенок. Это создает динамичный, «реактивный» визуальный эффект, где логотип взаимодействует с фоном и движущимися элементами.

this.add.image(400, 300, 'logo').setBlendMode(Phaser.BlendModes.DIFFERENCE);

Анимация: заставляем частицы двигаться

Логика анимации реализована в методе update, который вызывается на каждом кадре. Для каждой частицы из массива this.sprites происходит изменение её координаты `yна величину, равную индивидуальной скорости (r). Когда частица уходит за верхнюю границу экрана (sprite.y < -256`), она телепортируется вниз, создавая бесконечный поток.

update ()
{
    for (var i = 0; i < this.sprites.length; i++)
    {
        const sprite = this.sprites[i].s;
        sprite.y -= this.sprites[i].r;
        if (sprite.y < -256)
        {
            sprite.y = 700;
        }
    }
}

Именно это постоянное движение частиц под логотипом в режиме DIFFERENCE и порождает непрерывно меняющийся цветовой эффект на нем.

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

Режимы наложения — это простой, но чрезвычайно эффективный способ добавить вашей игре визуальную изюминку без использования тяжелых шейдеров. Экспериментируйте: попробуйте заменить ADD на SCREEN для более мягкого свечения частиц или примените DIFFERENCE к самим частицам, а лого оставьте в NORMAL. Используйте OVERLAY или HARD_LIGHT для создания текстурных эффектов на UI-элементах. Помните, что для Canvas-специфичных режимов (включая DIFFERENCE) в конфигурации игры должен быть указан type: Phaser.CANVAS.