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

В 2D-играх спрайты часто перекрывают друг друга. Когда динамический объект должен оказаться поверх всех остальных, простого изменения координат Y недостаточно — нужно явно управлять порядком отрисовки. В этой статье мы разберем, как работает система глубины (depth) в Phaser и как использовать метод `bringToTop()` для контроля визуальной иерархии объектов. Этот подход полезен для создания карточных игр, инвентарей, интерфейсов и любых сцен, где объекты перемещаются между слоями.

Версия 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('block', 'assets/sprites/block.png');
    }

    create ()
    {
        //  Create a stack of blocks
        const group = this.make.group({ key: 'block', frameQuantity: 12 });
        Phaser.Actions.SetXY(group.getChildren(), 48, 500, 64, 0);

        this.input.on('pointerdown', function (pointer) {
            const child = this.children.getAt(0);
            child.y -= 32;

            this.children.bringToTop(child);

        }, this);
    }
}

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

const game = new Phaser.Game(config);

Проблема: статичный порядок отрисовки

По умолчанию Phaser отрисовывает объекты в том порядке, в котором они были добавлены в сцену. Это называется порядком отображения (display list). Если объекты статичны, этого достаточно. Но если объект должен "всплыть" наверх (например, при клике), простое изменение его координаты Y не изменит его позицию в списке отрисовки.

В примере создается вертикальная стопка из 12 блоков. Они добавлены в группу и позиционированы с помощью Phaser.Actions.SetXY. Изначально блоки отрисовываются в порядке добавления: первый блок — внизу, последний — наверху.

Решение: метод `children.bringToTop()`

Ключевой метод для управления порядком — bringToTop(child). Он перемещает указанный дочерний объект в конец списка отрисовки группы, гарантируя, что он будет нарисован поверх всех остальных.

В примере обработчик клика (pointerdown) делает две вещи: 1. Смещает самый нижний блок вверх на 32 пикселя по оси Y. 2. Вызывает bringToTop() для этого блока, чтобы он отобразился поверх остальных.

this.input.on('pointerdown', function (pointer) {
    const child = this.children.getAt(0);
    child.y -= 32;
    this.children.bringToTop(child);
}, this);

Обратите внимание: метод вызывается у this.children, так как в контексте обработчика this ссылается на саму группу (group).

Работа с группами и списком детей

Группа (Group) в Phaser — это контейнер для управления коллекцией игровых объектов. В примере группа создается через this.make.group(). Метод group.getChildren() возвращает массив всех дочерних объектов группы.

const group = this.make.group({ key: 'block', frameQuantity: 12 });
Phaser.Actions.SetXY(group.getChildren(), 48, 500, 64, 0);

Phaser.Actions.SetXY — это хелпер для позиционирования массива объектов. Параметры: начальный X (48), начальный Y (500), шаг по X (64), шаг по Y (0). Так создается вертикальная колонка блоков.

Доступ к конкретному ребенку осуществляется через this.children.getAt(index). Индекс 0 соответствует первому добавленному блоку — самому нижнему в начальной стопке.

Важные нюансы и альтернативы

Метод bringToTop() работает в контексте конкретной группы или списка детей сцены. Если у вас несколько независимых групп, перемещение объекта в одной группе не повлияет на порядок отрисовки между группами. Для управления порядком между разными контейнерами используйте свойство depth.

Более современный и гибкий подход — явное задание глубины через setDepth(value). Объекты с большим значением depth отрисовываются поверх объектов с меньшим значением. Это особенно полезно для сложных сцен с несколькими слоями.

// Альтернатива: использование свойства depth
child.setDepth(100);

Также помните, что bringToTop() изменяет внутренний массив children, что может повлиять на последующие обращения по индексу.

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

Метод bringToTop() — это простой и эффективный способ динамического управления видимым порядком объектов в пределах одной группы или списка отрисовки. Он идеально подходит для быстрых прототипов и ситуаций, где нужен эффект "поднятия" объекта наверх по клику или столкновению. **Идеи для экспериментов:** 1. Измените логику: поднимайте наверх не первый блок, а случайный (getAt(Phaser.Math.Between(0, 11))). 2. Реализуйте перетаскивание (drag) спрайта с автоматическим вызовом bringToTop() при начале переноса. 3. Создайте две независимые группы и попробуйте управлять порядком между ними через свойство depth. 4. Сымитируйте стопку карт: при клике на карту в середине колоды поднимите ее и все карты выше на новую позицию по Y, а затем вызовите bringToTop() только для кликнутой карты.