О чем этот пример
Хотите добавить в игру динамичные, производительные визуальные эффекты без перегрузки CPU сотнями спрайтов? Рендер-текстура (Render Texture) в Phaser — ваш ключ к этому. Эта статья на практическом примере покажет, как с помощью всего одного объекта `RenderTexture` и метода `stamp()` создавать сложные, «живые» композиции, подобные знаменитому цифровому дождю из «Матрицы». Мы разберем, как этот подход экономит ресурсы и открывает двери для экспериментов с частицами, пост-обработкой и процедурной графикой.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
t;
rnd;
rt;
preload ()
{
// this.load.setBaseURL('https://cdn.phaserfiles.com/v385');
this.load.baseURL = 'https://cdn.rawgit.com/samid737/samid737.github.io/eca38c92/409/';
this.load.crossOrigin = 'anonymous';
this.load.spritesheet('matrix', '/assets/sprites/font.png', { frameWidth: 110, frameHeight: 125, endFrame: 23 });
}
create ()
{
this.rt = this.make.renderTexture({ x: 400, y: 300, width: 800, height: 600 });
this.rnd = Math.random;
}
update ()
{
for (let i = 0; i < 20; i++)
{
this.draw();
}
this.t = this.time.now / 100000;
this.cameras.main.shake(500, this.t / 100);
this.cameras.main.setZoom(1 + this.t);
}
draw ()
{
const alpha = this.rnd();
const tint = (0x00ffff * (this.rnd() * 0.1 + 0.8));
const frame = ~~(this.rnd() * 22);
this.rt.stamp('matrix', frame, this.rnd() * 800, this.rnd() * 600, { alpha, tint })
.render();
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
scene: Example,
width: 800,
height: 600
};
const game = new Phaser.Game(config);
Что такое рендер-текстура и зачем она здесь?
RenderTexture — это особый игровой объект в Phaser, который представляет собой холст в памяти. На него можно «рисовать» другие объекты (спрайты, текст, графику), а затем отображать эту текстуру как единое целое. Это мощный инструмент для оптимизации.
В нашем примере цель — создать эффект падающих символов, как в «Матрице». Вместо того чтобы создавать и управлять сотнями или тысячами отдельных спрайтов (что тяжело для производительности), мы создаем одну рендер-текстуру. Все символы «штампуются» на нее, и камера отображает уже готовый «кадр». Такой подход кардинально снижает количество обрабатываемых объектов в основном игровом цикле.
Инициализация сцены и загрузка ресурсов
Класс сцены наследуется от Phaser.Scene. В методе preload() загружается спрайтшит — изображение, содержащее все кадры (символы) нашего шрифта.
preload ()
{
this.load.baseURL = 'https://cdn.rawgit.com/samid737/samid737.github.io/eca38c92/409/';
this.load.crossOrigin = 'anonymous';
this.load.spritesheet('matrix', '/assets/sprites/font.png', { frameWidth: 110, frameHeight: 125, endFrame: 23 });
}
Здесь baseURL задает базовый путь для загрузки, а crossOrigin разрешает загрузку с другого домена (если нужно). Спрайтшит 'matrix' загружается с указанием размеров одного кадра (110x125) и общего их количества (23).
Создание рендер-текстуры в методе create()
В методе create(), который выполняется один раз при создании сцены, инициализируются ключевые объекты.
create ()
{
this.rt = this.make.renderTexture({ x: 400, y: 300, width: 800, height: 600 });
this.rnd = Math.random;
}
Строка this.make.renderTexture() создает объект рендер-текстуры. Параметры `xиyзадают его мировые координаты (центр текстуры будет в точке (400, 300) — центре экрана при разрешении 800x600).widthиheight` определяют размеры самой текстуры.
Также мы «кэшируем» ссылку на функцию Math.random в свойство this.rnd для удобства и небольшого прироста производительности в цикле.
Сердце эффекта: метод draw() и штамповка
Вся магия создания хаотичных символов происходит в методе draw(). Он вызывается многократно из основного цикла update().
draw ()
{
const alpha = this.rnd();
const tint = (0x00ffff * (this.rnd() * 0.1 + 0.8));
const frame = ~~(this.rnd() * 22);
this.rt.stamp('matrix', frame, this.rnd() * 800, this.rnd() * 600, { alpha, tint })
.render();
}
1. `alpha` — задает случайную прозрачность (от 0 до 1).
2. `tint` — вычисляет случайный оттенок цвета в диапазоне от немного темнее `0x00ffff` (бирюзовый) до его полной яркости. Множитель `(this.rnd() * 0.1 + 0.8)` дает диапазон от 0.8 до 0.9.
3. `frame` — выбирает случайный кадр (символ) из спрайтшита. Выражение `~~(this.rnd() * 22)` — это быстрый способ получить целое число от 0 до 21 (оператор `~~` эквивалентен `Math.floor`).
4. `this.rt.stamp()` — ключевой метод. Он «штампует» указанный кадр (`frame`) спрайтшита (`'matrix'`) на рендер-текстуру в случайные координаты (`this.rnd() * 800`, `this.rnd() * 600`). Опции `{ alpha, tint }` применяются к этому конкретному штампу. Метод возвращает ссылку на ту же текстуру, что позволяет сразу вызвать `.render()` для применения изменений.
Динамика в update(): анимация и камера
Метод update() выполняется каждый кадр игры и отвечает за динамику.
update ()
{
for (let i = 0; i < 20; i++)
{
this.draw();
}
this.t = this.time.now / 100000;
this.cameras.main.shake(500, this.t / 100);
this.cameras.main.setZoom(1 + this.t);
}
Цикл for вызывает draw() 20 раз за кадр, что быстро заполняет текстуру большим количеством символов, создавая эффект непрерывного потока.
Переменная this.t вычисляется на основе текущего времени игры (this.time.now), что обеспечивает плавное, непрерывно изменяющееся значение.
* `this.cameras.main.shake(500, this.t / 100)` — заставляет камеру дрожать (эффект тряски). Первый параметр (500) — длительность в миллисекундах (но так как метод вызывается каждый кадр, тряска становится постоянной). Второй параметр — интенсивность, которая медленно увеличивается со временем благодаря `this.t`.
* `this.cameras.main.setZoom(1 + this.t)` — плавно увеличивает зум камеры, создавая эффект «погружения» в цифровой поток.
Что попробовать дальше
Рендер-текстура в Phaser — это мощный и эффективный способ работы с массовыми визуальными эффектами. Показанный пример демонстрирует паттерн: создание одного холста в памяти и его интенсивное заполнение через stamp(). Для экспериментов попробуйте: изменить логику позиционирования штампов (например, падение сверху вниз), использовать другой алгоритм для tint (цветовой градиент), добавить постепенное затухание старых символов через this.rt.clear() с альфа-каналом или применить к самой рендер-текстуре шейдеры для дополнительной пост-обработки.
