О чем этот пример
Создание текстур на лету — мощный инструмент для оптимизации и динамического контента в играх. Вместо загрузки десятков изображений вы можете генерировать их программно, экономя память и время загрузки. В этой статье мы разберем, как создать Canvas-текстуру, нарисовать на ней градиент и анимировать её обновление, используя встроенные возможности Phaser.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
texture;
create ()
{
this.texture = this.textures.createCanvas('gradient', 16, 256);
// We can access the underlying Canvas context like this:
const grd = this.texture.context.createLinearGradient(0, 0, 0, 256);
grd.addColorStop(0, '#8ED6FF');
grd.addColorStop(1, '#004CB3');
this.texture.context.fillStyle = grd;
this.texture.context.fillRect(0, 0, 16, 256);
// Call this if running under WebGL, or you'll see nothing change
this.texture.refresh();
// Add a bunch of images that all use the same texture
for (let i = 0; i < 64; i++)
{
const image = this.add.image(8 + i * 16, 0, 'gradient');
this.tweens.add({
targets: image,
y: 650,
duration: 2000,
ease: 'Quad.easeInOut',
delay: i * 62.5,
yoyo: true,
repeat: -1
});
}
this.time.addEvent({ delay: 4000, callback: this.updateTexture, callbackScope: this, loop: true });
}
updateTexture ()
{
const grd = this.texture.context.createLinearGradient(0, 0, 0, 256);
grd.addColorStop(0, this.generateHexColor());
grd.addColorStop(1, this.generateHexColor());
this.texture.context.fillStyle = grd;
this.texture.context.fillRect(0, 0, 16, 256);
// Call this if running under WebGL, or you'll see nothing change
this.texture.refresh();
}
generateHexColor ()
{
return `#${((0.5 + 0.5 * Math.random()) * 0xFFFFFF << 0).toString(16)}`;
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Создание Canvas-текстуры: основа динамики
Phaser позволяет создавать текстуры прямо в коде, используя HTML5 Canvas. Это особенно полезно для генерации фонов, простых форм или текстур, которые меняются во время игры.
Ключевой метод — this.textures.createCanvas(). Он создает новый Canvas элемент, который становится текстурой, доступной для использования в спрайтах и изображениях.
this.texture = this.textures.createCanvas('gradient', 16, 256);
В этом примере мы создаем текстуру с ключом 'gradient', шириной 16 пикселей и высотой 256. После создания мы получаем доступ к её контексту рисования, чтобы работать с ней как с обычным Canvas.
Рисование на текстуре: работа с контекстом
После создания текстуры мы можем использовать стандартный Canvas API для рисования. Объект this.texture.context предоставляет доступ к контексту CanvasRenderingContext2D.
В примере рисуется вертикальный линейный градиент, который заполняет всю текстуру.
const grd = this.texture.context.createLinearGradient(0, 0, 0, 256);
grd.addColorStop(0, '#8ED6FF');
grd.addColorStop(1, '#004CB3');
this.texture.context.fillStyle = grd;
this.texture.context.fillRect(0, 0, 16, 256);
После рисования критически важно вызвать метод refresh(), если игра использует WebGL-рендерер. Этот метод сообщает графическому конвейеру, что текстура обновилась и её нужно перезагрузить в видеопамять.
this.texture.refresh();
Использование и анимация спрайтов с новой текстурой
Созданная текстура становится доступной в менеджере текстур Phaser под переданным ключом. Её можно использовать для любых сущностей, принимающих ключ текстуры, например, this.add.image().
В примере создается ряд изображений, которые используют нашу Canvas-текстуру 'gradient'. Каждому изображению добавляется твин (анимация) для движения по оси Y.
const image = this.add.image(8 + i * 16, 0, 'gradient');
this.tweens.add({
targets: image,
y: 650,
duration: 2000,
ease: 'Quad.easeInOut',
delay: i * 62.5,
yoyo: true,
repeat: -1
});
Важно понимать, что все эти изображения ссылаются на одну и ту же текстуру в памяти. Изменение исходной Canvas-текстуры мгновенно отразится на всех спрайтах, которые её используют.
Динамическое обновление текстуры во время выполнения
Настоящая сила Canvas-текстур раскрывается, когда их содержимое можно менять после создания. В примере по таймеру каждые 4 секунды вызывается функция updateTexture.
Она заново генерирует градиент, используя два случайных цвета, и перерисовывает текстуру.
this.time.addEvent({ delay: 4000, callback: this.updateTexture, callbackScope: this, loop: true });
Функция обновления повторяет процесс рисования: создает градиент, устанавливает его как стиль заливки, заполняет прямоугольник и, что важно, снова вызывает refresh().
grd.addColorStop(0, this.generateHexColor());
grd.addColorStop(1, this.generateHexColor());
this.texture.context.fillStyle = grd;
this.texture.context.fillRect(0, 0, 16, 256);
this.texture.refresh();
После вызова refresh() все 64 изображения на сцене моментально обновляются, отображая новый градиент. Это эффективно и не требует обновления каждого спрайта по отдельности.
Что попробовать дальше
Canvas-текстуры в Phaser — это мост между гибкостью программируемой графики и производительностью аппаратного рендеринга. Они идеально подходят для динамических фонов, процедурно генерируемых элементов интерфейса, спецэффектов и текстур, реагирующих на игровые события. Для экспериментов попробуйте: генерировать текстуры шума для эффектов дыма или воды, создавать динамические текстуры для индикаторов здоровья, которые меняют цвет и форму, или рисовать простые фигуры (круги, полосы) для использования в частицах.
