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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.spritesheet('invader1', 'assets/tests/invaders/invader1.png', { frameWidth: 32, frameHeight: 32 });
        this.load.spritesheet('invader2', 'assets/tests/invaders/invader2.png', { frameWidth: 44, frameHeight: 32 });
        this.load.spritesheet('invader3', 'assets/tests/invaders/invader3.png', { frameWidth: 48, frameHeight: 32 });
    }

    create ()
    {
        const invader1 = this.add.group({ key: 'invader1', frame: 0, repeat: 13, setXY: { x: 32, y: 100, stepX: 40 } });

        const invader2 = this.add.group([
            { key: 'invader2', frame: 0, repeat: 10, setXY: { x: 32, y: 148, stepX: 52 } },
            { key: 'invader2', frame: 0, repeat: 10, setXY: { x: 32, y: 148 + 48, stepX: 52 } }
        ]);

        const invader3 = this.add.group([
            { key: 'invader3', frame: 0, repeat: 9, setXY: { x: 32, y: 148 + 96, stepX: 58 } },
            { key: 'invader3', frame: 0, repeat: 9, setXY: { x: 32, y: 148 + 96 + 48, stepX: 58 } }
        ]);

        Phaser.Actions.IncX(invader1.getChildren(), 100);
        Phaser.Actions.IncX(invader2.getChildren(), 100);
        Phaser.Actions.IncX(invader3.getChildren(), 100);

        Phaser.Actions.SetTint(invader1.getChildren(), 0xff0000);
        Phaser.Actions.SetTint(invader2.getChildren(), 0x00ff00);
        Phaser.Actions.SetTint(invader3.getChildren(), 0x00ffff);
    }
}

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

const game = new Phaser.Game(config);

Загрузка ресурсов и настройка группы

Перед созданием группы необходимо загрузить графические ресурсы. В методе preload мы используем this.load.spritesheet, чтобы загрузить три разных спрайтшита с пришельцами, указывая ширину и высоту каждого кадра.

this.load.spritesheet('invader1', 'assets/tests/invaders/invader1.png', { frameWidth: 32, frameHeight: 32 });
this.load.spritesheet('invader2', 'assets/tests/invaders/invader2.png', { frameWidth: 44, frameHeight: 32 });
this.load.spritesheet('invader3', 'assets/tests/invaders/invader3.png', { frameWidth: 48, frameHeight: 32 });

Создание группы начинается в методе create. Самый простой способ — использовать this.add.group, передав объект конфигурации. Ключевые параметры: - key: ключ загруженного изображения. - frame: номер кадра из спрайтшита (в нашем случае 0). - repeat: количество *дополнительных* создаваемых объектов. Итоговое количество = repeat + 1. - setXY: объект, задающий позицию первого элемента и шаг по оси X для последующих.

const invader1 = this.add.group({ key: 'invader1', frame: 0, repeat: 13, setXY: { x: 32, y: 100, stepX: 40 } });

Эта строка создает группу invader1, состоящую из 14 спрайтов (1 + 13 повторов). Первый пришелец будет размещен в точке (32, 100), а каждый следующий — на 40 пикселей правее (stepX: 40).

Создание сложных формаций через массив конфигов

Что если нужно создать несколько рядов пришельцев одного типа? Для этого this.add.group может принимать массив объектов конфигурации. Каждый элемент массива создает свой ряд объектов с заданными параметрами.

const invader2 = this.add.group([
    { key: 'invader2', frame: 0, repeat: 10, setXY: { x: 32, y: 148, stepX: 52 } },
    { key: 'invader2', frame: 0, repeat: 10, setXY: { x: 32, y: 148 + 48, stepX: 52 } }
]);

В этом примере создается группа invader2, содержащая два ряда. В каждом ряду по 11 пришельцев (1 + 10 повторов). Первый ряд начинается с координаты Y = 148, второй — на 48 пикселей ниже (148 + 48). Шаг между пришельцами в ряду (stepX) равен 52 пикселя, что соответствует ширине спрайта (44px) плюс небольшой отступ.

Аналогично создается группа invader3 с еще двумя рядами, расположенными еще ниже и с большим шагом (stepX: 58), так как эти спрайты шире (48px).

Массовые операции с элементами групп

После создания групп часто требуется применить одно действие ко всем их элементам. Phaser предоставляет для этого утилиты Phaser.Actions. Они работают с массивами игровых объектов. Чтобы получить такой массив из группы, используем метод getChildren().

Сначала сдвинем все созданные ряды пришельцев вправо на 100 пикселей. Phaser.Actions.IncX увеличивает координату X каждого объекта в переданном массиве на указанное значение.

Phaser.Actions.IncX(invader1.getChildren(), 100);
Phaser.Actions.IncX(invader2.getChildren(), 100);
Phaser.Actions.IncX(invader3.getChildren(), 100);

Затем окрасим ряды в разные цвета с помощью Phaser.Actions.SetTint. Этот метод применяет тонирование (tint) к каждому спрайту в массиве. Цвет задается в шестнадцатеричном формате (0xRRGGBB).

Phaser.Actions.SetTint(invader1.getChildren(), 0xff0000); // Красный
Phaser.Actions.SetTint(invader2.getChildren(), 0x00ff00); // Зеленый
Phaser.Actions.SetTint(invader3.getChildren(), 0x00ffff); // Голубой

Эти действия демонстрируют мощь групп: управление десятками объектов сводится к нескольким строкам кода.

Конфигурация игры и запуск сцены

Весь описанный код находится внутри класса сцены Example. Чтобы игра заработала, необходимо создать конфигурационный объект и передать его в конструктор Phaser.Game.

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

const game = new Phaser.Game(config);

Ключевые параметры конфига: - type: Phaser.AUTO позволяет Phaser самому выбрать рендерер (WebGL или Canvas). - width / height: размер игрового холста. - backgroundColor: цвет фона (черный). - parent: ID HTML-элемента, в который будет вставлен canvas. Если элемента с таким ID нет, canvas будет добавлен в тело документа. - scene: класс нашей сцены, которая будет запущена сразу после инициализации игры.

После создания экземпляра game Phaser автоматически вызовет методы жизненного цикла сцены (preload, create, update).

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

Группы в Phaser — это фундаментальный инструмент для организации множества однотипных игровых объектов. Они не только упрощают создание и расстановку, но и открывают доступ к мощным массовым операциям через Phaser.Actions. Для экспериментов попробуйте: изменить параметры repeat и stepX для создания более плотных или разреженных формаций; использовать Phaser.Actions.Call для вызова пользовательской функции у каждого элемента группы; добавить группе физическое тело для коллективного управления столкновениями и движением.