О чем этот пример
Часто в играх нужно управлять видимостью целой группы объектов не по отдельности, а всем скопом. Например, чтобы герои появлялись из тумана, магический эффект постепенно раскрывал врагов или, как в нашем примере, стадо слонов выходило из кляксы. Обычные маски для спрайтов работают точечно, но что если объектов десятки? В Phaser 3 для этого есть мощный инструмент — фильтры и внешние маски на слоях (Layers). Эта статья покажет, как с помощью всего нескольких строк кода привязать одну битмап-маску ко всем объектам слоя, создав стильный и динамичный визуальный эффект.
Версия 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');
this.load.image('splat', 'assets/pics/splat1.png');
}
create ()
{
const elephantLayer = this.add.layer();
const splat = this.make.image({ x: 400, y: 300, key: 'splat' }, false);
elephantLayer.enableFilters().filters.external.addMask(splat);
for (let i = 0; i < 64; 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 * 50),
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 мы загружаем два изображения: спрайт слона и текстуру кляксы, которая будет нашей маской. Ключевое действие происходит в create.
Первым делом создаётся слой с помощью this.add.layer(). Слой (Layer) в Phaser — это контейнер для игровых объектов, который позволяет управлять ими как единой группой: применять трансформации, фильтры и, что самое важное для нас, общие маски.
const elephantLayer = this.add.layer();
Создание маски из изображения
Чтобы использовать изображение как маску, его нужно сначала создать как игровой объект. Мы используем фабрику this.make.image. Важный нюанс: второй параметр false указывает, что объект не должен быть автоматически добавлен на дисплейный список (не отрендерится сам по себе). Он нужен только как источник данных для маски.
const splat = this.make.image({ x: 400, y: 300, key: 'splat' }, false);
Теперь у нас есть объект splat, который содержит текстуру нашей кляксы. Его координаты (400, 300) задают центр маски на сцене.
Включение фильтров и применение маски к слою
Самый важный шаг — связать маску со слоем. Сначала необходимо активировать систему фильтров для слоя, вызвав метод enableFilters(). После этого у слоя становится доступно свойство filters, а в нём — массив external для внешних эффектов.
Метод addMask этого массива принимает наш объект splat и применяет его текстуру в качестве маски ко всему слою. Теперь любой объект, добавленный в elephantLayer, будет виден только в тех пикселях, где маска (splat) непрозрачна.
elephantLayer.enableFilters().filters.external.addMask(splat);
Наполнение слоя и анимация
Далее мы создаём 64 спрайта слона, используя фабрику this.make.sprite, и каждый сразу добавляем в наш слой с помощью elephantLayer.add(). Исходные позиции слонов генерируются случайно в правой части экрана.
let sprite = elephantLayer.add(this.make.sprite({ x, y, key: 'elephant' }));
Для каждого спрайта создаётся твин (анимация перемещения) с помощью this.tweens.add. Слоны двигаются по горизонтали от своей начальной позиции (`x) до точкиdx(рассчитанной относительно левого края), а затем обратно. Параметрыyoyo: trueиrepeat: -1заставляют анимацию бесконечно повторяться вперёд и назад. Задержка (delay`) для каждого следующего слона создаёт волнообразный эффект движения.
this.tweens.add({
targets: sprite,
x: dx,
ease: 'Sine.inOut',
duration: 4000,
delay: (i * 50),
yoyo: true,
repeat: -1
});
Итог: всё стадо слонов движется туда-сюда, но видно мы их только внутри контура кляксы-маски, создавая иллюзию, что они появляются из неё.
Что попробовать дальше
Использование внешних масок на слоях — это мощный и производительный способ применять сложные эффекты видимости к большим группам объектов. Вместо того чтобы накладывать маску на каждый спрайт по отдельности (что ресурсозатратно), мы делаем это один раз для всего контейнера. Вы можете экспериментировать: анимировать саму маску (менять её `x,y,scale`), использовать в качестве маски не статичную картинку, а спрайт-лист (с анимацией), или комбинировать несколько слоёв с разными масками для создания сложных композиций. Этот приём отлично подходит для создания переходов между уровнями, раскрытия карты или магических заклинаний, влияющих на видимость множества целей.
