О чем этот пример
Эмиттеры частиц в Phaser — мощный инструмент для создания эффектов. Частицы могут лететь к заданной точке, используя параметры `moveToX` и `moveToY`. Однако иногда требуется, чтобы цель движения менялась динамически, например, следовала за курсором мыши. В этой статье мы разберем пример, который показывает, как реализовать такое поведение с помощью функций `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;
});
const balls = this.add.particles(0, 0, 'ball', {
x: { min: 300, max: 500 },
y: 0,
advance: 2000,
moveToX: {
onEmit: () => {
return x;
},
onUpdate: () => {
return x;
}
},
moveToY: {
onEmit: () => {
return y;
},
onUpdate: () => {
return y;
}
},
lifespan: 2000,
sortProperty: 'lifeT',
sortOrderAsc: true
});
// Workaround:
// balls.moveTo = true;
const text = this.add.text();
this.events.on('update', () => {
text.text = `moveTo: ${balls.moveToX}, ${balls.moveToY}`
})
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
pixelArt: true,
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и обработка ввода
В методе preload загружаются две текстуры: фон (bg) и изображение частицы (ball). Основная логика начинается в create. Сначала добавляется фон.
Затем объявляются две переменные, `xиy`, которые будут хранить текущие координаты цели. Изначально они установлены в точку (400, 570) — низ экрана.
Далее на событие движения указателя (pointermove) вешается обработчик. При каждом движении мыши или касании координаты `xиyобновляются в соответствии с позицией курсора в игровом мире (pointer.worldX,pointer.worldY`). Это создает основу для динамической цели.
let x = 400;
let y = 570;
this.input.on('pointermove', pointer => {
x = pointer.worldX;
y = pointer.worldY;
});
Создание эмиттера с динамической целью
Эмиттер частиц создается с помощью this.add.particles. Ключевая настройка — параметры moveToX и moveToY. Обычно им присваивают статичное числовое значение, к которому будут стремиться все частицы. Но в нашем примере вместо числа используются объекты с двумя функциями: onEmit и onUpdate.
* onEmit вызывается в момент создания (эмита) каждой новой частицы. Она возвращает текущее значение цели (`xилиy`), которое будет зафиксировано для этой конкретной частицы на весь срок ее жизни.
* onUpdate вызывается при каждом обновлении существующей частицы. Она также возвращает текущее значение цели. Это позволяет уже летящим частицам скорректировать свой курс, если цель переместилась.
Таким образом, все частицы постоянно стремятся к актуальным координатам, хранящимся в переменных `xиy`.
const balls = this.add.particles(0, 0, 'ball', {
x: { min: 300, max: 500 },
y: 0,
advance: 2000,
moveToX: {
onEmit: () => { return x; },
onUpdate: () => { return x; }
},
moveToY: {
onEmit: () => { return y; },
onUpdate: () => { return y; }
},
lifespan: 2000,
sortProperty: 'lifeT',
sortOrderAsc: true
});
Остальные параметры:
* `xиy` задают область появления частиц.
* advance предварительно вычисляет частицы.
* lifespan — время жизни частицы в миллисекундах.
* sortProperty и sortOrderAsc управляют порядком отрисовки для правильного наложения.
Визуализация цели и важный нюанс
В примере также есть визуализация текущих координат цели. Создается текстовый объект, и на событие update сцены вешается обработчик, который обновляет текст, отображая текущие значения moveToX и moveToY эмиттера.
const text = this.add.text();
this.events.on('update', () => {
text.text = `moveTo: ${balls.moveToX}, ${balls.moveToY}`
})
**Критически важный момент:** для того чтобы механика moveTo с функциями onEmit/onUpdate заработала, необходимо вручную установить свойство moveTo эмиттера в true. В исходном коде примера эта строка закомментирована как // Workaround:. Без этого частицы не будут двигаться к цели, несмотря на корректные функции.
// Раскомментируйте эту строку для работы!
balls.moveTo = true;
Это неочевидное требование API, о котором важно помнить при реализации подобных эффектов.
Что попробовать дальше
Использование функций onEmit и onUpdate в параметрах moveToX/moveToY открывает возможность создания "живых", интерактивных систем частиц в Phaser. Частицы могут динамически менять свою цель прямо в полете. Для экспериментов попробуйте: изменить логику внутри onUpdate, чтобы цель двигалась по сложной траектории (синусоиде, окружности); привязать цель не к курсору, а к спрайту другого игрока; или создать несколько эмиттеров с разными целями для масштабного визуального эффекта. Не забудьте про ключевой параметр balls.moveTo = true!
