О чем этот пример
В Phaser 3 сцены могут работать одновременно, и каждая имеет собственный менеджер ввода. Это открывает возможность создавать сложные интерфейсы с независимыми интерактивными элементами в разных слоях игры. Данная статья на практическом примере покажет, как реализовать перетаскивание спрайтов в двух параллельно активных сценах, управляя их состоянием и внешним видом через события drag.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Boot extends Phaser.Scene
{
constructor ()
{
super({ key: 'boot', active: true });
}
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('atari1', 'assets/sprites/atari400.png');
this.load.image('atari2', 'assets/sprites/atari1200xl.png');
}
create ()
{
this.scene.start('sceneA');
this.scene.launch('sceneB');
}
}
class SceneA extends Phaser.Scene
{
constructor ()
{
super({ key: 'sceneA' });
}
create ()
{
const image = this.add.sprite(200, 300, 'atari1').setInteractive();
this.input.setDraggable(image);
image.on('dragstart', function (pointer)
{
this.setTint(0xff0000);
});
image.on('drag', function (pointer, dragX, dragY)
{
this.x = dragX;
this.y = dragY;
});
image.on('dragend', function (pointer)
{
this.clearTint();
});
}
}
class SceneB extends Phaser.Scene
{
constructor ()
{
super({ key: 'sceneB' });
}
create ()
{
const image = this.add.sprite(600, 300, 'atari2').setInteractive();
this.input.setDraggable(image);
image.on('dragstart', function (pointer)
{
this.setTint(0xff0000);
});
image.on('drag', function (pointer, dragX, dragY)
{
this.x = dragX;
this.y = dragY;
});
image.on('dragend', function (pointer)
{
this.clearTint();
});
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
scene: [ Boot, SceneA, SceneB ]
};
const game = new Phaser.Game(config);
Архитектура примера: загрузочная сцена и две параллельные
Код структурирован с использованием трех сцен. Сцена Boot служит для загрузки ресурсов и запуска основной логики. Она помечена как активная (active: true) и загружает два изображения спрайтов.
После загрузки в методе create она запускает сцену SceneA с помощью this.scene.start() и параллельно запускает сцену SceneB через this.scene.launch(). Важно понимать разницу: start останавливает предыдущую сцену (в данном случае Boot) и запускает новую, а launch добавляет сцену поверх существующих, позволяя им работать одновременно.
class Boot extends Phaser.Scene {
create () {
this.scene.start('sceneA');
this.scene.launch('sceneB');
}
}
Настройка перетаскивания в первой сцене (SceneA)
В сцене SceneA в методе create создается спрайт и сразу же делается интерактивным с помощью .setInteractive(). Без этого вызова объект не сможет генерировать события ввода, включая перетаскивание.
Ключевой метод this.input.setDraggable(image) регистрирует этот конкретный спрайт как элемент, который можно перетаскивать. Теперь система ввода сцены будет отслеживать для него события drag.
// Внутри SceneA.create()
const image = this.add.sprite(200, 300, 'atari1').setInteractive();
this.input.setDraggable(image);
Далее на объект навешиваются обработчики событий. Обратите внимание, что в функции-обработчике контекст (this) указывает на сам спрайт, что позволяет напрямую вызывать его методы, такие как setTint.
- dragstart: Срабатывает в момент начала перетаскивания (кнопка мыши зажата на объекте). Спрайт окрашивается в красный цвет.
- drag: Срабатывает постоянно при движении мыши с зажатой кнопкой. Параметры dragX и dragY содержат новые координаты указателя в мировом пространстве, которые присваиваются спрайту.
- dragend: Срабатывает при отпускании кнопки мыши. Красный оттенок убирается.
image.on('dragstart', function (pointer) {
this.setTint(0xff0000);
});
image.on('drag', function (pointer, dragX, dragY) {
this.x = dragX;
this.y = dragY;
});
image.on('dragend', function (pointer) {
this.clearTint();
});
Независимое перетаскивание во второй сцене (SceneB)
Сцена SceneB является полным аналогом SceneA, но работает независимо. Она создает свой спрайт с другим изображением в другой позиции (600x300). Важно понимать, что this.input в этом коде относится к менеджеру ввода сцены SceneB. Это два отдельных менеджера, которые не конфликтуют друг с другом.
// Внутри SceneB.create()
const image = this.add.sprite(600, 300, 'atari2').setInteractive();
this.input.setDraggable(image);
// ... обработчики событий dragstart, drag, dragend
Благодаря такой архитектуре вы можете перетаскивать оба спрайта одновременно, независимо друг от друга. Каждый из них реагирует только на ввод, предназначенный для его родной сцены. Это мощный паттерн для создания сложных UI, где разные элементы интерфейса или игровые объекты требуют изолированного поведения.
Конфигурация игры и запуск
Все сцены регистрируются в основном конфигурационном объекте игры в массиве scene. Порядок в этом массиве не определяет порядок запуска — им полностью управляет код в сцене Boot.
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
scene: [ Boot, SceneA, SceneB ] // Регистрация всех сцен
};
const game = new Phaser.Game(config);
После создания экземпляра игры Phaser автоматически запускает первую сцену из массива, у которой установлен флаг active: true. В нашем примере это Boot.
Что попробовать дальше
Пример наглядно демонстрирует работу изолированных систем ввода в параллельных сценах Phaser 3 — мощный инструмент для организации сложной игровой логики. Для экспериментов попробуйте: добавить физические тела спрайтам и связать перетаскивание с их скоростью, реализовать перетаскивание между сценами с помощью глобальных событий или создать сцену-интерфейс поверх игровой, элементы которой также можно перетаскивать, не влияя на геймплей.
