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

Визуальные эффекты — ключевая часть геймдева. Они оживляют мир, передают настроение и выделяют важные элементы. Один из простых, но мощных инструментов для этого в Phaser — система тинта (tint). Она позволяет динамически изменять цвет спрайтов, не создавая новые текстуры. Эта статья разберет пример с тремя режимами тинта: одноцветная заливка, многоцветная заливка и режим умножения (multiply). Вы узнаете, как применять их на практике и в каких игровых ситуациях они наиболее полезны.

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


class Example extends Phaser.Scene
{
    constructor ()
    {
        super();
    }

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('lulu', 'assets/pics/shocktroopers-lulu2.png');
    }

    create ()
    {
        const image1 = this.add.image(140, 300, 'lulu').setScale(2).setInteractive({ pixelPerfect: true });
        const image2 = this.add.image(140 + 260, 300, 'lulu').setScale(2).setInteractive({ pixelPerfect: true });
        const image3 = this.add.image(140 + 260 + 260, 300, 'lulu').setScale(2).setInteractive({ pixelPerfect: true });

        this.add.text(20, 20).setColor('#ffffff').setText('Click sprites to toggle tint mode');

        this.add.text(30, 500).setColor('#ffffff').setText('Single Tint Fill');
        this.add.text(290, 500).setColor('#ffffff').setText('Multi Tint Fill');
        this.add.text(550, 500).setColor('#ffffff').setText('Multiply Tint');

        image1.on('pointerdown', function () {

            if (this.isTinted)
            {
                this.clearTint();
            }
            else
            {
                this.setTintMode(Phaser.TintModes.FILL);
            }

        });

        image2.on('pointerdown', function () {

            if (this.isTinted)
            {
                this.clearTint();
            }
            else
            {
                this.setTint(0xffff00, 0xffff00, 0xff0000, 0xff0000).setTintMode(Phaser.TintModes.FILL);
            }

        });

        image3.on('pointerdown', function () {

            if (this.isTinted)
            {
                this.clearTint();
            }
            else
            {
                this.setTint(0xff00ff, 0xff0000, 0x00ff00, 0x0000ff);
            }

        });
    }
}

const config = {
    type: Phaser.WEBGL,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    backgroundColor: '#222288',
    pixelArt: true,
    scene: Example
};

const game = new Phaser.Game(config);

Настройка сцены и загрузка ресурсов

Вся работа происходит в классе сцены Example. В методе preload мы загружаем изображение, которое будем использовать. Обратите внимание на this.load.setBaseURL. Этот метод устанавливает базовый URL для всех последующих загрузок, что упрощает указание относительных путей.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('lulu', 'assets/pics/shocktroopers-lulu2.png');
}

В методе create мы создаем три экземпляра одного и того же изображения, выстраивая их в ряд. Каждому спрайту задается увеличенный масштаб (setScale(2)) и интерактивность с пиксельной точностью (setInteractive({ pixelPerfect: true })). Последний параметр важен для объектов с прозрачными областями — клик будет срабатывать только по непрозрачным пикселям. Также создаются текстовые подписи для каждого режима и общая инструкция.

create ()
{
    const image1 = this.add.image(140, 300, 'lulu').setScale(2).setInteractive({ pixelPerfect: true });
    const image2 = this.add.image(140 + 260, 300, 'lulu').setScale(2).setInteractive({ pixelPerfect: true });
    const image3 = this.add.image(140 + 260 + 260, 300, 'lulu').setScale(2).setInteractive({ pixelPerfect: true });

    this.add.text(20, 20).setColor('#ffffff').setText('Click sprites to toggle tint mode');

    this.add.text(30, 500).setColor('#ffffff').setText('Single Tint Fill');
    this.add.text(290, 500).setColor('#ffffff').setText('Multi Tint Fill');
    this.add.text(550, 500).setColor('#ffffff').setText('Multiply Tint');
    ...
}

Одноцветная заливка (Single Tint Fill)

Первый спрайт демонстрирует режим Phaser.TintModes.FILL с одним цветом. По умолчанию, если не задать цвет явно через setTint, будет использован белый (0xffffff). В этом режиме цвет спрайта полностью заменяется указанным оттенком, игнорируя исходную текстуру. Это похоже на заливку сплошным цветом.

Обработчик события pointerdown для image1 проверяет свойство isTinted. Если тинт уже применен, он сбрасывается методом clearTint. Если нет — активируется режим заливки.

image1.on('pointerdown', function () {
    if (this.isTinted)
    {
        this.clearTint();
    }
    else
    {
        this.setTintMode(Phaser.TintModes.FILL);
    }
});

Этот подход идеален для создания силуэтов, индикации состояния (например, выделение недоступного предмета серым цветом) или простой смены цвета униформы у однотипных персонажей.

Многоцветная заливка (Multi Tint Fill)

Второй спрайт использует тот же режим FILL, но с применением четырех разных цветов через метод setTint. Цвета задаются для каждого угла текстуры (top-left, top-right, bottom-left, bottom-right) и интерполируются между собой, создавая градиентный эффект.

image2.on('pointerdown', function () {
    if (this.isTinted)
    {
        this.clearTint();
    }
    else
    {
        this.setTint(0xffff00, 0xffff00, 0xff0000, 0xff0000).setTintMode(Phaser.TintModes.FILL);
    }
});

В данном коде верхним углам задан желтый цвет (0xffff00), а нижним — красный (0xff0000). Результат — плавный вертикальный градиент от желтого к красному. Важно: метод setTintMode вызывается цепочкой после setTint. Порядок имеет значение — сначала задаем цвета, затем режим их применения.

Такой прием отлично подходит для создания эффектов заката, магических аур или динамического освещения, когда цвет объекта должен меняться плавно по площади.

Режим умножения (Multiply Tint)

Третий спрайт работает в режиме тинта по умолчанию, который в Phaser называется Phaser.TintModes.MULTIPLY (хотя в коде он явно не указан). Это наиболее часто используемый режим. В нем заданный цвет умножается на исходный цвет пикселя текстуры. Вместо полной замены цвета происходит его "подкрашивание", сохраняющее детали и тени исходного изображения.

image3.on('pointerdown', function () {
    if (this.isTinted)
    {
        this.clearTint();
    }
    else
    {
        this.setTint(0xff00ff, 0xff0000, 0x00ff00, 0x0000ff);
    }
});

Здесь для каждого угла задан свой яркий цвет: пурпурный, красный, зеленый и синий. Так как режим по умолчанию — MULTIPLY, мы получаем разноцветное подкрашивание с сохранением деталей исходной картинки. Этот режим незаменим для создания атмосферных эффектов (ночная синева, свет костра), быстрой смены цвета одежды персонажа или визуального обозначения элемента стихии (огонь, вода, яд).

Конфигурация игры и важные настройки

Код примера завершается конфигурационным объектом и созданием экземпляра игры Phaser.Game. Обратите внимание на два ключевых параметра:

const config = {
    type: Phaser.WEBGL,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    backgroundColor: '#222288',
    pixelArt: true,
    scene: Example
};

1. type: Phaser.WEBGL — использование WebGL-рендерера обязательно для корректной работы режимов тинта, особенно FILL. При использовании Phaser.CANVAS некоторые эффекты могут отображаться некорректно. 2. pixelArt: true — эта настройка отключает линейную интерполяцию текстур при масштабировании, что сохраняет четкие, "пиксельные" края. Это важно для ретро-стилистики. Однако сам эффект тинта не зависит от этого параметра.

Темно-синий фон (backgroundColor: '#222288') выбран для лучшего контраста с яркими цветами тинта.

Что попробовать дальше

Система тинта в Phaser — это гибкий и производительный инструмент для динамического изменения внешнего вида спрайтов. Вы можете полностью перекрашивать объекты (FILL), создавать сложные градиенты или мягко подкрашивать их, сохраняя текстуру (MULTIPLY). **Идеи для экспериментов:** 1. Свяжите цвет тинта со здоровьем персонажа — от зеленого до красного. 2. Используйте setTintFill (короткий метод для setTint(...).setTintMode(Phaser.TintModes.FILL)) для быстрой одноцветной заливки. 3. Анимируйте изменение цвета тинта с помощью tweens, чтобы создавать плавные переходы (например, мигание при получении урона). 4. Комбинируйте режимы тинта с другими эффектами, например, с setAlpha для полупрозрачности.