О чем этот пример
Визуальные эффекты — ключ к созданию атмосферных и запоминающихся игр. В этой статье мы разберем, как использовать фильтры смешивания (Blend Filters) в Phaser для добавления динамического свечения и сложных цветовых преобразований к спрайтам. Вы научитесь рендерить анимацию в текстуру и применять к ней режимы наложения, такие как ADD, чтобы объекты сияли, а их цвета преображались прямо во время выполнения игры. Этот прием отлично подходит для создания магических заклинаний, сияющих кристаллов или неоновых интерфейсов.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
image;
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('bg', 'assets/pics/shadow-of-the-beast2-karamoon.png');
this.load.atlas('diamond', 'assets/animations/diamond.png', 'assets/animations/diamond.json');
}
create ()
{
this.add.image(640, 360, 'bg')
.setDisplaySize(1280, 720);
this.anims.create({
key: 'diamond',
frames: this.anims.generateFrameNames('diamond', { prefix: 'diamond_', end: 15, zeroPad: 4 }),
repeat: -1
});
const updaters = [];
// Create an off-screen sprite to render the diamond animation to a texture.
const diamond = this.make.sprite({
key: 'diamond'
});
diamond.play('diamond');
// Render diamond to a texture so we can use it easily.
const renderDiamond = this.add.renderTexture(640, 128, 64, 64);
renderDiamond.saveTexture('diamondOut');
// Update the render texture.
this.updateRenderDiamond = () => {
renderDiamond.clear();
renderDiamond.draw(diamond, diamond.width / 2, diamond.height);
renderDiamond.render();
updaters.forEach(fn => fn());
};
// Create a blend filter.
renderDiamond.enableFilters();
const blend = renderDiamond.filters.internal.addBlend('diamondOut', Phaser.BlendModes.ADD, 0);
// Tween the blend amount.
this.tweens.add({
targets: blend,
amount: 2,
duration: 1000,
yoyo: true,
repeat: -1
});
// Create a function to add a blended diamond using the same texture.
const addBlendedDiamond = (x, y, color) => {
const diamond = this.add.sprite(x, y, 'diamondOut');
diamond.enableFilters();
const newBlend = diamond.filters.internal.addBlend('diamondOut', Phaser.BlendModes.ADD, 0, color);
// Update the blend abount based on the first box.
updaters.push(() => {
newBlend.amount = blend.amount;
});
};
// Accentuate just the red channel.
addBlendedDiamond(340, 512, [1, 0, 0, 1]);
// Invert the blue channel, and accentuate the red, to create an orange gem.
addBlendedDiamond(940, 512, [1, 0, -1, 1]);
// Add more un-blended diamonds.
this.add.sprite(540, 192, 'diamond').play('diamond');
this.add.sprite(740, 192, 'diamond').play('diamond');
this.add.sprite(340, 512 - 64, 'diamond').play('diamond');
this.add.sprite(340 + 96, 512 + 64, 'diamond').play('diamond');
this.add.sprite(940, 512 - 64, 'diamond').play('diamond');
this.add.sprite(940 - 96, 512 + 64, 'diamond').play('diamond');
}
update ()
{
this.updateRenderDiamond();
}
}
const config = {
type: Phaser.WEBGL,
width: 1280,
height: 720,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и ресурсов
В начале, как и в любой сцене Phaser, мы загружаем необходимые ресурсы в preload(). Для нашего примера это фоновая картинка и атлас анимации бриллианта.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('bg', 'assets/pics/shadow-of-the-beast2-karamoon.png');
this.load.atlas('diamond', 'assets/animations/diamond.png', 'assets/animations/diamond.json');
}
В методе create() мы сначала размещаем фон и создаем анимацию из загруженного атласа. Ключевой момент — создание спрайта diamond через this.make.sprite(). Этот метод создает игровой объект, но не добавляет его на дисплей списка сцены, что идеально для объектов, которые должны рендериться "за кадром". Мы сразу запускаем на нем анимацию.
Рендеринг анимации в текстуру
Чтобы многократно использовать один и тот же кадр анимации с фильтрами, мы рендерим её в Render Texture. Render Texture — это специальный текстовый объект, на который можно отрисовывать другие игровые объекты, как на холст.
const renderDiamond = this.add.renderTexture(640, 128, 64, 64);
renderDiamond.saveTexture('diamondOut');
Мы создаем Render Texture размером 64x64 и сохраняем его под ключом 'diamondOut'. Это позволяет позже создавать спрайты, используя эту текстуру, как обычное изображение.
Для постоянного обновления текстуры актуальным кадром анимации мы создаем функцию updateRenderDiamond(). Она очищает текстуру, рисует в её центре наш off-screen спрайт с анимацией и принудительно вызывает рендеринг.
this.updateRenderDiamond = () => {
renderDiamond.clear();
renderDiamond.draw(diamond, diamond.width / 2, diamond.height);
renderDiamond.render();
updaters.forEach(fn => fn());
};
Эта функция вызывается каждый кадр в update(), обеспечивая плавную анимацию в текстуре.
Применение фильтра смешивания (Blend)
Самый интересный этап — добавление фильтра. Сначала мы активируем фильтры для Render Texture с помощью enableFilters(). Затем через менеджер filters.internal добавляем фильтр типа Blend.
renderDiamond.enableFilters();
const blend = renderDiamond.filters.internal.addBlend('diamondOut', Phaser.BlendModes.ADD, 0);
Параметры addBlend: имя текстуры для смешивания (в данном случае она смешивается сама с собой), режим наложения Phaser.BlendModes.ADD и начальное значение amount (силы эффекта). Режим ADD складывает значения цветов пикселей, что создает эффект яркого свечения.
Чтобы эффект был динамическим, мы анимируем свойство amount с помощью твина. Это заставляет свечение пульсировать.
this.tweens.add({
targets: blend,
amount: 2,
duration: 1000,
yoyo: true,
repeat: -1
});
Создание цветных вариаций с разными каналами
Теперь мы можем создавать новые спрайты на основе нашей текстуры 'diamondOut' и применять к ним свои фильтры смешивания. Функция addBlendedDiamond делает именно это.
const addBlendedDiamond = (x, y, color) => {
const diamond = this.add.sprite(x, y, 'diamondOut');
diamond.enableFilters();
const newBlend = diamond.filters.internal.addBlend('diamondOut', Phaser.BlendModes.ADD, 0, color);
updaters.push(() => {
newBlend.amount = blend.amount;
});
};
Ключевой параметр здесь — color. Это массив [r, g, b, a], который применяет цветовую матрицу к фильтру. Он умножает соответствующие цветовые каналы (R, G, B, A) исходного изображения.
В примере создаются два особых бриллианта:
1. **Акцентированный красный канал:** [1, 0, 0, 1] — умножает красный канал на 1, а зеленый и синий на 0. В режиме ADD это дает интенсивное красное свечение.
2. **Оранжевый оттенок:** [1, 0, -1, 1] — умножает красный на 1, синий на -1 (инвертирует), зеленый игнорируется. Инверсия синего канала в сочетании с красным дает оранжевые и желтые тона.
Функция в updaters синхронизирует силу эффекта (amount) нового фильтра с основным, поэтому все бриллианты пульсируют одинаково.
Что попробовать дальше
Фильтры смешивания в Phaser открывают мощный инструментарий для постобработки графики прямо в рантайме. Вы можете не только создавать свечение (ADD), но и экспериментировать с другими Phaser.BlendModes, такими как MULTIPLY для затемнения или SCREEN для осветления. Попробуйте анимировать не только amount, но и компоненты цветового вектора color, чтобы получить плавные цветовые переходы. Примените эту технику к частицам в системе эммитеров для создания сияющих взрывов или магических следов.
