О чем этот пример
Одной из самых мощных возможностей партиклов в Phaser является система `moveTo`. Она позволяет частицам не просто лететь по прямой, а перемещаться к динамически изменяющейся цели. Это открывает двери для создания сложных визуальных эффектов, таких как магические потоки, летящие за персонажем, или энергетические лучи, преследующие цель. В этой статье мы разберем конкретный пример, где источник частиц испускает поток, который плавно следует за координатами курсора мыши. Вы узнаете, как использовать функции обратного вызова `onEmit` и `onUpdate` для управления поведением частиц в реальном времени.
Версия 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/gradient26.png');
this.load.image('ball', 'assets/demoscene/green_ball.png');
}
create ()
{
this.add.image(400, 300, 'bg');
let x = 400;
let y = 570;
this.input.on('pointermove', pointer => {
x = pointer.worldX;
y = pointer.worldY;
});
this.add.particles(0, 0, 'ball', {
x: { min: 300, max: 500 },
y: -32,
advance: 2000,
moveToX: {
onEmit: () => {
return x;
},
onUpdate: () => {
return x;
}
},
moveToY: {
onEmit: () => {
return y;
},
onUpdate: () => {
return y;
}
},
lifespan: 2000,
sortProperty: 'lifeT',
sortOrderAsc: true
});
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Идея: Частицы с интеллектом
Обычно, задав для эмиттера координаты `xиy, мы получаем статический источник частиц. Но что, если нужно, чтобы каждая новая частица летела к точке, которая постоянно двигается? Например, к позиции игрока или курсора. Для этого в Phaser есть свойстваmoveToXиmoveToY`.
Эти свойства могут принимать не просто число, а специальный объект конфигурации. Внутри него мы определяем две ключевые функции:
* onEmit — вызывается в момент создания (эмита) каждой новой частицы и определяет её изначальную цель.
* onUpdate — вызывается на каждом кадре обновления уже существующей частицы и позволяет менять её цель в процессе жизни.
Именно благодаря onUpdate частицы могут плавно перенацеливаться, создавая эффект "живого", следящего потока.
Настройка сцены и отслеживание мыши
В методе create() сцены, после добавления фона, мы создаем две переменные для хранения целевых координат и привязываем их к движению мыши.
let x = 400;
let y = 570;
this.input.on('pointermove', pointer => {
x = pointer.worldX;
y = pointer.worldY;
});
Здесь `xиyинициализируются начальными значениями. Слушатель события'pointermove'постоянно обновляет эти переменные, присваивая им мировые координаты (pointer.worldX,pointer.worldY) курсора. Таким образом,xиy` всегда актуальны.
Создание и конфигурация эмиттера
Создадим сам эмиттер частиц с помощью this.add.particles. Ключевая магия происходит в конфигурационном объекте.
this.add.particles(0, 0, 'ball', {
x: { min: 300, max: 500 },
y: -32,
advance: 2000,
moveToX: {
onEmit: () => { return x; },
onUpdate: () => { return x; }
},
moveToY: {
onEmit: () => { return y; },
onUpdate: () => { return y; }
},
lifespan: 2000,
sortProperty: 'lifeT',
sortOrderAsc: true
});
Разберем важные параметры:
1. **Источник (`x,y`)**: Частицы появляются в случайной точке по горизонтали (от 300 до 500) и чуть выше верхней границы экрана (y = -32).
2. **Динамическая цель (moveToX, moveToY)**: Обе функции возвращают текущие значения переменных `xиy.onEmitзадает точку, к которой частица полетит в момент рождения.onUpdate` перенаправляет её к новой цели на каждом кадре. Поскольку цели для всех частиц одинаковы, весь поток стремится к курсору.
3. **advance: 2000**: Этот параметр указывает системе частиц заранее просчитать и сгладить траекторию движения на 2000 миллисекунд. Без него резкое изменение цели в onUpdate могло бы выглядеть как резкий скачок.
4. **Сортировка (sortProperty, sortOrderAsc)**: Частицы сортируются по оставшемуся времени жизни (lifeT) по возрастанию. Это нужно для корректного порядка отрисовки частиц, движущихся по сложной траектории.
Как это работает в движении
Представьте цепочку частиц, вылетающих из верхней части экрана. В момент создания (onEmit) первая частица получает координаты курсора (например, [100, 200]) и начинает движение к ним. Через мгновение курсор перемещается. Функция onUpdate для этой же частицы вызывается в следующем кадре и возвращает новые координаты ([150, 250]). Система частиц, благодаря параметру advance, плавно корректирует траекторию, направляя частицу к новой точке.
Новые частицы, рождающиеся уже после перемещения мыши, сразу получают актуальные координаты из своих вызовов onEmit. В результате весь шлейф частиц гибко и плавно изгибается, повторяя путь курсора, создавая впечатление управляемого энергетического потока или струи.
Что попробовать дальше
Использование moveTo с динамическими функциями onEmit и onUpdate превращает обычный эмиттер частиц в интерактивный инструмент. Вы можете привязать целевые координаты не только к курсору, но и к спрайту игрока (sprite.x), к случайной точке в области (Phaser.Math.Between), или даже к результату физического расчета.
**Идеи для экспериментов:**
1. Замените pointermove на клик, чтобы частицы летели к последней нажатой точке.
2. Сделайте несколько целей, разделив частицы на группы с разными moveTo функциями.
3. Добавьте в функции onUpdate небольшие случайные отклонения (+ Math.sin(time) * 10), чтобы поток "дрожал" или вихрился.
