О чем этот пример
Один из ключевых вопросов при работе с Phaser 3 — как эффективно управлять множеством игровых объектов. Встроенный Display List отлично справляется с базовыми задачами, но для сложной организации элементов нужны слои (Layers). В этой статье разберем практический пример переноса спрайтов между основным списком отображения и отдельным слоем, что позволит вам гибко управлять группами объектов, их видимостью и порядком отрисовки.
Версия 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('coffee', 'assets/sprites/coffee.png');
this.load.image('donut', 'assets/sprites/donut.png');
}
create ()
{
this.add.text(10, 10, 'Click Sprite to move between Layer');
const size1 = this.add.text(10, 48);
const size2 = this.add.text(410, 48);
this.add.line(400, 300, 0, 0, 0, 600, 0xffffff);
const layer = this.add.layer();
const clickSprite = function ()
{
if (this.displayList === layer)
{
// By calling addToDisplayList it will automatically remove
// it from the Layer and add it back onto the Display List.
this.addToDisplayList();
// If you want to remove it from the Layer and NOT add it
// to the Display List, then call layer.remove(this)
this.x -= 400;
}
else
{
// Move from Display List to Layer
// Doing this will automatically remove it from the Display List
layer.add(this);
this.x += 400;
}
size1.setText('Display List size: ' + this.scene.children.length);
size2.setText('Layer size: ' + layer.length);
}
for (let i = 0; i < 8; i++)
{
const x1 = Phaser.Math.Between(64, 336);
const y1 = Phaser.Math.Between(128, 536);
const x2 = Phaser.Math.Between(464, 736);
const y2 = Phaser.Math.Between(128, 536);
const sprite1 = new Phaser.GameObjects.Sprite(this, x1, y1, 'coffee');
const sprite2 = new Phaser.GameObjects.Sprite(this, x2, y2, 'donut');
sprite1.addToDisplayList();
layer.add(sprite2);
sprite1.setInteractive();
sprite2.setInteractive();
sprite1.on('pointerdown', clickSprite);
sprite2.on('pointerdown', clickSprite);
}
size1.setText('Display List size: ' + this.children.length);
size2.setText('Layer size: ' + layer.length);
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Что такое Display List и Layer?
Каждая сцена Phaser 3 содержит главный контейнер для отображения объектов — Display List. Все добавленные через this.add или метод addToDisplayList() объекты попадают в него. Layer — это особый тип контейнера, который также наследуется от GameObject и может содержать в себе другие объекты. Слой можно воспринимать как отдельную группу, которую можно трансформировать, скрывать или отрисовывать поверх других элементов целиком.
Ключевое отличие: объект может находиться только в одном родительском контейнере одновременно — либо в основном списке сцены, либо в слое.
Разбор примера: создание сцены и объектов
В методе preload загружаются два изображения для спрайтов. В create создается текст-инструкция, два текстовых поля для вывода количества объектов и вертикальная разделительная линия.
Создается слой и объявляется функция-обработчик клика clickSprite. Затем в цикле генерируются 8 пар спрайтов со случайными координатами.
const layer = this.add.layer();
const clickSprite = function ()
{
// Логика перемещения...
}
Особенность создания объектов: спрайты создаются через конструктор Phaser.GameObjects.Sprite, а не через фабрику this.add.sprite. Это важно, потому что изначально они не добавлены ни в один список отображения.
const sprite1 = new Phaser.GameObjects.Sprite(this, x1, y1, 'coffee');
const sprite2 = new Phaser.GameObjects.Sprite(this, x2, y2, 'donut');
Первый спрайт добавляется в основной Display List, второй — в созданный слой.
sprite1.addToDisplayList();
layer.add(sprite2);
Логика перемещения объектов между контейнерами
Функция clickSprite вызывается при клике на любой спрайт. Она проверяет, в каком контейнере (displayList) находится объект в данный момент.
Если объект находится в слое (this.displayList === layer), то вызов this.addToDisplayList() автоматически удалит его из слоя и поместит в основной список сцены. Объект также смещается по оси X, чтобы визуально оказаться в левой части экрана.
if (this.displayList === layer)
{
this.addToDisplayList();
this.x -= 400;
}
Если объект находится в основном Display List, то метод layer.add(this) автоматически удалит его оттуда и добавит в слой, после чего объект сместится вправо.
else
{
layer.add(this);
this.x += 400;
}
После каждого перемещения обновляется текст, отображающий количество объектов в каждом контейнере, через свойства this.scene.children.length и layer.length.
Важные нюансы работы с Layers
1. **Автоматическое удаление:** Методы addToDisplayList() и layer.add() сами заботятся об удалении объекта из предыдущего родительского контейнера. Вручную это делать не нужно.
2. **Альтернативное удаление из слоя:** Если нужно просто удалить объект из слоя, не добавляя его обратно в основной список, используйте layer.remove(this). Объект станет невидимым, пока снова не будет куда-либо добавлен.
3. **Свойство displayList:** Каждый GameObject имеет свойство displayList, которое ссылается на его текущий родительский контейнер. Это может быть либо this.scene.children (основной список), либо объект Layer.
4. **Интерактивность:** Обратите внимание, что интерактивность (setInteractive) назначается каждому спрайту индивидуально. Она сохраняется при перемещении между контейнерами.
Что попробовать дальше
Использование слоев — мощный инструмент для структурирования сложных сцен. Вы можете создавать отдельные слои для фона, игровых объектов, интерфейса и эффектов, управляя их видимостью и глубиной. Для экспериментов попробуйте: создать несколько слоев с разными свойствами (alpha, visible), реализовать перетаскивание объектов между слоями или динамически менять порядок отрисовки (depth) объектов внутри слоя и между слоями.
