О чем этот пример
Управление отображением множества объектов — ключевая задача в разработке игр. Phaser предлагает мощный инструмент — `Container` (контейнер), который позволяет группировать объекты и управлять ими как единым целым. В этой статье мы разберем, как динамически перемещать спрайты между контейнерами, что особенно полезно для реализации инвентаря, меню перетаскивания или логики уровней, где объекты могут менять свою принадлежность.
Версия 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('cake', 'assets/sprites/cake.png');
}
create ()
{
this.add.text(10, 10, 'Click Sprite to move between Containers');
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 container1 = this.add.container();
const container2 = this.add.container();
for (let i = 0; i < 8; i++)
{
const x = Phaser.Math.Between(64, 336);
const y = Phaser.Math.Between(128, 536);
const sprite = new Phaser.GameObjects.Sprite(this, x, y, 'cake');
container1.add(sprite);
sprite.setInteractive();
sprite.on('pointerdown', () => {
if (sprite.parentContainer === container1)
{
container2.add(sprite);
sprite.x += 400;
}
else
{
container1.add(sprite);
sprite.x -= 400;
}
size1.setText('Container 1 size: ' + container1.length);
size2.setText('Container 2 size: ' + container2.length);
});
}
size1.setText('Container 1 size: ' + container1.length);
size2.setText('Container 2 size: ' + container2.length);
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Что такое контейнер и зачем он нужен
Контейнер (Container) в Phaser — это специальный игровой объект, который может содержать в себе другие объекты (спрайты, текст, графику). Все дочерние объекты наследуют трансформации (позицию, масштаб, поворот) от родительского контейнера. Это позволяет:
- Группировать логически связанные объекты.
- Управлять их видимостью и активностью одновременно.
- Эффективно организовывать сцену.
В примере создаются два контейнера, и спрайты с изображением торта могут переключаться между ними по клику.
Создание контейнеров и добавление спрайтов
Инициализация происходит в методе create() сцены. Сначала создаются два пустых контейнера и разделительная линия для наглядности. Затем в цикле генерируются спрайты с случайными координатами и добавляются в первый контейнер.
const container1 = this.add.container();
const container2 = this.add.container();
for (let i = 0; i < 8; i++)
{
const x = Phaser.Math.Between(64, 336);
const y = Phaser.Math.Between(128, 536);
const sprite = new Phaser.GameObjects.Sprite(this, x, y, 'cake');
container1.add(sprite);
}
Каждый спрайт создается через new Phaser.GameObjects.Sprite. Важно: изначально спрайт не находится в списке отображения сцены. Он попадает в него только после добавления в контейнер (который сам является дочерним объектом сцены). Метод container1.add(sprite) делает спрайт видимым.
Взаимодействие и логика перемещения
Чтобы спрайт реагировал на клик, ему нужно назначить обработчик событий. Для этого используется setInteractive(). Далее на событие pointerdown вешается функция, которая проверяет текущий родительский контейнер спрайта и перемещает его в другой.
sprite.setInteractive();
sprite.on('pointerdown', () => {
if (sprite.parentContainer === container1)
{
container2.add(sprite);
sprite.x += 400;
}
else
{
container1.add(sprite);
sprite.x -= 400;
}
});
Ключевой момент: свойство parentContainer хранит ссылку на контейнер, в котором в данный момент находится объект. Метод container2.add(sprite) автоматически удаляет спрайт из предыдущего контейнера и добавляет в новый. Смещение координаты sprite.x на 400 пикселей компенсирует визуальное расположение контейнеров по разные стороны от разделительной линии.
Отслеживание состояния контейнеров
Контейнер имеет свойство length, которое показывает количество содержащихся в нем объектов. В примере это используется для отображения текстовых счетчиков, обновляющихся после каждого перемещения.
size1.setText('Container 1 size: ' + container1.length);
size2.setText('Container 2 size: ' + container2.length);
Такой подход полезен для отладки и создания UI, отображающего, например, количество предметов в инвентаре игрока или на игровом поле.
Что попробовать дальше
Использование контейнеров и динамическое перемещение объектов между ними — это фундаментальный паттерн в Phaser для организации сложных сцен. Вы можете экспериментировать: добавить перетаскивание (Drag & Drop), анимацию перемещения, вложенные контейнеры или реализовать логику, где объекты меняют свое поведение в зависимости от текущего контейнера (например, в инвентаре предмет неактивен, а на поле боя — атакует).
