О чем этот пример
В 2D-играх объекты часто перекрывают друг друга. Управление порядком отрисовки — ключ к реалистичной графике. Метод `depthSort()` из списка детей сцены (`this.children`) позволяет быстро отсортировать массив игровых объектов по их глубине (значению `depth`) от нижнего к верхнему. Это особенно полезно для определения, какой объект находится «сверху» в заданной точке, например, при клике мышью или для логики взаимодействий.
Версия 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.atlas('atlas', 'assets/atlas/megaset-3.png', 'assets/atlas/megaset-3.json');
}
create ()
{
// Create a bunch of images and store some of them in a local array
// Works even with setDepth() added
const image1 = this.add.image(100, 300, 'atlas', 'contra2');
const image2 = this.add.image(200, 300, 'atlas', 'contra3');
const image3 = this.add.image(300, 300, 'atlas', 'exocet_spaceman');
const image4 = this.add.image(400, 300, 'atlas', 'helix');
const image5 = this.add.image(500, 300, 'atlas', 'pacman_by_oz_28x28');
const image6 = this.add.image(600, 300, 'atlas', 'profil-sad-plush');
const test1 = [ image1, image2, image4 ];
// contra2 -> contra3 -> helix
this.dump(test1);
const test2 = [ image6, image4, image2 ];
// profil-sad-plush -> helix -> contra3
this.dump(test2);
const test3 = [ image6, image4, image2 ];
// contra3 -> helix -> profil-sad-plush
this.children.depthSort(test3);
this.dump(test3);
const test4 = [ image3, image1, image2, image1, image1, image6, image4 ];
this.children.depthSort(test4);
// contra2 -> contra2 -> contra2 -> contra3 -> exocet_spaceman -> helix -> profil-sad-plush
this.dump(test4);
}
dump (arr)
{
let s = '';
arr.forEach(function(e, i) {
s = s.concat(e.frame.name);
if (i < arr.length - 1)
{
s = s.concat(' -> ');
}
});
console.log(s);
}
}
const config = {
type: Phaser.WEBGL,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Проблема: кто сверху?
Когда несколько спрайтов занимают одну и ту же область экрана, возникает вопрос: какой из них считать видимым «сверху»? Phaser управляет порядком отрисовки через свойство depth у каждого игрового объекта. Чем выше значение depth, тем «ближе» объект к камере, и тем позже он будет нарисован (перекрывая другие).
Однако, если у вас есть массив объектов (например, собранных по определенным координатам), их порядок в массиве может не соответствовать реальному порядку отрисовки. Метод depthSort() решает эту задачу.
Как работает `depthSort()`
Метод this.children.depthSort() принимает массив игровых объектов и сортирует его **на месте** (in-place) по возрастанию их свойства depth. После сортировки первый элемент массива будет иметь наименьшую глубину (будет «нижним»), а последний — наибольшую (будет «верхним»).
const group = [spriteA, spriteB, spriteC];
this.children.depthSort(group);
// Теперь group[0] — самый нижний, group[2] — самый верхний.
Важно: метод изменяет исходный массив. Исходный порядок элементов теряется.
Разбор примера кода
В примере создается шесть изображений (image1...image6). По умолчанию их depth равен 0, и порядок отрисовки соответствует порядку добавления на сцену (последний добавленный — сверху).
Сначала мы видим базовые тесты. Массив test1 создается вручную, и его дамп показывает порядок, в котором объекты были добавлены в массив. Это не обязательно порядок по глубине.
const test1 = [ image1, image2, image4 ];
// дамп: contra2 -> contra3 -> helix
Затем массив test3 демонстрирует работу depthSort. До сортировки порядок в массиве: image6, image4, image2. После вызова метода порядок меняется согласно depth объектов.
const test3 = [ image6, image4, image2 ];
// До сортировки: profil-sad-plush -> helix -> contra3
this.children.depthSort(test3);
// После сортировки: contra3 -> helix -> profil-sad-plush
Поскольку все объекты имеют depth = 0, сортировка, по сути, возвращает их в порядок добавления на сцену (от первого к последнему). Чтобы увидеть реальный эффект, нужно менять depth через setDepth().
Практическое применение: выбор верхнего объекта
Основное применение — определение, с каким объектом взаимодействует игрок. Например, при клике мыши вы собираете все объекты под курсором в массив, а затем с помощью depthSort() находите тот, что отрисовывается последним (верхний элемент массива).
// Псевдокод: находим объекты под указателем
const objectsUnderPointer = getObjectsAtPoint(pointer.x, pointer.y);
// Сортируем по глубине
this.children.depthSort(objectsUnderPointer);
// Верхний объект — последний в массиве
const topObject = objectsUnderPointer[objectsUnderPointer.length - 1];
// Взаимодействуем с topObject
Этот подход критически важен для инвентаря, карт, игр с изометрией или любыми сложными слоями графики.
Особенности и ограничения
1. **Сортировка на месте:** Исходный массив изменяется. Если исходный порядок нужно сохранить, передавайте копию массива.
const sortedArray = [...originalArray];
this.children.depthSort(sortedArray);
2. **Работает только с игровыми объектами:** Метод ожидает массив объектов, являющихся потомками this.children. Передача других данных вызовет ошибку.
3. **Зависит от depth:** Эффективность метода раскрывается при явном управлении глубиной. Без изменения depth сортировка может давать неочевидные результаты.
image3.setDepth(5); // Этот объект будет всегда сверху
image1.setDepth(-2); // Этот объект будет всегда снизу
4. **Повторяющиеся объекты:** Как видно в test4, метод корректно обрабатывает дубликаты одного и того же объекта в массиве.
Что попробовать дальше
Метод children.depthSort() — это мощный и эффективный инструмент для работы с порядком отрисовки в Phaser. Он незаменим для логики выбора объектов, управления UI-слоями и создания сложных сцен.
**Идеи для экспериментов:**
1. Создайте сцену с накладывающимися друг на друга карточками. Реализуйте перетаскивание, всегда выбирая верхнюю карточку под курсором.
2. Сымитируйте изометрическую проекцию, назначая объектам depth в зависимости от их Y-координаты, и используйте depthSort для корректного отображения.
3. Постройте систему контекстного меню, которая появляется при клике правой кнопкой мыши на самом верхнем объекте в группе.
