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

При разработке игр часто возникает задача генерации комбинаций элементов: уровней, врагов, диалогов или анимационных ключей. Писать вложенные циклы каждый раз неэффективно. Утилита `Phaser.Utils.Array.Range` из библиотеки Phaser 3 решает эту проблему, предлагая гибкий инструмент для создания последовательностей на основе двух массивов. Эта статья покажет, как с её помощью генерировать предсказуемые и случайные комбинации, управлять количеством и порядком элементов, что полезно при создании процедурного контента, систем волн противников или сложных цепочек событий.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    create ()
    {
        // var a = [ 'a', 'b', 'c' ];
        // var a = [ 'a' ];
        const a = [ 'a', 'b' ];
        const b = [ 1, 2 ];

        // var b = [ 1, 2, 3 ];
        // var b = [ 1, 2, 3, 4, 5, 6, 7, 8 ];

        // var out = Phaser.Utils.Array.Range(a, b);

        // var out = Phaser.Utils.Array.Range(a, b, { repeat: 1 });
     
        // var out = Phaser.Utils.Array.Range(a, b, { yoyo: true, repeat: 1 });

        // var out = Phaser.Utils.Array.Range(a, b, { qty: 3, yoyo: true });

        // var out = Phaser.Utils.Array.Range(a, b, { random: true });

        // var out = Phaser.Utils.Array.Range(a, b, { randomB: true });

        const out = Phaser.Utils.Array.Range(a, b, { repeat: -1, max: 10 });

        const text = [ '{' ];
        out.forEach(e =>
        {
            text.push(` ${JSON.stringify(e)}`);
        });
        text.push('}');

        this.add.text(100, 100, text, { font: '32px Courier', fill: '#00ff00' });
    }
}

const config = {
    type: Phaser.CANVAS,
    parent: 'phaser-example',
    scene: Example
};

const game = new Phaser.Game(config);

Что делает Phaser.Utils.Array.Range?

Метод Phaser.Utils.Array.Range генерирует новый массив, комбинируя элементы из двух исходных массивов (`aиb`) согласно заданным правилам. Базовый принцип похож на декартово произведение: каждый элемент первого массива последовательно сопоставляется с каждым элементом второго, формируя пары. Однако утилита предлагает гораздо больше контроля над процессом через объект настроек.

Рассмотрим сигнатуру метода:

Phaser.Utils.Array.Range(a, b, options)

Здесь `aиb— исходные массивы любого типа.options` — необязательный объект конфигурации, который мы детально разберем в следующих разделах.

Базовое использование и настройка повторений

Без передачи объекта options метод создаст простую последовательность пар, перебирая все элементы массива `bдля каждого элемента массиваa`.

const letters = ['a', 'b'];
const numbers = [1, 2];
const out = Phaser.Utils.Array.Range(letters, numbers);
// Результат: [ ['a',1], ['a',2], ['b',1], ['b',2] ]

Ключ repeat позволяет повторить всю сгенерированную последовательность указанное количество раз. Значение -1 задает бесконечное повторение, которое ограничивается ключом max.

const out = Phaser.Utils.Array.Range(a, b, { repeat: 1 });
// Последовательность пройдет дважды.
const out = Phaser.Utils.Array.Range(a, b, { repeat: -1, max: 10 });
// Бесконечное повторение, но всего будет сгенерировано 10 элементов.

Использование эффекта йо-йо и ограничение количества

Опция yoyo изменяет поведение при повторении. Если yoyo: true, то при каждом новом проходе последовательность будет воспроизводиться в обратном порядке, создавая эффект "туда-обратно".

const out = Phaser.Utils.Array.Range(a, b, { yoyo: true, repeat: 1 });
// Прямой проход: [a,1], [a,2], [b,1], [b,2]
// Обратный проход (йо-йо): [b,2], [b,1], [a,2], [a,1]

Ключ qty позволяет явно задать общее количество элементов в итоговом массиве. Метод самостоятельно рассчитает, сколько полных или частичных циклов нужно сделать для достижения этого числа.

const out = Phaser.Utils.Array.Range(a, b, { qty: 3, yoyo: true });
// Будет создан массив ровно из 3 пар, с учетом йо-йо.

Генерация случайных последовательностей

Утилита предоставляет два способа внести случайность в результат. Опция random: true перемешивает весь итоговый массив после его генерации по стандартным правилам.

const out = Phaser.Utils.Array.Range(a, b, { random: true });
// Пары будут следовать в случайном порядке.

Более интересна опция randomB: true. В этом случае для каждого элемента из массива `aбудет выбран *случайный* элемент из массиваb`. Это не просто перемешивание, а случайное сопоставление на этапе создания пар.

const out = Phaser.Utils.Array.Range(a, b, { randomB: true });
// Для 'a' случайно выбран элемент из [1,2], для 'b' — другой случайный элемент из [1,2].

Практическое применение в играх

Рассмотрим несколько идей, где Phaser.Utils.Array.Range сэкономит время и строки кода.

**Генерация волн противников:**

const enemyTypes = ['grunt', 'ranged', 'tank'];
const spawnPoints = [100, 200, 300, 400];
const wave = Phaser.Utils.Array.Range(enemyTypes, spawnPoints, { random: true, max: 15 });
// Создаст 15 пар [тип врага, точка появления] в случайном порядке.

**Создание сетки анимационных ключей для спрайта:**

const actions = ['idle', 'walk', 'attack'];
const directions = ['north', 'east', 'south', 'west'];
const animationKeys = Phaser.Utils.Array.Range(actions, directions);
// Получим массив вида ['idle_north', 'idle_east', ..., 'attack_west'] для загрузки атласа.

**Построение диалоговых цепочек с йо-йо:**

const characters = ['hero', 'npc'];
const moods = ['neutral', 'angry', 'happy'];
const dialogueSequence = Phaser.Utils.Array.Range(characters, moods, { yoyo: true, repeat: 2 });
// Диалог будет плавно переходить от нейтральных реплик к эмоциональным и обратно.

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

Phaser.Utils.Array.Range — мощный и недооцененный инструмент для декларативного описания последовательностей. Он убирает из кода шаблонные вложенные циклы и сложную логику индексов, делая его чище и нагляднее. Для экспериментов попробуйте комбинировать опции: создайте бесконечную йо-йо последовательность (repeat: -1, yoyo: true) с ограничением по max для плавного патрулирования врага или используйте randomB для генерации уровней, где каждая комната содержит случайный, но связанный с типом комнаты набор предметов.