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

Одной из мощных, но не всегда очевидных возможностей Phaser является работа со слоями (Layers) и масками (Masks). Они позволяют организовать объекты в группы и применять к ним общие эффекты, такие как отсечение видимой области. В этой статье мы разберем практический пример, где слой спрайтов анимируется через твины, но отображается только внутри области, заданной геометрической маской. Этот подход полезен для создания сложных визуальных эффектов, порталов, ограничительных областей или кастомизации 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('elephant', 'assets/sprites/elephant.png');
    }

    create ()
    {
        const elephantLayer = this.add.layer();

        const graphics = this.make.graphics();

        graphics.fillStyle(0xffffff);
        graphics.fillCircle(400, 300, 300);

        const mask = graphics.createGeometryMask();

        elephantLayer.setMask(mask);

        for (let i = 0; i < 32; i++)
        {
            let x = Phaser.Math.Between(600, 800);
            let y = Phaser.Math.Between(0, 600);

            let sprite = elephantLayer.add(this.make.sprite({ x, y, key: 'elephant' }));

            let dx = x - 600;

            this.tweens.add({
                targets: sprite,
                x: dx,
                ease: 'Sine.inOut',
                duration: 4000,
                delay: (i * 250),
                yoyo: true,
                repeat: -1
            });
        }
    }
}

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

const game = new Phaser.Game(config);

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

В методе preload мы загружаем единственный спрайт — изображение слона. Обратите внимание на использование this.load.setBaseURL для указания базового пути к ресурсам. Это удобно, когда все ассеты хранятся в одном удаленном хранилище.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('elephant', 'assets/sprites/elephant.png');
}

Создание слоя и геометрической маски

В create первым делом создается объект слоя с помощью this.add.layer(). Слой — это контейнер, который может содержать другие игровые объекты и управлять ими как единой группой.

Затем мы создаем графический объект (Graphics) через this.make.graphics(). Этот объект используется для рисования примитивов. В нашем случае мы рисуем белый круг радиусом 300 пикселей в центре экрана.

Ключевой шаг — превращение этого графического объекта в геометрическую маску с помощью метода createGeometryMask(). После этого маска применяется ко всему слою через метод setMask(). Теперь любой объект, добавленный в elephantLayer, будет виден только в пределах нарисованного круга.

const elephantLayer = this.add.layer();

const graphics = this.make.graphics();
graphics.fillStyle(0xffffff);
graphics.fillCircle(400, 300, 300);

const mask = graphics.createGeometryMask();
elephantLayer.setMask(mask);

Наполнение слоя спрайтами и анимация

Далее в цикле создается 32 спрайта слона. Каждому случайно назначаются начальные координаты в правой части экрана (X от 600 до 800, Y от 0 до 600). Важно: спрайты создаются через this.make.sprite и сразу добавляются в слой методом elephantLayer.add. Это связывает их с ранее созданной маской.

Для каждого спрайта создается твин (анимация) с помощью this.tweens.add. Твин перемещает спрайт по горизонтали: от его начальной позиции `xдо точкиdx, которая рассчитывается какx - 600. Это обеспечивает движение влево. Параметрыyoyo: trueиrepeat: -1заставляют анимацию колебаться туда-обратно бесконечно. Задержка (delay`) для каждого следующего спрайта увеличивается, создавая волнообразный эффект.

for (let i = 0; i < 32; i++)
{
    let x = Phaser.Math.Between(600, 800);
    let y = Phaser.Math.Between(0, 600);

    let sprite = elephantLayer.add(this.make.sprite({ x, y, key: 'elephant' }));

    let dx = x - 600;

    this.tweens.add({
        targets: sprite,
        x: dx,
        ease: 'Sine.inOut',
        duration: 4000,
        delay: (i * 250),
        yoyo: true,
        repeat: -1
    });
}

Конфигурация и запуск игры

Стандартная конфигурация игры Phaser. Указываем тип рендерера (Phaser.AUTO), размеры холста, цвет фона и родительский HTML-элемент. Важно передать наш класс сцены Example в свойство scene конфига.

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

const game = new Phaser.Game(config);

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

Использование слоев с геометрическими масками в Phaser открывает широкие возможности для визуального дизайна. Вы можете маскировать не только кругами, но и любыми другими фигурами, рисуемыми через Graphics. Попробуйте заменить круг на прямоугольник, звезду или сложный путь. Экспериментируйте с анимацией самой маски (изменяя ее размер или положение) — это создаст динамический эффект "видоискателя". Также можно применять маски к другим контейнерам или отдельным объектам для создания многослойных композиций.