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

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

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

Живой запуск

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

Исходный код


var UIText2;

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 () 
    {
        const image = this.add.image(400, 300, 'einstein');

        this.UIText1 = this.add.text(0, 32, '0');
        this.UIText2 = this.add.text(0, 64, '0');
    
        this.cont = this.add.container();
    
        this.cont.add([this.UIText1, this.UIText2]);
    
        //  Add in a new camera, the same size and position as the main camera
        this.UICam = this.cameras.add(0, 0, 800, 600);
    
        //  The main camera will not render the container
        this.cameras.main.ignore(this.cont);
    
        //  The new UI Camera will not render the background image
        this.UICam.ignore(image);
    }

    update () 
    {
        this.UIText1.setText("Main camera rotation: " + this.cameras.main.rotation);
        this.UIText2.setText("Main camera zoom: " + this.cameras.main.zoom);
    
        //wobble the container
        this.cont.y = Math.sin(this.time.now / 100) * 10;
    
        this.cameras.main.setZoom(Math.abs(Math.sin(this.cameras.main.rotation)) * 0.5 + 1);
        this.cameras.main.rotation += 0.01;
    }
}


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

const game = new Phaser.Game(config);

Зачем нужно несколько камер?

Основная камера в Phaser (this.cameras.main) управляет тем, какую часть игрового мира видит игрок. Но что, если нужно поверх игрового мира отображать элементы интерфейса (здоровье, счет, меню), которые не должны вращаться или масштабироваться вместе с миром?

Решение — создать вторую камеру, которая будет рендерить только UI. Каждая камера в Phaser — это независимый вид на сцену. Объекты можно настраивать так, чтобы они отображались только в определенных камерах. Это и есть разделение на слои.

Настройка сцены и создание объектов

В примере сначала создается фоновая картинка и два текстовых поля. Текст должен стать частью пользовательского интерфейса.

const image = this.add.image(400, 300, 'einstein');

this.UIText1 = this.add.text(0, 32, '0');
this.UIText2 = this.add.text(0, 64, '0');

Затем текстовые элементы помещаются в контейнер. Контейнер (Phaser.GameObjects.Container) — это удобный способ группировки нескольких игровых объектов для совместного управления.

this.cont = this.add.container();
this.cont.add([this.UIText1, this.UIText2]);

Создание UI-камеры и настройка игнорирования

Ключевой момент — создание второй камеры. Она имеет те же размеры и положение, что и основная (полноэкранная).

this.UICam = this.cameras.add(0, 0, 800, 600);

Теперь нужно "научить" камеры игнорировать определенные объекты. Метод ignore объекта камеры исключает переданные игровые объекты из списка рендеринга для этой камеры.

// Основная камера не будет рендерить контейнер с текстом
this.cameras.main.ignore(this.cont);

// Новая UI-камера не будет рендерить фоновое изображение
this.UICam.ignore(image);

В результате фон (image) рендерится только в основной камере, а контейнер с текстом (this.cont) — только в UI-камере. Они стали независимыми слоями.

Анимация и независимое обновление слоев

В методе update происходит анимация, которая наглядно демонстрирует независимость слоев.

Значения вращения и зума основной камеры выводятся в текстовых полях. Сами поля (через контейнер) совершают колебательное движение по оси Y.

this.cont.y = Math.sin(this.time.now / 100) * 10;

Основная камера динамически меняет свой зум на основе текущего угла вращения.

this.cameras.main.setZoom(Math.abs(Math.sin(this.cameras.main.rotation)) * 0.5 + 1);
this.cameras.main.rotation += 0.01;

Главное наблюдение: текст, привязанный к UI-камере, колеблется вверх-вниз, но НЕ вращается и НЕ масштабируется вместе с безумным зумом и вращением основной камеры, которая влияет только на фон. Это и есть цель достигнута.

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

Использование нескольких камер с методом ignore — мощный паттерн в Phaser 3 для создания сложных многослойных сцен. Он идеально подходит для HUD, меню, эффектов постобработки или разделения кооперативных экранов. **Идеи для экспериментов:** 1. Добавьте больше UI-элементов (иконки, полоски здоровья) в контейнер и анимируйте их. 2. Создайте третью камеру специально для эффектов частиц и настройте игнорирование для других слоев. 3. Попробуйте перемещать UI-камеру независимо от основной, создавая эффект "дрожания" интерфейса при ударах.