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

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

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

Живой запуск

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

Исходный код


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

        this.hsl;
        this.count = 0;
        this.colorCount = 0;
        this.ropes = [];
    }

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('bg', 'assets/rope/background-geo-grey.jpg');
        this.load.image('glassblock', 'assets/rope/glassblock.png');
    }

    create ()
    {
        this.add.image(400, 300, 'bg');

        const hsl = Phaser.Display.Color.HSVColorWheel();

        const rope1 = this.add.rope(400, 150, 'glassblock', null, 32);
        
        //  Here we'll modify the Rope.colors array to use the HSV color values:

        for (let i = 0; i < rope1.colors.length; i++)
        {
            rope1.colors[i] = hsl[Math.floor(hsl.length / rope1.colors.length) * i].color;
        }

        const rope2 = this.add.rope(400, 300, 'glassblock', null, 32);
        
        //  In this Rope, each vertex will have a random color from the HSV color wheel:

        for (let i = 0; i < rope2.colors.length; i++)
        {
            rope2.colors[i] = Phaser.Utils.Array.GetRandom(hsl).color;
        }

        const rope3 = this.add.rope(400, 450, 'glassblock', null, 32);
        
        //  This Rope is updated in the `update` method, to cycle the color values through it:

        for (let i = 0; i < rope3.colors.length; i++)
        {
            rope3.colors[i] = hsl[i].color;
        }

        this.ropes.push(rope1, rope2, rope3);

        this.hsl = hsl;
    }

    update ()
    {
        this.count += 0.1;
        this.colorCount++;

        //  Color cycle rope3
        const rope3 = this.ropes[2];

        for (let i = 0; i < rope3.colors.length; i++)
        {
            let colorIndex = Phaser.Math.Wrap(this.colorCount + i, 0, this.hsl.length);

            rope3.colors[i] = this.hsl[colorIndex].color;
        }

        this.ropes.forEach((rope) => {

            let points = rope.points;

            for (let i = 0; i < points.length; i++)
            {
                points[i].y = Math.sin(i * 0.15 + this.count) * 24;
            }
    
            rope.setDirty();
    
        });
    }
}

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

let game = new Phaser.Game(config);

Создание основы: три веревки и цветовое колесо

Классический пример начинается с загрузки фона и текстуры для веревки. Ключевой подготовительный шаг — создание цветового колеса HSV с помощью Phaser.Display.Color.HSVColorWheel(). Этот метод возвращает массив объектов, содержащих все цвета радуги в формате HSV (и удобное свойство color в виде целочисленного значения), что идеально подходит для плавных переходов.

const hsl = Phaser.Display.Color.HSVColorWheel();

Далее создаются три веревки с помощью this.add.rope. Первый параметр — координаты X и Y, третий — ключ текстуры, четвертый — опциональная строка кадра (в данном случае null), а пятый — количество сегментов (или вершин) веревки. Чем больше сегментов, тем плавнее будут волны и цветовые переходы.

const rope1 = this.add.rope(400, 150, 'glassblock', null, 32);
const rope2 = this.add.rope(400, 300, 'glassblock', null, 32);
const rope3 = this.add.rope(400, 450, 'glassblock', null, 32);

Статичное окрашивание: градиент и случайные цвета

Каждый объект Rope имеет массив colors, длина которого соответствует количеству его вершин (сегментов). Изменяя значения в этом массиве, мы управляем цветом каждой вершины. В примере показаны два подхода к статичному окрашиванию.

Для первой веревки создается равномерный градиент по всему цветовому колесу. Мы берем цвет из массива hsl с определенным шагом, чтобы равномерно распределить цвета по длине веревки.

for (let i = 0; i < rope1.colors.length; i++) {
    rope1.colors[i] = hsl[Math.floor(hsl.length / rope1.colors.length) * i].color;
}

Вторая веревка получает совершенно случайную палитру. Для каждой вершины цвет выбирается произвольно из всего цветового колеса с помощью утилиты Phaser.Utils.Array.GetRandom.

for (let i = 0; i < rope2.colors.length; i++) {
    rope2.colors[i] = Phaser.Utils.Array.GetRandom(hsl).color;
}

Динамическая анимация цвета

Третья веревка демонстрирует динамическую смену цветов. Изначально ей присваивается последовательность цветов из начала колеса. Однако настоящая магия происходит в методе update. Глобальная переменная this.colorCount увеличивается каждый кадр, создавая эффект смещения цветов по веревке.

for (let i = 0; i < rope3.colors.length; i++) {
    let colorIndex = Phaser.Math.Wrap(this.colorCount + i, 0, this.hsl.length);
    rope3.colors[i] = this.hsl[colorIndex].color;
}

Функция Phaser.Math.Wrap обеспечивает зацикливание индекса в пределах длины массива hsl. Это означает, что когда colorCount + i превышает количество доступных цветов, индекс "перескакивает" обратно к началу, создавая бесконечную плавную цветовую анимацию, которая "бежит" вдоль веревки.

Анимация геометрии: создание волны

Помимо цвета, веревки оживляются за счет анимации их вершин. Каждый объект Rope имеет массив points, содержащий координаты каждой вершины. В цикле update для всех трех веревок изменяется координата `y` каждой точки по синусоидальному закону.

let points = rope.points;
for (let i = 0; i < points.length; i++) {
    points[i].y = Math.sin(i * 0.15 + this.count) * 24;
}

Переменная this.count, плавно увеличивающаяся каждый кадр, отвечает за движение волны вдоль веревки. Множитель i * 0.15 создает сдвиг фазы между соседними вершинами, что и формирует характерную волнообразную форму. Амплитуда волны задается числом 24.

После изменения координат или цветов крайне важно вызвать метод rope.setDirty(). Этот метод сообщает рендереру, что данные веревки изменились и ее необходимо перерисовать. Без этого вызова изменения не отобразятся на экране.

rope.setDirty();

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

Работа с массивами colors и points объекта Rope открывает широкие возможности для визуальных экспериментов в Phaser. Вы можете создавать не только качающиеся флаги или щупальца, но и сложные эффекты, например, радужные мосты, энергетические лучи или пульсирующие ауры. Попробуйте изменить закон движения точек (например, использовать косинус или шум Перлина), комбинировать анимацию цвета и геометрии по разным правилам или привязать изменение цвета к положению точки `y`. Это отличный способ добавить уникальные визуальные детали в вашу игру с минимальными затратами производительности.