О чем этот пример
Частицы — это мощный инструмент для создания атмосферы и визуальных эффектов в играх. Они оживляют сцену, будь то дым, огонь, магические следы или погодные явления. В Phaser 3 система частиц гибкая и производительная, но её настройка требует понимания основных параметров. На примере кода из баг-трекера разберём, как создать массивную сетку из эмиттеров частиц для формирования сложного визуального паттерна, и как тонко управлять их поведением — от движения и масштабирования до цвета и прозрачности.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class ParticlesSquare extends Phaser.Scene
{
preload()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image("smokeTest", "assets/particles/smoke0.png");
}
create()
{
const mainCam = this.cameras.main;
const width = 1600;
const height = 1200;
mainCam.setBackgroundColor("#FFFFFF");
mainCam.setZoom(0.50);
mainCam.centerOn(width/2,height/2)
for (let x = 0; x < width; x +=100)
{
for (let y = 0; y < height; y += 200)
{
this.add.particles(0, 0, "smokeTest", {
x,
y,
lifespan: { min: 3000, max: 5000 },
speed: { min: 5, max: 25 },
angle: { min: 270 - 10, max: 270 + 10 },
timeScale: 0.35,
gravityX: -2,
gravityY: -5,
scale: {
start: 0.33,
end: 1.2,
ease: Phaser.Math.Easing.Cubic.In
},
rotate: { min: 0, max: 360 },
alpha: { start: 0.6, end: 0 },
quantity: 1,
tint: [0x000000, 0x141414, 0x292929, 0x2e2e2e],
frequency: 25,
// blendMode: Phaser.BlendModes.NORMAL,
// particleBringToTop: true,
});
}
}
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000000',
parent: 'phaser-example',
scene: ParticlesSquare
};
const game = new Phaser.Game(config);
Подготовка сцены и камеры
Перед созданием частиц необходимо правильно настроить сцену. В методе preload загружается текстура для частиц. В create устанавливается фон и параметры камеры, которые критически важны для отображения масштабного эффекта.
preload()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image("smokeTest", "assets/particles/smoke0.png");
}
create()
{
const mainCam = this.cameras.main;
const width = 1600;
const height = 1200;
mainCam.setBackgroundColor("#FFFFFF");
mainCam.setZoom(0.50);
mainCam.centerOn(width/2,height/2)
// ... создание частиц
}
Ключевые моменты: setZoom(0.50) уменьшает вид камеры, позволяя уместить большую рабочую область (1600x1200) в окно игры (800x600). centerOn позиционирует камеру по центру этой области. Без этого частицы могли бы оказаться за пределами видимости.
Создание сетки эмиттеров частиц
Вместо одного мощного источника частиц, в этом примере используется множество небольших эмиттеров, расположенных в сетке. Это даёт больше контроля над распределением эффекта.
for (let x = 0; x < width; x +=100)
{
for (let y = 0; y < height; y += 200)
{
this.add.particles(0, 0, "smokeTest", { /* конфиг */ });
}
}
Циклы создают эмиттер каждые 100 пикселей по X и 200 пикселей по Y, покрывая всю область. Первые два аргумента this.add.particles(0, 0, ...) — это координаты *эмиттера*. Однако в конфиге отдельно задаются `xиy` для *испускания* частиц. Это позволяет иметь один управляемый эмиттер (например, для паузы), но испускать частицы в разных точках сетки.
Конфигурация поведения частиц: движение и жизнь
Сердце эффекта — объект конфигурации. Он определяет, как частицы рождаются, движутся и исчезают.
{
x, // Позиция испускания берется из переменных цикла
y,
lifespan: { min: 3000, max: 5000 },
speed: { min: 5, max: 25 },
angle: { min: 260, max: 280 },
gravityX: -2,
gravityY: -5,
frequency: 25,
}
* lifespan: Время жизни в миллисекундах. Случайное значение делает исчезновение менее однородным.
* speed и angle: Задают начальный вектор движения. Угол ~270 градусов означает движение вверх (в системе координат Phaser, где 0 — направо).
* gravityX и gravityY: Постоянное ускорение, применямого к частицам каждое обновление. Отрицательные значения по Y (-5) тянут частицы вверх сильнее, а небольшой отрицательный gravityX (-2) создаёт лёгкий снос влево, добавляя динамики.
* frequency: Интервал в миллисекундах между испусканием новых частиц. Значение 25 означает примерно 40 частиц в секунду от каждого эмиттера.
Конфигурация внешнего вида: трансформация и цвет
Эти параметры отвечают за визуальное преобразование частиц на протяжении их жизни.
{
scale: {
start: 0.33,
end: 1.2,
ease: Phaser.Math.Easing.Cubic.In
},
rotate: { min: 0, max: 360 },
alpha: { start: 0.6, end: 0 },
tint: [0x000000, 0x141414, 0x292929, 0x2e2e2e],
// blendMode: Phaser.BlendModes.NORMAL,
}
* scale: Частицы увеличиваются от 33% до 120% размера текстуры. ease: Cubic.In означает, что рост начинается медленно, а к концу ускоряется.
* rotate: Каждая частица получает случайный начальный угол вращения.
* alpha: Плавное исчезновение от полупрозрачности (0.6) до полной прозрачности (0).
* tint: Массив цветов в HEX формате (0xRRGGBB). Частицам случайным образом назначается один из этих оттенков серого, создавая неоднородный, "грязный" дым.
* timeScale: Глобальный множитель скорости анимации для всех частиц в этом эмиттере (0.35 замедляет их).
Что попробовать дальше
Пример демонстрирует, как комбинация множества простых эмиттеров и детальной конфигурации рождает сложный и живой визуальный эффект. Для экспериментов попробуйте: изменить gravityY на положительное значение, чтобы дым опускался; заменить текстуру smoke0.png на искру или звезду; использовать blendMode: Phaser.BlendModes.ADD для создания светящихся эффектов; или динамически менять frequency или позицию (`x,y`) эмиттеров в реальном времени, привязав их к курсору мыши.
