О чем этот пример
RenderTexture в Phaser — это мощный инструмент для работы с графикой в реальном времени, но многие разработчики используют его только для отрисовки статичных изображений. В этой статье мы разберем, как с помощью `setCrop()` и `stamp()` превратить RenderTexture в интерактивную поверхность для динамического кадрирования. Этот подход полезен для создания интерактивных карт, масок, порталов или эффектов "подглядывания" в играх, где часть мира должна отображаться обособленно и реагировать на действия игрока. Вы научитесь рендерить множество спрайтов в текстуру и взаимодействовать с ней.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
offset;
graphics;
rt;
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 ()
{
this.rt = this.add.renderTexture(0, 0, 800, 800).setOrigin(0);
const atlasTexture = this.textures.get('megaset');
const frames = atlasTexture.getFrameNames();
for (let i = 0; i < frames.length; i++)
{
const x = Phaser.Math.Between(0, 800);
const y = Phaser.Math.Between(0, 600);
this.rt.stamp('megaset', frames[i], x, y);
}
this.rt.render();
this.graphics = this.add.graphics();
const cropWidth = 290;
const cropHeight = 120;
this.rt.setCrop(200, 200, cropWidth, cropHeight);
this.offset = this.rt.getTopLeft();
this.input.on('pointermove', pointer =>
{
this.rt.setCrop(
(pointer.x - this.offset.x) - cropWidth / 2,
(pointer.y - this.offset.y) - cropHeight / 2,
cropWidth,
cropHeight
);
});
}
update ()
{
this.graphics.clear();
this.graphics.lineStyle(1, 0x00ff00);
this.graphics.strokeRect(this.offset.x + this.rt._crop.x, this.offset.y + this.rt._crop.y, this.rt._crop.width, this.rt._crop.height);
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
backgroundColor: 0x2d2d2d,
scene: Example
};
const game = new Phaser.Game(config);
Подготовка и базовый рендеринг
Класс сцены содержит три ключевых свойства: offset, graphics и rt. rt — это наш главный объект, RenderTexture.
В preload() загружается атлас спрайтов 'megaset' с удаленного URL.
В create() происходит основная настройка. Сначала создается RenderTexture размером 800x800 пикселей с началом в точке (0,0).
this.rt = this.add.renderTexture(0, 0, 800, 800).setOrigin(0);
Затем из загруженной текстуры атласа получаем список всех имен кадров (фреймов).
const atlasTexture = this.textures.get('megaset');
const frames = atlasTexture.getFrameNames();
В цикле для каждого фрейма генерируются случайные координаты в пределах сцены, и он "штампуется" на RenderTexture.
for (let i = 0; i < frames.length; i++)
{
const x = Phaser.Math.Between(0, 800);
const y = Phaser.Math.Between(0, 600);
this.rt.stamp('megaset', frames[i], x, y);
}
Метод stamp() рисует указанный кадр из текстуры в заданные координаты на RenderTexture. После заполнения текстуры вызывается this.rt.render(), чтобы отобразить результат.
Настройка кадрирования (Crop)
После рендеринга создается объект Graphics для отладки — он будет рисовать зеленую рамку вокруг области кадрирования.
Задаются начальные размеры окна кадрирования (кропа).
const cropWidth = 290;
const cropHeight = 120;
На RenderTexture устанавливается начальная область кадрирования методом setCrop(). Он принимает координаты X, Y левого верхнего угла, ширину и высоту области.
this.rt.setCrop(200, 200, cropWidth, cropHeight);
Важно: координаты кадрирования (crop.x, crop.y) отсчитываются от внутренней системы координат самой RenderTexture. Чтобы корректно связать их с координатами указателя на канвасе, нам нужна точка привязки текстуры на сцене. Метод getTopLeft() возвращает мировые координаты верхнего левого угла текстуры.
this.offset = this.rt.getTopLeft();
Это значение сохраняется и будет использоваться для корректировки позиции указателя.
Интерактивное управление кадрированием
Для интерактивности на событие движения указателя pointermove вешается обработчик. При каждом движении мыши или касании вычисляется новая область кадрирования.
this.input.on('pointermove', pointer =>
{
this.rt.setCrop(
(pointer.x - this.offset.x) - cropWidth / 2,
(pointer.y - this.offset.y) - cropHeight / 2,
cropWidth,
cropHeight
);
});
Логика расчета:
1. pointer.x - this.offset.x — переводит мировые координаты указателя в локальные координаты RenderTexture.
2. - cropWidth / 2 — центрирует область кадрирования относительно позиции указателя. Без этого смещения левый верхний угол кропа будет находиться прямо под курсором.
Таким образом, окно кадрирования будет следовать за указателем, оставаясь центрированным на нем.
Визуализация области кадрирования
Метод update() вызывается на каждом кадре игры. Здесь происходит отрисовка зеленой рамки, которая помогает визуализировать текущую вырезанную область.
update ()
{
this.graphics.clear();
this.graphics.lineStyle(1, 0x00ff00);
this.graphics.strokeRect(this.offset.x + this.rt._crop.x, this.offset.y + this.rt._crop.y, this.rt._crop.width, this.rt._crop.height);
}
Важные моменты:
- `this.graphics.clear()` — очищает рисунок с предыдущего кадра.
- `this.graphics.lineStyle(1, 0x00ff00)` — задает стиль линии: толщина 1 пиксель, зеленый цвет.
- Для расчета позиции прямоугольника на сцене используются внутренние свойства `_crop` объекта RenderTexture. Обратите внимание на символ подчеркивания — это непубличное свойство, используемое здесь для демонстрации. Координаты кропа (`_crop.x`, `_crop.y`) преобразуются в мировые путем прибавления `this.offset`.
Благодаря этому в update() мы визуализируем, какая именно часть большой RenderTexture в данный момент отображается на сцене.
Что попробовать дальше
RenderTexture с кадрированием — это гибкий механизм для создания сложной графики. Вы научились заполнять текстуру спрайтами, вырезать из нее интерактивные области и визуализировать процесс.
Идеи для экспериментов:
1. Изменяйте размер области кадрирования (cropWidth, cropHeight) в зависимости от скорости движения указателя.
2. Используйте не прямоугольный кроп, а создайте маску (this.rt.setMask()) сложной формы для рендера в текстуру.
3. Применяйте этот подход для создания мини-карты, которая показывает только окрестности игрока на большой уровне, отрендеренной в текстуру.
4. Анимируйте параметры кадрирования для создания эффектов "открывания" или плавного перехода между кадрами.
