О чем этот пример
Стандартные эмиттеры частиц в Phaser обычно генерируют эффекты из заданных точек. Но что, если вы хотите, чтобы частицы появлялись только из непрозрачных областей сложного спрайта, например, логотипа? Этот пример демонстрирует продвинутую технику — создание системы частиц, которая использует произвольный источник данных для эмиссии. Такой подход открывает двери для визуальных эффектов, где форма объекта напрямую влияет на поведение частиц, будь то искры, вылетающие из контуров текста, или туман, поднимающийся только с поверхности земли на тайлмапе.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.55.2.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.atlas('flares', 'assets/particles/flares.png', 'assets/particles/flares.json');
this.load.image('logo', 'assets/sprites/phaser2.png');
this.load.image('space', 'assets/skies/space.jpg');
}
create ()
{
const textures = this.textures;
this.add.image(400, 300, 'space');
const logo = this.add.image(400, 300, 'logo');
const origin = logo.getTopLeft();
const logoSource = {
getRandomPoint: function (vec)
{
let x;
let y;
let pixel;
do
{
x = Phaser.Math.Between(0, logo.width - 1);
y = Phaser.Math.Between(0, logo.height - 1);
pixel = textures.getPixel(x, y, 'logo');
} while (pixel.alpha < 255);
return vec.setTo(x + origin.x, y + origin.y);
}
};
const particles = this.add.particles('flares');
particles.createEmitter({
x: 0,
y: 0,
lifespan: 1000,
gravityY: 10,
scale: { start: 0, end: 0.25, ease: 'Quad.easeOut' },
alpha: { start: 1, end: 0, ease: 'Quad.easeIn' },
blendMode: 'ADD',
emitZone: { type: 'random', source: logoSource }
});
}
}
const config = {
type: Phaser.WEBGL,
width: 800,
height: 600,
backgroundColor: '#000',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Загрузка ресурсов и настройка сцены
Как и в любом проекте Phaser, начинаем с метода preload. Здесь загружаются необходимые для примера ресурсы: атлас частиц flares, спрайт логотипа и фон.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.atlas('flares', 'assets/particles/flares.png', 'assets/particles/flares.json');
this.load.image('logo', 'assets/sprites/phaser2.png');
this.load.image('space', 'assets/skies/space.jpg');
}
В методе create мы добавляем фон и логотип в центр экрана. Ключевой объект logo нам понадобится далее для вычислений.
create ()
{
this.add.image(400, 300, 'space');
const logo = this.add.image(400, 300, 'logo');
Создание кастомного источника данных (EmitZone Source)
Сердце примера — объект logoSource. Система частиц Phaser может использовать так называемую emitZone — зону эмиссии. Вместо стандартных геометрических фигур мы можем передать ей объект с методом getRandomPoint. Этот метод будет вызываться эмиттером каждый раз, когда нужно создать новую частицу, чтобы определить её стартовую позицию.
const origin = logo.getTopLeft();
const logoSource = {
getRandomPoint: function (vec)
{
let x;
let y;
let pixel;
do
{
x = Phaser.Math.Between(0, logo.width - 1);
y = Phaser.Math.Between(0, logo.height - 1);
pixel = textures.getPixel(x, y, 'logo');
} while (pixel.alpha < 255);
return vec.setTo(x + origin.x, y + origin.y);
}
};
Логика метода проста, но мощна: в цикле do...while мы случайным образом выбираем пиксель в пределах размеров текстуры логотипа. Функция textures.getPixel возвращает объект с данными пикселя, включая альфа-канал (alpha). Цикл продолжается до тех пор, пока не будет найден полностью непрозрачный пиксель (alpha === 255). Это гарантирует, что частицы будут появляться только из видимой части изображения, а не из прозрачного фона. Найденные координаты `xиyявляются локальными для текстуры логотипа. Чтобы перевести их в глобальные координаты сцены, мы прибавляем к ним координату верхнего левого угла спрайта логотипа (origin.x,origin.y). Результат записывается в переданный векторvec`, который затем возвращается.
Настройка и запуск эмиттера частиц
Следующий шаг — создать систему частиц (ParticleEmitterManager) и настроить в ней эмиттер.
const particles = this.add.particles('flares');
particles.createEmitter({
x: 0,
y: 0,
lifespan: 1000,
gravityY: 10,
scale: { start: 0, end: 0.25, ease: 'Quad.easeOut' },
alpha: { start: 1, end: 0, ease: 'Quad.easeIn' },
blendMode: 'ADD',
emitZone: { type: 'random', source: logoSource }
});
Обратите внимание на параметры x: 0 и y: 0. Они устанавливают базовую позицию эмиттера в ноль, потому что реальные координаты эмиссии теперь полностью контролируются нашей emitZone. Самая важная строка — emitZone: { type: 'random', source: logoSource }. Указывая type: 'random', мы говорим системе использовать переданный source для получения случайных точек. Именно здесь наш объект logoSource с его методом getRandomPoint вступает в игру. Остальные параметры эмиттера задают поведение самих частиц: время жизни (lifespan), гравитацию (gravityY), анимацию масштаба и прозрачности, а также режим наложения 'ADD' для создания светящегося эффекта.
Конфигурация игры и запуск
Финальный шаг — стандартная конфигурация игры Phaser. Мы указываем тип рендерера (WEBGL для лучшей производительности частиц), размеры холста, цвет фона и класс нашей сцены Example.
const config = {
type: Phaser.WEBGL,
width: 800,
height: 600,
backgroundColor: '#000',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
После создания экземпляра Game Phaser автоматически запустит цикл рендеринга, и мы увидим результат: из логотипа Phaser будут постоянно вылетать светящиеся частицы, причем они будут появляться строго в пределах его цветных областей.
Что попробовать дальше
Этот пример раскрывает мощь системы частиц Phaser, выходящей далеко за рамки простых точек и прямоугольников. Используя кастомный объект в качестве emitZone.source, вы можете привязать эмиссию частиц к любой логике: форме спрайта, данным тайловой карты или даже динамически изменяющейся маске. Для экспериментов попробуйте изменить условие в цикле do...while (например, pixel.alpha > 128), чтобы частицы появлялись и из полупрозрачных областей. Или создайте источник, который эмитирует частицы только из пикселей определенного цвета, создавая разноцветные струи из разных частей изображения.
