О чем этот пример
Оживить игру можно не только анимацией персонажей, но и динамическими визуальными эффектами. Например, частицами, которые следуют за движущимся объектом, создавая шлейф, ауру или след. В Phaser 3 это делается буквально парой строк кода с помощью системы частиц (Particle Emitter) и её свойства `follow`. Эта статья на практическом примере покажет, как заставить эмиттер частиц следовать за физическим спрайтом, будь то летящий кристалл или убегающий персонаж. Вы научитесь создавать эффекты, которые автоматически обновляют свою позицию, освобождая вас от ручного управления в игровом цикле.
Версия 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('megaset', 'assets/atlas/megaset-0.png', 'assets/atlas/megaset-0.json');
}
create ()
{
const particles = this.add.particles('megaset');
const sprite = this.physics.add.image(300, 300, 'megaset', 'gem').setVelocity(300, 200).setBounce(1).setCollideWorldBounds(true);
const sprite2 = this.physics.add.image(200, 200, 'megaset', 'ilkke').setVelocity(-300, -200).setBounce(1).setCollideWorldBounds(true);
particles.createEmitter({
frame: 'yellow_ball',
speed: 100,
gravity: { x: 0, y: 200 },
scale: { start: 0.1, end: 1 },
follow: sprite
});
// You can also follow a sprite like this (rather than setting it in the config)
const emitter = particles.createEmitter({
frame: 'red_ball',
speed: 100,
gravity: { x: 0, y: 200 },
scale: { start: 0.1, end: 1 }
});
emitter.startFollow(sprite2);
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000',
parent: 'phaser-example',
physics: {
default: 'arcade',
arcade: {
gravity: { y: 150 }
}
},
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и загрузка ассетов
Вся логика примера содержится в классе сцены Example. Первым делом в методе preload мы загружаем необходимые ресурсы. Здесь используется один атлас megaset, который содержит и текстуры для спрайтов, и кадры для частиц.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.atlas('megaset', 'assets/atlas/megaset-0.png', 'assets/atlas/megaset-0.json');
}
Загруженный атлас с ключом 'megaset' станет основным источником изображений для всех объектов в этой сцене. Важно, что атлас уже содержит нужные нам кадры: gem, ilkke, yellow_ball и red_ball.
Создание физических спрайтов-целей
В методе create мы создаём два физических спрайта, за которыми будут следовать частицы. Для этого используется менеджер физики Arcade, который мы настроили в конфигурации игры.
const sprite = this.physics.add.image(300, 300, 'megaset', 'gem').setVelocity(300, 200).setBounce(1).setCollideWorldBounds(true);
const sprite2 = this.physics.add.image(200, 200, 'megaset', 'ilkke').setVelocity(-300, -200).setBounce(1).setCollideWorldBounds(true);
Метод this.physics.add.image создаёт спрайт, который сразу же подчиняется законам физики Arcade. Мы задаём им начальную скорость с помощью setVelocity, упругость отскока от границ (setBounce(1)) и включаем столкновение с границами мира. Эти два спрайта будут хаотично отскакивать по экрану.
Создание системы частиц и первого эмиттера
Перед созданием эмиттеров необходимо создать их фабрику — систему частиц (Particle Manager). Она управляет всеми эмиттерами, которые используют общий текстуру или атлас.
const particles = this.add.particles('megaset');
Теперь создадим первый эмиттер, который сразу же привяжется к первому спрайту (sprite). Ключевой параметр здесь — follow.
particles.createEmitter({
frame: 'yellow_ball',
speed: 100,
gravity: { x: 0, y: 200 },
scale: { start: 0.1, end: 1 },
follow: sprite
});
Конфигурация эмиттера:
* frame: определяет, какой кадр из атласа (yellow_ball) будет использовать каждая частица.
* speed: начальная скорость разлёта частиц.
* gravity: сила, которая притягивает частицы по оси Y, создавая эффект падения.
* scale: частицы будут появляться маленькими (0.1) и увеличиваться до полного размера (`1`).
* follow: самый важный параметр. Передавая сюда объект sprite, мы указываем, что точка испускания частиц будет постоянно совпадать с текущей позицией этого спрайта.
Альтернативный способ привязки эмиттера
Phaser позволяет привязать эмиттер к цели и после его создания. Это полезно, если цель становится известна позже или её нужно менять динамически.
const emitter = particles.createEmitter({
frame: 'red_ball',
speed: 100,
gravity: { x: 0, y: 200 },
scale: { start: 0.1, end: 1 }
});
emitter.startFollow(sprite2);
Сначала мы создаём эмиттер (emitter) без свойства follow в конфиге. Затем вызываем у созданного экземпляра метод startFollow(), передавая ему второй спрайт (sprite2). Результат будет идентичен первому способу: эмиттер красных частиц начнёт следовать за вторым спрайтом. Чтобы остановить слежение, у эмиттера есть парный метод stopFollow().
Конфигурация игры и физики
Для корректной работы примера важна глобальная конфигурация игры, особенно настройка физического плагина Arcade.
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000',
parent: 'phaser-example',
physics: {
default: 'arcade',
arcade: {
gravity: { y: 150 }
}
},
scene: Example
};
const game = new Phaser.Game(config);
В блоке physics мы задаём Arcade в качестве системы физики по умолчанию. Обратите внимание: здесь задана глобальная гравитация по оси Y (y: 150), которая влияет на все Arcade Physics тела в игре (наши спрайты sprite и sprite2). Гравитация же в конфиге эмиттера (gravity: { x: 0, y: 200 }) влияет только на частицы и не зависит от глобальных настроек.
Что попробовать дальше
Свойство follow и метод startFollow() — мощные инструменты для создания "живых" эффектов, привязанных к игровым объектам. Они избавляют разработчика от необходимости вручную обновлять позицию эмиттера в каждом кадре.
Для экспериментов попробуйте:
1. Менять цель слежения emitter.startFollow() в реальном времени по клику или таймеру.
2. Привязать эмиттер не к спрайту, а к курсору мыши (this.input.activePointer), создавая интерактивный след.
3. Комбинировать несколько эмиттеров с разными настройками (scale, speed), следующих за одним объектом, чтобы создать сложный составной эффект.
