О чем этот пример

Создание игр часто требует вариативности визуального контента. Например, чтобы показать повреждение врага или создать разнообразных противников из одного спрайта. Вручную подготавливать десятки текстур — трудоемко. Встроенный в 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. Использовать спрайт с четко выраженными областями яркости (например, затененный арт) для более предсказуемого и контролируемого результата замены цветов.