О чем этот пример
Эффект "цифрового дождя", известный по фильму "Матрица", — это отличный способ добавить атмосферы и динамики в игру. В Phaser его можно реализовать с помощью мощной системы партиклов, создав не просто хаотичный поток символов, а управляемую, стильную анимацию. Эта статья покажет, как использовать `ParticleEmitter` для генерации сложных эффектов на основе пользовательской логики, что пригодится для создания фонов, визуализаций данных или просто крутых интро.
Версия 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.spritesheet('font', 'assets/fonts/retro/knighthawks-font-filled.png', { frameWidth: 32, frameHeight: 25 });
}
create ()
{
const codeRain = {
width: 50,
height: 40,
cellWidth: 16,
cellHeight: 16,
getPoints: function (quantity)
{
const cols = (new Array(codeRain.width)).fill(0);
const lastCol = cols.length - 1;
const Between = Phaser.Math.Between;
const RND = Phaser.Math.RND;
const points = [];
for (let i = 0; i < quantity; i++)
{
const col = Between(0, lastCol);
let row = (cols[col] += 1);
if (RND.frac() < 0.01)
{
row *= RND.frac();
}
row %= codeRain.height;
row |= 0;
points[i] = new Phaser.Math.Vector2(16 * col, 16 * row);
}
return points;
}
};
this.add.particles('font').createEmitter({
alpha: { start: 1, end: 0.25, ease: 'Expo.easeOut' },
angle: 0,
blendMode: 'ADD',
emitZone: { source: codeRain, type: 'edge', quantity: 2000 },
frame: Phaser.Utils.Array.NumberArray(8, 58),
frequency: 100,
lifespan: 6000,
quantity: 25,
scale: -0.5,
tint: 0x0066ff00
});
}
}
const config = {
type: Phaser.WEBGL,
width: 800,
height: 600,
backgroundColor: '#000',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Загрузка ресурсов и основа эффекта
Всё начинается с загрузки спрайтшита — изображения, содержащего набор кадров (символов). В данном примере используется шрифт в стиле ретро.
this.load.spritesheet('font', 'assets/fonts/retro/knighthawks-font-filled.png', { frameWidth: 32, frameHeight: 25 });
Ключевая идея эффекта — эмитировать частицы не случайно по всей сцене, а в узлах невидимой сетки, имитирующей падающие столбцы кода. Для этого мы создаём пользовательский объект codeRain, который будет определять эту сетку и точки эмиссии.
Создание пользовательской эмиттерной зоны
Стандартные зоны эмиссии в Phaser (например, RandomZone) разбрасывают частицы случайно. Для контролируемого эффекта дождя нужна своя логика. Объект codeRain описывает сетку и предоставляет метод getPoints для генерации точек.
В его свойствах задаётся размер сетки в "ячейках" и их физические размеры в пикселях.
const codeRain = {
width: 50,
height: 40,
cellWidth: 16,
cellHeight: 16,
getPoints: function (quantity) { /* ... */ }
};
Метод getPoints — это сердце эффекта. Он получает запрошенное количество точек (quantity) и возвращает массив векторов Phaser.Math.Vector2 с координатами для новых частиц.
Алгоритм работает так:
1. Создаётся массив cols для отслеживания текущей "высоты" в каждом столбце сетки.
2. Для каждой новой точки случайно выбирается столбец.
3. "Высота" (строка) в этом столбце увеличивается на 1, создавая эффект последовательного падения.
4. С небольшим шансом (1%) строка сдвигается случайным образом, добавляя лёгкий хаос.
5. Координаты переводятся из ячеек сетки в пиксели на экране.
const col = Between(0, lastCol);
let row = (cols[col] += 1);
if (RND.frac() < 0.01) { row *= RND.frac(); }
row %= codeRain.height;
row |= 0;
points[i] = new Phaser.Math.Vector2(16 * col, 16 * row);
Настройка эмиттера частиц
Созданный объект codeRain используется как источник (emitZone) для эмиттера частиц. Указывается тип 'edge', что означает использование метода getPoints для получения точек на "границе" зоны.
emitZone: { source: codeRain, type: 'edge', quantity: 2000 }
Параметр quantity в зоне эмиссии (2000) — это общий пул предрассчитанных точек, из которого эмиттер будет брать позиции. Остальные параметры эмиттера управляют визуальным поведением каждой частицы:
- frame: задаёт диапазон кадров из спрайтшита, которые будут использоваться как частицы (символы с кодами от 8 до 58).
- alpha, lifespan, scale: определяют, как частица появляется, плавно исчезает и уменьшается за время жизни.
- frequency и quantity: управляют интенсивностью — каждые 100 мс испускается 25 новых частиц.
- tint: окрашивает все частицы в зелёный цвет (0x0066ff00), характерный для "матричного" стиля.
this.add.particles('font').createEmitter({
alpha: { start: 1, end: 0.25, ease: 'Expo.easeOut' },
angle: 0,
blendMode: 'ADD',
emitZone: { source: codeRain, type: 'edge', quantity: 2000 },
frame: Phaser.Utils.Array.NumberArray(8, 58),
frequency: 100,
lifespan: 6000,
quantity: 25,
scale: -0.5,
tint: 0x0066ff00
});
Почему именно такая конфигурация работает
Комбинация параметров создаёт убедительную иллюзию:
- **blendMode: 'ADD'** заставляет яркие зелёные символы "светиться" и сливаться при наложении, что усиливает эффект потока данных.
- **scale: -0.5** — отрицательное значение масштаба означает, что частица будет уменьшаться от своего начального размера до половины. Это создаёт перспективу — символы "убегают" вглубь экрана.
- **Долгий lifespan (6000 мс) и частый frequency (100 мс)** обеспечивают плавное, непрерывное заполнение экрана частицами без резких пропаданий.
- **Пользовательская emitZone** — главный секрет. Без неё мы получили бы просто облако случайных мерцающих символов. Логика getPoints организует их в упорядоченные, но слегка "сломанные" столбцы, что и является визуальной отсылкой к цифровому дождю.
Что попробовать дальше
Вы создали не просто эмиттер частиц, а систему, где каждая новая частица появляется в контексте общего алгоритма — это мощный подход. Для экспериментов попробуйте: изменить логику в getPoints на волновую или спиральную; заменить tint на градиент в зависимости от позиции row; использовать другую текстуру (например, простые линии или иконки) для создания эффекта звёздного дождя или водопада из монет. Система частиц Phaser становится по-настоящему гибкой, когда вы управляете источником их появления.
