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

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

Версия 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/monika-krawinkel-amberstar-title.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);
        this.cameras.main.name = 'Cam0';
    
        let i = 1;
    
        for (let y = 0; y < 4; y++)
        {
            for (let x = 0; x < 8; x++)
            {
                if (x === 0 && y === 0)
                {
                    continue;
                }
    
                const tx = x * 100;
                const ty = y * 150;
    
                this.cameras.add(tx, ty, 100, 150, false, 'Cam' + i);
    
                i++;
    
            }
        }
    
        this.input.on(Phaser.Input.Events.POINTER_UP, function (pointer) {
    
            const x = Phaser.Math.Snap.Floor(pointer.x, 100);
            const y = Phaser.Math.Snap.Floor(pointer.y, 150);
    
            const total = this.cameras.remove(pointer.camera);
    
            if (total === 0)
            {
                const newCam = this.cameras.add(x, y, 100, 150);
                console.log('Added Camera ID', newCam.id);
            }
            else
            {
                console.log('Removed Camera ID', pointer.camera.id);
            }
    
        }, this);
    }

    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);

Создание сетки камер

В примере создается 32 камеры, расположенные в сетке 8x4. Основная камера (this.cameras.main) уменьшается до размера 100x150 пикселей и задает шаблон для остальных.

this.cameras.main.setSize(100, 150);
this.cameras.main.name = 'Cam0';

Затем в двойном цикле создаются дополнительные камеры с помощью метода this.cameras.add(). Каждая камера имеет свои координаты (tx, ty), размер 100x150 и уникальное имя.

for (let y = 0; y < 4; y++) {
    for (let x = 0; x < 8; x++) {
        if (x === 0 && y === 0) continue;
        const tx = x * 100;
        const ty = y * 150;
        this.cameras.add(tx, ty, 100, 150, false, 'Cam' + i);
        i++;
    }
}

Важно: первая ячейка сетки (x=0, y=0) пропускается, так как там уже находится основная камера. В результате мы получаем мозаику из камер, каждая из которых отображает одну и ту же вращающуюся картинку, но в своем прямоугольнике.

Обработка клика и удаление камеры

Логика динамического управления камерами привязана к событию клика (POINTER_UP). При клике определяется, по какой камере был произведен клик, и эта камера удаляется из системы.

this.input.on(Phaser.Input.Events.POINTER_UP, function (pointer) {
    const total = this.cameras.remove(pointer.camera);
    // ...
}, this);

Ключевой момент: объект pointer события содержит свойство pointer.camera — это ссылка на конкретную камеру, в области которой произошел клик. Метод this.cameras.remove() удаляет эту камеру из менеджера камер сцены и возвращает общее количество оставшихся камер. Если камер не осталось, в следующем разделе будет создана новая.

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

После удаления камеры проверяется, сколько их осталось. Если камер больше нет (total === 0), создается новая камера в координатах клика, приведенных к сетке.

const x = Phaser.Math.Snap.Floor(pointer.x, 100);
const y = Phaser.Math.Snap.Floor(pointer.y, 150);
if (total === 0) {
    const newCam = this.cameras.add(x, y, 100, 150);
    console.log('Added Camera ID', newCam.id);
}

Функции Phaser.Math.Snap.Floor() используются для привязки координат клика к шагу сетки (100 по X, 150 по Y). Это гарантирует, что новая камера встанет ровно в ячейку сетки, а не произвольно. Если камеры были удалены (т.е. total > 0), в консоль выводится ID удаленной камеры.

Вращение изображения как визуальный индикатор

Чтобы было наглядно видно, что все камеры отображают одну и ту же сцену, в методе update() происходит постоянное вращение изображения.

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

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

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

Пример демонстрирует гибкость системы камер Phaser: их можно создавать, компоновать в сетку и удалять в реальном времени. Это открывает возможности для нестандартных интерфейсов. Для экспериментов попробуйте

  1. Удалять камеры не по клику, а по таймеру, создавая "мигающий" эффект
  2. Менять свойства камеры (например, zoom или alpha) перед удалением для анимации исчезновения
  3. Сохранять массив удаленных камер и восстанавливать их в исходных позициях по нажатию клавиши