О чем этот пример
Движущиеся частицы — ключевой элемент визуальной динамики в играх. Phaser предоставляет мощную систему Particle Emitter, но её возможности часто остаются нераскрытыми. В этой статье разберем, как заставить частицы проигрывать анимацию кадров текстуры по циклу, создавая сложные и живые эффекты, такие как мерцающие искры или вращающиеся кристаллы, без написания кастомной логики для каждой частицы. Мы сосредоточимся на параметре `frame` (и его внутреннем аналоге `_frame`) эмиттера, который управляет выбором кадра из спрайтшита. Понимание его работы с флагами `cycle` и `quantity` позволит вам создавать не просто статичные точки, а миниатюрные анимированные спрайты, составляющие сложную визуальную картину.
Версия 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.spritesheet('diamonds', 'assets/sprites/diamonds32x24x5.png', { frameWidth: 32, frameHeight: 24 });
}
create ()
{
const shape1 = new Phaser.Geom.Circle(0, 0, 200);
const particles = this.add.particles('diamonds');
particles.createEmitter({
frame: { frames: [ 0, 1, 2, 3, 4 ], cycle: true, quantity: 32 },
// _frame: { frames: [ 0, 1, 2, 3, 4 ], cycle: true },
// _frame: { frames: [ 0, 1 ], cycle: true },
_frame: { frames: [ 0, 1 ], cycle: true },
x: 400,
y: 300,
frequency: 32,
lifespan: 400,
alpha: { start: 1, end: 0 },
zone: { type: 'edge', source: shape1, quantity: 32 }
});
}
}
const config = {
type: Phaser.WEBGL,
width: 800,
height: 600,
backgroundColor: '#000',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Подготовка спрайтшита
Всё начинается с загрузки ресурса. В данном примере используется спрайтшит diamonds, который содержит 5 кадров (5 алмазов разного цвета), упакованных в одну текстуру.
Ключевой момент — корректное указание размеров одного кадра при загрузке. Это позволит системе партиклов в дальнейшем правильно нарезать текстуру.
this.load.spritesheet('diamonds', 'assets/sprites/diamonds32x24x5.png', { frameWidth: 32, frameHeight: 24 });
Метод load.spritesheet принимает ключ ресурса, путь к файлу и объект конфигурации с шириной и высотой кадра. После этой операции в кеше загрузчика появляется текстура, разделённая на пронумерованные кадры от 0 до 4.
Создание эмиттера и настройка зоны
Эмиттер создаётся из фабрики частиц, которой передаётся ключ загруженной текстуры. Для определения места появления частиц используется понятие "зона". В примере зона — это край геометрической фигуры (Circle).
const shape1 = new Phaser.Geom.Circle(0, 0, 200);
const particles = this.add.particles('diamonds');
particles.createEmitter({
// ... остальные параметры ...
zone: { type: 'edge', source: shape1, quantity: 32 }
});
Параметр zone.type со значением 'edge' указывает, что частицы должны появляться по границе фигуры (source). quantity в контексте зоны определяет, сколько частиц будет создано за один выброс. Фигура Circle создаётся с центром в (0,0), но её позиция в мире задаётся отдельно через `xиy` эмиттера.
Волшебство параметра `frame`
Это сердце механизма анимации частиц. Параметр frame управляет тем, какой кадр из спрайтшита будет использован для частицы.
Базовый объект конфигурации позволяет задать массив кадров и режим их перебора. Флаг cycle включает циклическое проигрывание последовательности кадров для каждой отдельной частицы на протяжении её жизни (lifespan).
frame: { frames: [ 0, 1, 2, 3, 4 ], cycle: true, quantity: 32 }
Здесь frames — массив индексов кадров для анимации. cycle: true включает циклическое проигрывание этого массива. quantity в этом контексте — это количество кадров в анимации (можно не указывать, система возьмёт длину массива frames). Частица будет плавно переключаться от кадра 0 к кадру 4 и затем снова к 0, пока не истечёт её время жизни.
Скрытая сила `_frame` (экспериментальный параметр)
В исходном коде примера закомментированы несколько вариантов параметра _frame. Это экспериментальная, более низкоуровневая версия настройки кадров, которая может вести себя иначе, чем публичный frame. В оставленном варианте используется только два кадра.
_frame: { frames: [ 0, 1 ], cycle: true }
Важно: если указаны оба параметра (frame и _frame), система будет использовать значение _frame, так как оно имеет более высокий приоритет (это видно в итоговом примере — частицы мигают только двумя цветами). Используйте _frame для тонкой настройки, но помните, что его API может измениться в будущих версиях Phaser.
Сборка полной конфигурации эмиттера
Давайте соберём все параметры вместе, чтобы увидеть полную картину работы эмиттера. Основные параметры жизненного цикла частицы задаются здесь.
particles.createEmitter({
_frame: { frames: [ 0, 1 ], cycle: true }, // Используется этот параметр
x: 400, // Мировая позиция эмиттера (и его зоны)
y: 300,
frequency: 32, // Период между выбросами (в мс). 32мс ~ 30 FPS
lifespan: 400, // Время жизни одной частицы (в мс)
alpha: { start: 1, end: 0 }, // Частица плавно исчезает
zone: { type: 'edge', source: shape1, quantity: 32 }
});
Эмиттер расположен в центре экрана (400, 300). Каждые 32 миллисекунды по краю круга появляется 32 новые частицы. Каждая частица живёт 400 миллисекунд, за это время она проигрывает циклическую анимацию из кадров 0 и 1 и постепенно становится прозрачной. Параметр frequency создаёт непрерывный поток.
Что попробовать дальше
Использование циклической анимации кадров в системе частиц Phaser открывает путь к созданию сложных эффектов из простых спрайтшитов: от мерцающего магического тумана до падающих анимированных листьев. Экспериментируйте: попробуйте увеличить lifespan и добавить больше кадров в массив frames для более плавной анимации. Измените zone.type на 'random', чтобы частицы появлялись не по краю, а случайным образом внутри фигуры. Комбинируя это с другими параметрами, такими как scale или tint, вы сможете генерировать уникальные визуальные стили для своей игры с минимальными затратами на производительность.
