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

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

Версия 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('pic', 'assets/pics/cougar-dragonsun.png');
    }

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

        const overlay = this.add.graphics();

        overlay.fillStyle(0x000000, 0.8).fillRect(0, 0, 800, 600);

        const maskGraphics = this.make.graphics();

        maskGraphics.fillStyle(0xffffff);
        maskGraphics.fillRect(100, 100, 256, 256);

        const mask = overlay.enableFilters().filters.external.addMask(maskGraphics);

        mask.invert = true;
    }
}

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

const game = new Phaser.Game(config);

Как работает Bitmap Mask

Bitmap Mask (битовая маска) в Phaser — это фильтр, который использует графику (Phaser.GameObjects.Graphics) для определения видимых и невидимых областей другого графического объекта. Принцип прост: пиксели маски, залитые цветом (например, белым 0xffffff), определяют, какие части целевого объекта будут видны. Если используется инверсия маски (mask.invert = true), то всё наоборот — видимым становится всё, кроме залитой области.

В данном примере мы создадим тёмную полупрозрачную накладку (overlay) на весь экран, а затем «прорежем» в ней прямоугольное окно с помощью маски.

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

Всё начинается со стандартной структуры сцены Phaser. В методе preload мы загружаем фоновое изображение, которое будет служить основным контентом нашей сцены. Базовая настройка пути (setBaseURL) позволяет удобно использовать ресурсы из репозитория примеров.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('pic', 'assets/pics/cougar-dragonsun.png');
}

После загрузки, в create, мы сначала добавляем это изображение в центр экрана как фон.

Создание затемняющего overlay

Поверх фона нам нужно разместить полупрозрачный чёрный слой, который и будет затемнять экран. Для этого создаётся объект Graphics с помощью this.add.graphics(). Стиль заливки устанавливается чёрным цветом (0x000000) с альфа-каналом 0.8, что даёт 80% непрозрачности. Затем заливается прямоугольник на весь размер экрана (800x600).

const overlay = this.add.graphics();
overlay.fillStyle(0x000000, 0.8).fillRect(0, 0, 800, 600);

Важно: этот объект является тем, к чему мы позже применим маску.

Создание графики для маски

Маска сама по себе — это тоже объект Graphics, но созданный через фабрику this.make.graphics(). Этот объект не добавляется на сцену как видимый элемент, а существует только как источник данных для маскирования. Мы заливаем на нём белым цветом прямоугольник размером 256x256, начиная с координат (100, 100). Эта область и будет определять «окно» видимости.

const maskGraphics = this.make.graphics();
maskGraphics.fillStyle(0xffffff);
maskGraphics.fillRect(100, 100, 256, 256);

Применение маски как внешнего фильтра

Ключевой шаг — привязать созданную графику маски к нашему затемняющему слою. Для этого сначала необходимо включить систему фильтров для объекта overlay с помощью метода .enableFilters(). Фильтры в Phaser позволяют применять различные шейдерные эффекты, включая маскирование.

Далее, мы добавляем External Mask (внешнюю маску) в коллекцию фильтров объекта, передавая в метод addMask наш объект maskGraphics. После создания маски мы инвертируем её, установив свойство invert в true. Это означает, что видимой останется область ВНЕ нашего белого прямоугольника, создавая эффект затемнения всего, кроме «окна».

const mask = overlay.enableFilters().filters.external.addMask(maskGraphics);
mask.invert = true;

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

В результате мы получаем сцену с фоновым изображением, частично скрытым под полупрозрачной чёрной накладкой, в которой «вырезано» прямоугольное окно. Механика Bitmap Mask мощна и гибка: вы можете использовать не прямоугольники, а любую другую графику — круги, сложные фигуры или даже загруженные текстуры для создания маски. Для экспериментов попробуйте анимировать позицию или размер объекта maskGraphics, чтобы создать эффект движущегося фонарика, или замените прямоугольник на круг для прицела. Используйте разные формы маски для создания динамических переходов между локациями.