О чем этот пример
Создание игр часто требует вариативности визуального контента. Например, чтобы показать повреждение врага или создать разнообразных противников из одного спрайта. Вручную подготавливать десятки текстур — трудоемко. Встроенный в Phaser фильтр `GradientMap` позволяет программно менять цветовую палитру изображения в реальном времени, используя градиент. Эта техника, известная как palette swap (смена палитры), экономит ресурсы, ускоряет разработку и открывает возможности для динамических визуальных эффектов, таких как изменение цвета персонажа в зависимости от статуса или уровня.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
original;
swapped;
gradientMap;
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('sprite', 'assets/sprites/shmup-boom.png');
}
create ()
{
this.original = this.add.image(320, 360, 'sprite').setScale(16);
this.swapped = this.add.image(960, 360, 'sprite').setScale(16);
// Map the colors to a gradient.
// You can define several bands based on image value,
// and assign different colors to do palette swapping,
// if the source image has values that fit this approach.
this.gradientMap = this.swapped.enableFilters().filters.internal.addGradientMap({
ramp: {
colorStart: 0x010000,
colorEnd: 0xfffeff,
colorSpace: 2
}
});
}
}
const config = {
type: Phaser.WEBGL,
width: 1280,
height: 720,
parent: 'phaser-example',
smoothPixelArt: true,
scene: Example
};
const game = new Phaser.Game(config);
Принцип работы Gradient Map
Фильтр GradientMap (градиентная карта) работает как таблица замены цветов. Он анализирует исходное изображение, переводя его в оттенки серого (усредняя значения красного, зеленого и синего каналов каждого пикселя). Полученное значение яркости (от 0 до 1) затем используется как позиция на пользовательском градиенте, чтобы определить новый цвет для этого пикселя.
Таким образом, вы определяете градиент (например, от темно-красного к ярко-желтому), и все темные участки спрайта окрашиваются в начало градиента, а светлые — в его конец. Это мощный инструмент для стилизации и быстрой генерации цветовых вариаций.
// Пример создания градиентной карты для `this.swapped`
this.gradientMap = this.swapped.enableFilters().filters.internal.addGradientMap({
ramp: {
colorStart: 0x010000,
colorEnd: 0xfffeff,
colorSpace: 2
}
});
Подготовка сцены и загрузка ассетов
В примере создается простая сцена, загружающая одно изображение и отображающая его в двух экземплярах: оригинальный и обработанный. Ключевые моменты:
* smoothPixelArt: true в конфигурации игры предотвращает размытие пиксельной графики при масштабировании, что критично для сохранения четкости при увеличении спрайта в 16 раз.
* Изображение размещается по центру двух половин экрана для наглядного сравнения.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('sprite', 'assets/sprites/shmup-boom.png');
}
create ()
{
// Оригинальный спрайт слева
this.original = this.add.image(320, 360, 'sprite').setScale(16);
// Спрайт для применения фильтра справа
this.swapped = this.add.image(960, 360, 'sprite').setScale(16);
}
// Конфигурация игры
const config = {
type: Phaser.WEBGL, // Фильтры работают только в WebGL-режиме
width: 1280,
height: 720,
parent: 'phaser-example',
smoothPixelArt: true, // Важно для пиксель-арта
scene: Example
};
const game = new Phaser.Game(config);
Создание и применение фильтра
Фильтры в Phaser применяются к игровым объектам, поддерживающим рендеринг, таким как Image или Sprite. Процесс состоит из двух шагов:
1. Активация системы фильтров для объекта с помощью метода enableFilters().
2. Добавление конкретного фильтра через filters.internal.addGradientMap(config).
Конфигурация ramp определяет градиент. В примере:
* colorStart: 0x010000 — почти черный цвет (очень темный сине-зеленый оттенок).
* colorEnd: 0xfffeff — почти белый цвет (с легким розовым оттенком).
* colorSpace: 2 — определяет цветовое пространство для интерполяции градиента. Значение 2 соответствует линейной интерполяции в sRGB пространстве, что является стандартным выбором.
// Включение фильтров для объекта и добавление GradientMap
this.gradientMap = this.swapped.enableFilters().filters.internal.addGradientMap({
ramp: {
colorStart: 0x010000, // Цвет для темных участков (яркость ~0)
colorEnd: 0xfffeff, // Цвет для светлых участков (яркость ~1)
colorSpace: 2 // Пространство цвета для расчета градиента
}
});
Эксперименты с параметрами градиента
Настоящая сила GradientMap раскрывается при изменении параметров градиента. Вы можете создавать не двухцветные, а многоцветные градиенты, используя массив stops. Это позволяет выполнять сложную замену палитры, выделяя разные тональные диапазоны исходного изображения.
// Пример многоцветного градиента для имитации "огненной" палитры
this.swapped.enableFilters().filters.internal.addGradientMap({
ramp: {
stops: [
{ offset: 0.0, color: 0x000000 }, // Черный для теней
{ offset: 0.3, color: 0x8b0000 }, // Темно-красный
{ offset: 0.7, color: 0xff4500 }, // Оранжево-красный
{ offset: 1.0, color: 0xffff00 } // Желтый для бликов
],
colorSpace: 2
}
});
Параметр offset (от 0 до 1) определяет позицию цвета в градиенте, соответствующую определенной яркости исходного пикселя.
Что попробовать дальше
Фильтр GradientMap — это эффективный способ динамического управления цветом в Phaser. Он превращает одну текстуру в множество визуальных вариантов, что идеально подходит для создания семейств врагов, индикаторов состояния (например, ядовитый/замороженный персонаж) или стилистических эффектов.
Для экспериментов попробуйте:
1. Анимировать цвета colorStart и colorEnd во времени, чтобы создать мерцающий или пульсирующий эффект.
2. Привязать смену палитры к игровым событиям (получение урона, усиление).
3. Использовать спрайт с четко выраженными областями яркости (например, затененный арт) для более предсказуемого и контролируемого результата замены цветов.
