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

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

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

Живой запуск

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

Исходный код


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

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('einstein', 'assets/pics/ra-einstein.png');
    }

    create ()
    {
        this.image = this.add.image(100, 70, 'einstein');

        //  We're going to create 32 cameras in a 8x4 grid, making each 100x150 in size
        this.cameras.main.setSize(100, 150);

        for (let y = 0; y < 4; y++)
        {
            for (let x = 0; x < 8; x++)
            {
                if (x === 0 && y === 0)
                {
                    continue;
                }
                this.cameras.add(x * 100, y * 150, 100, 150);
            }
        }
    }

    update ()
    {
        this.image.rotation += 0.01;
    }
}

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

const game = new Phaser.Game(config);

Инициализация сцены и загрузка ассета

Класс сцены Example наследуется от Phaser.Scene. В методе preload мы загружаем единственное изображение, используя this.load.setBaseURL для указания базового пути и this.load.image для загрузки файла.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('einstein', 'assets/pics/ra-einstein.png');
}

Это стандартная процедура подготовки ресурсов для сцены. Изображение будет доступно под ключом 'einstein'.

Создание основного объекта и настройка главной камеры

В методе create мы сначала добавляем изображение в сцену с помощью this.add.image. Оно будет нашим центральным объектом, за которым наблюдают все камеры.

this.image = this.add.image(100, 70, 'einstein');

Затем происходит ключевое действие: мы изменяем размер основной камеры (this.cameras.main). По умолчанию камера занимает весь холст игры (800x600 пикселей из конфига). С помощью метода setSize мы ограничиваем её область просмотра.

this.cameras.main.setSize(100, 150);

Теперь основная камера будет отображать только квадрат 100x150 пикселей в левом верхнем углу игрового мира. Важно: это не изменяет размер игрового мира, а лишь область, которую эта конкретная камера проецирует на холст.

Генерация сетки дополнительных камер

Следующий шаг — создание остальных 31 камеры с помощью двух вложенных циклов for. Цель — расположить их в виде сетки 8x4.

for (let y = 0; y < 4; y++)
{
    for (let x = 0; x < 8; x++)
    {
        if (x === 0 && y === 0)
        {
            continue;
        }
        this.cameras.add(x * 100, y * 150, 100, 150);
    }
}

Условие if (x === 0 && y === 0) пропускает создание камеры для первой ячейки сетки, так как эта позиция уже занята главной камерой, которую мы настроили ранее. Метод this.cameras.add создает новую камеру. Его параметры: 1. x * 100: X-координата *всех* камер внутри игрового мира. 2. y * 150: Y-координата *всех* камер внутри игрового мира. 3. 100: Ширина области просмотра камеры. 4. 150: Высота области просмотра камеры.

Все созданные камеры автоматически "смотрят" на одну и ту же область игрового мира, но каждая проецирует свой маленький фрагмент (100x150) на свою уникальную позицию на общем холсте игры. Так формируется сетка.

Анимация объекта и настройка игры

В методе update, который вызывается каждый кадр, мы вращаем наше изображение. Это наглядно демонстрирует, что все камеры наблюдают за одним и тем же динамичным объектом.

update ()
{
    this.image.rotation += 0.01;
}

Конфигурация игры задает стандартный холст размером 800x600 пикселей, что идеально вмещает нашу сетку из 8 камер по ширине (8 * 100 = 800) и 4 по высоте (4 * 150 = 600).

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

const game = new Phaser.Game(config);

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

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

  1. Применить к разным камерам разные визуальные эффекты через camera.setPostPipeline
  2. Заставить камеры следовать за разными игровыми объектами с помощью camera.startFollow
  3. Динамически менять размер или положение камер в реальном времени для создания "пинч-зума" или переключения видов