О чем этот пример
Работа с цветами — частая задача в игровой разработке, будь то плавные переходы интерфейса или динамическое освещение. Phaser предоставляет удобный инструментарий в классе `Phaser.Display.Color`. Однако один из методов может вести себя неочевидно и возвращать `undefined`, ломая логику игры, если не знать тонкости его работы. В этой статье мы разберем конкретный кейс из баг-трекера фреймворка, объясним причину ошибки и покажем, как правильно использовать интерполяцию цветов.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
constructor()
{
super({
key: "example"
});
}
create ()
{
let interpValue = 0.5;
const colorA = Phaser.Display.Color.IntegerToColor(0x000000); //Black
const colorB = Phaser.Display.Color.IntegerToColor(0xFFFFFF); //White
const colorObject = Phaser.Display.Color.Interpolate.ColorWithColor(colorA, colorB, 1, interpValue);
console.log(colorObject);
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#1d1d1d',
parent: 'phaser-example',
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH
},
scene: [ Example ]
};
const game = new Phaser.Game(config);
В чем проблема?
В представленном примере разработчик пытается получить промежуточный цвет между черным (0x000000) и белым (0xFFFFFF). Логика кажется простой: есть два цвета и значение интерполяции (в данном случае 0.5, что означает ровно середина). Однако при запуске этого кода в консоль выводится undefined, а не ожидаемый объект цвета серого оттенка.
Код, вызывающий проблему, выглядит так:
let interpValue = 0.5;
const colorA = Phaser.Display.Color.IntegerToColor(0x000000); //Black
const colorB = Phaser.Display.Color.IntegerToColor(0xFFFFFF); //White
const colorObject = Phaser.Display.Color.Interpolate.ColorWithColor(colorA, colorB, 1, interpValue);
console.log(colorObject); // Вывод: undefined
Разбираем аргументы метода ColorWithColor
Ключ к пониманию ошибки — в сигнатуре метода. Взглянем на официальную документацию или исходный код Phaser. Метод Phaser.Display.Color.Interpolate.ColorWithColor ожидает четыре аргумента в строгом порядке:
1. color1 (Phaser.Types.Display.ColorObject): Исходный цвет.
2. color2 (Phaser.Types.Display.ColorObject): Конечный цвет.
3. length (number): Общее количество шагов в интерполяции. Это целое число (integer), определяющее, на сколько частей разбивается переход.
4. index (number): Конкретный шаг, цвет для которого мы хотим получить. Должен быть в диапазоне от 0 до length.
Главное правило: index не может быть больше или равен length. Метод возвращает цвет для конкретного шага в дискретной последовательности.
Ошибка в исходном примере
Теперь становится ясно, что не так с кодом из баг-репорта. Разработчик передал length = 1 и index = 0.5.
// Неверные аргументы
const colorObject = Phaser.Display.Color.Interpolate.ColorWithColor(colorA, colorB, 1, 0.5);
При length = 1 существует только два валидных индекса: `0(полностью цвет A) и1(полностью цвет B). Любое дробное значение, такое как0.5, выходит за пределы этого дискретного набора шагов. Именно поэтому метод не может найти подходящий цвет и возвращаетundefined`. Это не баг, а строгое следование заложенной логике.
Как делать правильно: два подхода
Есть два основных способа получить промежуточный цвет.
**1. Дискретная интерполяция (для заранее известного числа шагов).**
Если вам нужен, например, 5-й цвет из 10-шагового градиента, используйте целые числа для length и index.
// 10 шагов между цветами, получаем 5-й шаг (индекс 4)
const steps = 10;
const stepIndex = 4;
const correctColor = Phaser.Display.Color.Interpolate.ColorWithColor(colorA, colorB, steps, stepIndex);
console.log(correctColor); // Выведет объект цвета
**2. Плавная интерполяция (для любого дробного значения).**
Для получения цвета по произвольному коэффициенту (например, от 0.0 до 1.0) используйте метод Phaser.Display.Color.Interpolate.RGBWithRGB. Он работает напрямую с компонентами цветов.
// Плавный переход по значению factor от 0.0 (colorA) до 1.0 (colorB)
let factor = 0.5;
const r = Phaser.Math.Interpolation.Linear([colorA.r, colorB.r], factor);
const g = Phaser.Math.Interpolation.Linear([colorA.g, colorB.g], factor);
const b = Phaser.Math.Interpolation.Linear([colorA.b, colorB.b], factor);
// Создаем новый объект цвета из полученных компонентов
const smoothColor = new Phaser.Display.Color(r, g, b);
console.log(smoothColor); // Объект серого цвета
Практическое применение в игре
Понимание этой разницы критично для геймдева.
* ColorWithColor идеален для ситуаций, где переход разбит на этапы. Например, индикатор здоровья, который меняет цвет по заранее заданным ступеням (зеленый -> желтый -> красный за 3 шага).
* RGBWithRGB с линейной интерполяцией нужен для плавных, анимированных изменений. Например, для затемнения экрана (Tween) или динамического окрашивания спрайта в зависимости от времени суток в игре.
Вот как можно быстро получить плавный градиентный цвет, обернув логику в функцию:
function getInterpolatedColor(colorA, colorB, t) {
// t - значение от 0 до 1
const r = Phaser.Math.Interpolation.Linear([colorA.r, colorB.r], t);
const g = Phaser.Math.Interpolation.Linear([colorA.g, colorB.g], t);
const b = Phaser.Math.Interpolation.Linear([colorA.b, colorB.b], t);
return new Phaser.Display.Color(Math.round(r), Math.round(g), Math.round(b));
}
const myGrey = getInterpolatedColor(colorA, colorB, 0.5);
Что попробовать дальше
Метод ColorWithColor — это инструмент для дискретной интерполяции, работающий с целочисленными шагами. Ошибка undefined возникает при передаче дробного индекса, выходящего за пределы заданного количества шагов (length). Для плавных переходов используйте комбинацию Interpolate.RGBWithRGB или линейной интерполяции компонентов.
**Идеи для экспериментов:** Попробуйте создать индикатор, который плавно меняет цвет в зависимости от значения (заряда щита, уровня ярости). Или реализуйте систему времени суток, где цвет неба интерполируется между несколькими ключевыми оттенками (рассвет, день, закат, ночь) с помощью ColorWithColor и length, равному 4.
