О чем этот пример
Визуальная целостность игры часто зависит от корректного порядка отрисовки объектов. По умолчанию Phaser добавляет объекты на сцену один за другим, но что делать, когда нужно динамически менять их взаимное расположение? В этой статье мы разберем, как работает система глубины (depth) в Phaser 3, которая является аналогом z-index в веб-разработке. Вы научитесь контролировать, какие спрайты будут перекрывать другие, что критически важно для создания многослойных интерфейсов, изометрических проекций или просто корректного отображения игровых элементов.
Версия 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('ayu', 'assets/pics/ayu2.png');
}
create ()
{
// When creating images they are added in display order.
// The first one created appears at the back of the display list, and so on.
// By default that have a depth value of 0 which means "unsorted"
// Click to change the depth of image3 to 1, raising it higher than the others.
const image1 = this.add.image(100, 300, 'ayu');
const image2 = this.add.image(200, 300, 'ayu');
const image3 = this.add.image(300, 300, 'ayu');
const image4 = this.add.image(400, 300, 'ayu');
const image5 = this.add.image(500, 300, 'ayu');
const image6 = this.add.image(600, 300, 'ayu');
const image7 = this.add.image(700, 300, 'ayu');
this.input.on(Phaser.Input.Events.POINTER_DOWN, function (pointer) {
image3.setDepth(1);
}, this);
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Проблема порядка по умолчанию
Когда вы создаете игровые объекты, такие как изображения, с помощью методов вроде this.add.image(), Phaser размещает их в так называемом "списке отображения" (display list). Ключевое правило по умолчанию: порядок создания определяет порядок отрисовки.
Первый созданный объект оказывается внизу стопки, а последний — сверху, перекрывая предыдущие. В исходном коде примера это наглядно показано: изображения создаются от image1 до image7. Поскольку image7 создана последней, она будет поверх всех остальных, если их координаты пересекаются.
Однако вся семерка изображений изначально имеет значение глубины, равное 0. Это специальное значение, которое Phaser интерпретирует как "не отсортировано". В таком состоянии движок полагается исключительно на порядок в списке отображения.
Свойство `depth` — ваш инструмент для сортировки
Чтобы взять управление порядком отрисовки в свои руки, у каждого игрового объекта в Phaser есть свойство .depth. Это числовое значение, по которому движок сортирует объекты перед отрисовкой на кадре.
Принцип прост: объект с большим значением depth будет отрисован поверх объекта с меньшим значением. Это полностью переопределяет порядок, заданный при создании.
В нашем примере все изображения имеют depth: 0. Внутри обработчика клика мы меняем глубину только одного объекта — image3.
this.input.on(Phaser.Input.Events.POINTER_DOWN, function (pointer) {
image3.setDepth(1);
}, this);
Вызов метода image3.setDepth(1) присваивает третьему изображению глубину, равную 1. Поскольку у всех остальных изображений глубина остается 0, image3 мгновенно "всплывет" на самый верх стопки и будет отрисован последним, перекрывая все остальные изображения, включая image7.
Практические сценарии и важные нюансы
Использование depth выходит за рамки простого примера с кликом. Вот типичные случаи применения:
* **Динамический UI:** Когда всплывающее окно или меню должно быть поверх всего игрового мира.
* **Эффекты:** Частицы взрыва или магические эффекты поверх персонажей, но под элементами интерфейса.
* **Изометрия и параллакс:** Корректное отображение слоев окружения, где дальние объекты должны быть за ближними.
Важно помнить о двух вещах:
1. Сортировка по depth происходит каждый кадр для всех объектов, у которых это значение отличается от 0. Объекты с depth: 0 не сортируются между собой и отрисовываются в порядке списка отображения.
2. Метод .setDepth() не только меняет числовое значение, но и автоматически помечает сцену как требующую сортировки.
// Пример: создание окна поверх всех объектов
const gameWorldLayer = this.add.layer();
const uiLayer = this.add.layer();
// Все игровые объекты добавляются в gameWorldLayer
// Все элементы интерфейса добавляются в uiLayer
// Устанавливаем слой UI поверх игрового мира
uiLayer.setDepth(1000);
Работа со слоями (Phaser.GameObjects.Layer) — это продвинутый способ группового управления глубиной для множества объектов.
Что попробовать дальше
Система глубины в Phaser 3 — это простой и мощный механизм для управления визуальной иерархией объектов. Используя свойство depth, вы получаете полный контроль над порядком отрисовки, что необходимо для создания сложных, динамичных и визуально целостных сцен.
**Идеи для экспериментов:**
1. Создайте несколько спрайтов и меняйте их глубину в реальном времени в ответ на игровые события (подбор предмета, получение урона).
2. Реализуйте простую систему слоев (фон, игрок, враги, эффекты, UI), назначив каждой группе свой диапазон значений глубины (например, 0, 10, 20, 30, 100).
3. Поэкспериментируйте с отрицательными значениями depth. Они также полностью поддерживаются.
