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

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

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

Живой запуск

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

Исходный код


class SceneC extends Phaser.Scene {

    constructor ()
    {
        super('SceneC');

        this.asteroids = [];

        this.positions = [
            { x: 37, y: 176 },
            { x: 187, y: 66 },
            { x: 177, y: 406 },
            { x: 317, y: 256 },
            { x: 417, y: -10 },
            { x: 487, y: 336 },
            { x: 510, y: 116 },
            { x: 727, y: 186 },
            { x: 697, y: 10 },
            { x: 597, y: 216 },
            { x: 695, y: 366 },
            { x: 900, y: 76 },
            { x: 1008, y: 315 }
        ];
    }

    create ()
    {
        this.cameras.main.setViewport(0, 136, 1024, 465);

        for (let i = 0; i < this.positions.length; i++)
        {
            let pos = this.positions[i];

            let therock = this.add.sprite(pos.x, pos.y, 'asteroid').play('asteroid');

            therock.setData('vx', 0.04);
            therock.setOrigin(0);
            therock.setScale(Phaser.Math.FloatBetween(0.3, 0.6));

            this.asteroids.push(therock);
        }
    }

    update (time, delta)
    {
        for (let i = 0; i < this.asteroids.length; i++)
        {
            let therock = this.asteroids[i];

            therock.x -= therock.getData('vx') * delta;

            if (therock.x <= -100)
            {
                therock.x = 1224;
            }
        }
    }

}

Структура класса сцены и предопределенные позиции

Класс SceneC расширяет базовый класс Phaser.Scene. В конструкторе мы задаем ключ сцены для системы менеджера сцен Phaser и готовим структуры данных для работы.

Массив this.asteroids будет хранить все созданные спрайты астероидов для дальнейшего обновления.

Массив this.positions содержит набор статических координат `xиy`. Эти координаты являются точками, где будут первоначально размещены астероиды. Использование предопределенного массива позволяет точно контролировать начальное расположение объектов, что удобно для создания сложных, художественно выверенных композиций, вместо случайного размещения.

class SceneC extends Phaser.Scene {
    constructor () {
        super('SceneC');
        this.asteroids = [];
        this.positions = [
            { x: 37, y: 176 }, { x: 187, y: 66 }, { x: 177, y: 406 },
            // ... остальные позиции
        ];
    }
}

Создание и конфигурация спрайтов в методе `create`

Метод create вызывается один раз при инициализации сцены. Здесь происходит настройка камеры и создание игровых объектов.

Сначала мы устанавливаем область просмотра (viewport) для основной камеры сцены. Это определяет, какая часть игрового мира будет отображаться этой конкретной камерой на экране.

this.cameras.main.setViewport(0, 136, 1024, 465);

Затем в цикле по массиву позиций создаются спрайты астероидов. Для каждого объекта используется текстура 'asteroid' и запускается анимация с ключом 'asteroid'. Это предполагает, что текстура и анимация были предварительно загружены.

let therock = this.add.sprite(pos.x, pos.y, 'asteroid').play('asteroid');

После создания спрайта мы задаем ему индивидуальную скорость движения, используя метод setData. Это удобный способ хранить пользовательские данные прямо на объекте игры. Скорость (vx) задается как небольшое положительное число.

therock.setData('vx', 0.04);

Мы также устанавливаем точку привязки (origin) в (0,0) – верхний левый угол спрайта – и задаем случайный масштаб для разнообразия.

therock.setOrigin(0);
therock.setScale(Phaser.Math.FloatBetween(0.3, 0.6));

Каждый созданный спрайт добавляется в массив this.asteroids для последующего управления.

this.asteroids.push(therock);

Обновление положения объектов в методе `update`

Метод update вызывается каждый кадр игры. Здесь реализуется логика движения астероидов и их "перерождения" для создания непрерывного потока.

В цикле по массиву this.asteroids для каждого спрайта мы получаем его индивидуальную скорость из данных объекта и вычисляем новую позицию по оси X. Умножение скорости на delta (время, прошедшее с предыдущего кадра) делает движение плавным и независимым от частоты кадров.

therock.x -= therock.getData('vx') * delta;

Далее проверяется условие: если спрайт полностью покинул видимую область слева (его `x` меньше или равен -100), он перемещается в правую часть за пределами текущего вьюпорта камеры (координата x = 1224). Это создает эффект бесконечного циклического движения объектов.

if (therock.x <= -100) {
    therock.x = 1224;
}

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

Ключевые API Phaser, использованные в примере

В этом примере используются несколько важных методов API Phaser 3:

- `this.add.sprite()`: создает новый спрайт в сцене.
- `.play()`: запускает анимацию на спрайте.
- `setData()` / `getData()`: устанавливают и получают пользовательские данные, связанные с игровым объектом.
- `setOrigin()`: задает точку привязки (origin) объекта.
- `setScale()`: изменяет масштаб объекта.
- `Phaser.Math.FloatBetween()`: генерирует случайное число в заданном диапазоне.
- `this.cameras.main.setViewport()`: устанавливает область просмотра для основной камеры.

Все эти методы являются частью публичного API Phaser и подробно описаны в официальной документации.

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

Пример демонстрирует чистый и эффективный подход к управлению движением множества спрайтов без использования физического движка. Это идеально для создания параллакс-эффектов, атмосферного фона или частичных систем. Для экспериментов попробуйте: изменять скорость (vx) на положительную для движения вправо; использовать Phaser.Math.FloatBetween для задания случайной скорости каждому объекту; добавить движение по оси Y для более сложных траекторий; или реализовать более сложную логику "перерождения", например, случайное размещение по высоте при возвращении объекта.