О чем этот пример
Создание визуальных эффектов — ключевая часть разработки игр. Phaser предлагает мощную систему Particle Emitter, которая оживляет сцену, но управлять запуском и остановкой частиц вручную не всегда удобно. В этой статье мы разберем, как использовать встроенные события эмиттера (`start`, `stop`, `complete`) для создания отзывчивых и контролируемых эффектов, которые срабатывают автоматически при взаимодействии игрока с объектами. Это особенно полезно для создания подсветки предметов, магических аур или следов при наведении курсора.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('bg', 'assets/skies/darkstone.png');
this.load.image('flare', 'assets/particles/white-flare.png');
this.load.image('fox', 'assets/pics/card3.png');
}
create ()
{
this.add.image(400, 300, 'bg');
const card = this.add.image(400, 300, 'fox').setInteractive();
const emitter = this.add.particles(0, 0, 'flare', {
speed: 24,
lifespan: 1500,
quantity: 10,
scale: { start: 0.4, end: 0 },
emitting: false,
emitZone: { type: 'edge', source: card.getBounds(), quantity: 42 },
duration: 500
});
this.add.text(10, 10, 'Mouse over the card');
const list = this.add.text(10, 60);
card.on('pointerover', () => {
list.text = '';
emitter.start(2000);
});
emitter.on('start', () => {
list.text = list.text.concat('START\n');
});
emitter.on('stop', () => {
list.text = list.text.concat('STOP\n');
});
emitter.on('complete', () => {
list.text = list.text.concat('COMPLETE\n');
});
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и объектов
В методе preload загружаются необходимые ресурсы: фон, текстура частицы (белая вспышка flare) и изображение карточки (fox).
В create мы размещаем фон и карточку в центре экрана. Ключевой момент — карточка делается интерактивной с помощью метода setInteractive(). Это позволяет ей реагировать на события мыши.
const card = this.add.image(400, 300, 'fox').setInteractive();
Сразу создается текстовый элемент для вывода инструкций и второй (list) — в который будут записываться события эмиттера.
Настройка и конфигурация эмиттера частиц
Эмиттер создается с помощью this.add.particles(). Изначально он не активен (emitting: false). Частицы будут появляться из зоны испускания (emitZone), которая привязана к границам карточки (card.getBounds()). Это значит, что частицы будут рождаться по краям (type: 'edge') изображения, а не из одной точки.
const emitter = this.add.particles(0, 0, 'flare', {
speed: 24,
lifespan: 1500,
quantity: 10,
scale: { start: 0.4, end: 0 },
emitting: false,
emitZone: { type: 'edge', source: card.getBounds(), quantity: 42 },
duration: 500
});
Важные параметры:
- duration: 500 — эмиттер будет испускать частицы 500 мс после старта, а затем автоматически остановится.
- lifespan: 1500 — каждая отдельная частица живет 1500 мс, постепенно исчезая ( scale: { start: 0.4, end: 0 } ).
Запуск эмиттера по событию мыши
Основная логика реакции на игрока строится на событии pointerover интерактивного объекта card. Когда курсор наводится на карточку, мы очищаем лог событий (list.text = '') и запускаем эмиттер.
card.on('pointerover', () => {
list.text = '';
emitter.start(2000);
});
Метод emitter.start() принимает необязательный параметр — задержку до повтора эффекта в миллисекундах. В нашем случае 2000 означает, что если курсор продолжает находиться над карточкой, эффект запустится повторно через 2 секунды после завершения предыдущего цикла.
Отслеживание событий жизненного цикла эмиттера
Мощь подхода — в подписке на события самого эмиттера. Это позволяет точно знать, в каком состоянии он находится, и синхронизировать с ним другую логику игры.
emitter.on('start', () => {
list.text = list.text.concat('START\n');
});
emitter.on('stop', () => {
list.text = list.text.concat('STOP\n');
});
emitter.on('complete', () => {
list.text = list.text.concat('COMPLETE\n');
});
- Событие start срабатывает в момент вызова emitter.start().
- Событие stop генерируется, когда эмиттер прекращает испускать новые частицы (после истечения duration).
- Событие complete вызывается, когда все созданные частицы завершили свой жизненный цикл (их lifespan истек).
В нашем примере эти события визуализируются через вывод текста, но в реальной игре их можно использовать для запуска звуков, анимаций или изменения состояния игрового объекта.
Что попробовать дальше
Использование событий эмиттера (start, stop, complete) превращает его из простого генератора частиц в полноценный управляемый объект с предсказуемым жизненным циклом. Для экспериментов попробуйте изменить параметры duration и lifespan, чтобы увидеть, как меняется последовательность событий. Свяжите событие complete не с текстом, а, например, с исчезновением объекта, над которым был курсор, создавая эффект "растворения". Или используйте разные текстуры частиц в зависимости от типа объекта, над которым произошло наведение.
