О чем этот пример
В Phaser сцены — это мощный инструмент для организации игровой логики. Но что, если вам нужно управлять объектами в одной сцене, затем переключиться на другую и продолжить управление там же? Этот пример демонстрирует, как настраивать обработку клавиатуры для нескольких сцен, корректно управлять их состоянием (пауза, запуск, остановка) и передавать контекст между ними. Вы научитесь создавать сложные интерактивные состояния в вашей игре, не теряя контроль над вводом данных.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class SceneA extends Phaser.Scene {
constructor ()
{
super('sceneA');
this.hotdog;
}
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('pic', 'assets/pics/case.jpg');
this.load.image('hotdog', 'assets/sprites/hotdog.png');
}
create ()
{
this.add.image(400, 300, 'pic');
var hotdog = this.add.image(400, 300, 'hotdog');
this.add.text(10, 10, 'Scene A. Press arrows to move. Click to change Scene.', { font: '16px Courier', fill: '#00ff00' });
this.input.keyboard.addCapture('UP, DOWN, LEFT, RIGHT')
this.input.keyboard.on('keydown_UP', function (event) {
hotdog.y -= 4;
}, this);
this.input.keyboard.on('keydown_DOWN', function (event) {
hotdog.y += 4;
}, this);
this.input.keyboard.on('keydown_LEFT', function (event) {
console.log('A left');
hotdog.x -= 4;
}, this);
this.input.keyboard.on('keydown_RIGHT', function (event) {
console.log('A right');
hotdog.x += 4;
}, this);
this.input.on('pointerdown', function () {
console.log('down');
this.scene.pause();
this.scene.run('sceneB');
}, this);
this.hotdog = hotdog;
}
update ()
{
this.hotdog.rotation += 0.001;
}
}
class SceneB extends Phaser.Scene {
constructor ()
{
super('sceneB');
this.hotdog;
}
create ()
{
var graphics = this.add.graphics();
graphics.fillStyle(0x000000, 0.5);
graphics.fillRect(0, 0, 800, 600);
this.add.text(10, 30, 'Scene B. Press arrows to move. Space to change Scene.', { font: '16px Courier', fill: '#00ff00' });
var hotdog = this.add.image(400, 300, 'hotdog').setTint(0xff0000);
this.input.keyboard.addCapture('UP, DOWN, LEFT, RIGHT');
this.input.keyboard.on('keydown_UP', function (event) {
hotdog.y -= 4;
}, this);
this.input.keyboard.on('keydown_DOWN', function (event) {
hotdog.y += 4;
}, this);
this.input.keyboard.on('keydown_LEFT', function (event) {
console.log('B left');
hotdog.x -= 4;
}, this);
this.input.keyboard.on('keydown_RIGHT', function (event) {
console.log('B right');
hotdog.x += 4;
}, this);
this.input.keyboard.once('keydown_SPACE', function (event) {
this.scene.stop();
this.scene.resume('sceneA');
}, this);
this.hotdog = hotdog;
}
}
var config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
scene: [ SceneA, SceneB ]
};
var game = new Phaser.Game(config);
Инициализация сцен и загрузка ресурсов
В примере определены две сцены: SceneA и SceneB. Каждая наследуется от Phaser.Scene. В конструкторе задаётся уникальный ключ сцены, который используется для её идентификации при переключении.
В SceneA метод preload загружает два изображения: фоновую картинку и спрайт хот-дога. Обратите внимание, что SceneB не перезагружает ресурсы — она использует уже загруженную в памяти текстуру hotdog, что является стандартным поведением Phaser.
class SceneA extends Phaser.Scene {
constructor () {
super('sceneA'); // Уникальный ключ сцены
this.hotdog;
}
preload () {
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('pic', 'assets/pics/case.jpg');
this.load.image('hotdog', 'assets/sprites/hotdog.png');
}
Настройка ввода и захват клавиш в SceneA
В методе create сцены A создаётся фон, спрайт хот-дога и информационный текст. Ключевой момент — настройка обработки клавиатуры.
Метод this.input.keyboard.addCapture('UP, DOWN, LEFT, RIGHT') предотвращает прокрутку страницы браузера при нажатии стрелок, «захватывая» эти события для игры.
Затем для каждой клавиши стрелки назначается обработчик события keydown_*. Внутри этих обработчиков изменяются координаты спрайта hotdog. Контекст (this) передаётся третьим аргументом в on, чтобы внутри функции-колбэка this указывал на экземпляр сцены.
Также на клик мыши (pointerdown) назначено переключение сцен: текущая сцена ставится на паузу pause(), а сцена B запускается run().
create () {
this.add.image(400, 300, 'pic');
var hotdog = this.add.image(400, 300, 'hotdog');
this.add.text(10, 10, 'Scene A. Press arrows to move. Click to change Scene.', { font: '16px Courier', fill: '#00ff00' });
this.input.keyboard.addCapture('UP, DOWN, LEFT, RIGHT');
this.input.keyboard.on('keydown_UP', function (event) {
hotdog.y -= 4;
}, this);
// ... аналогично для DOWN, LEFT, RIGHT
this.input.on('pointerdown', function () {
console.log('down');
this.scene.pause();
this.scene.run('sceneB');
}, this);
this.hotdog = hotdog;
}
Создание наложенной сцены и альтернативное управление
SceneB создаётся как наложенная сцена. В её методе create рисуется полупрозрачный чёрный прямоугольник с помощью Graphics, создавая эффект затемнения фона из SceneA. Спрайт хот-дога добавляется заново, но с красным оттенком (setTint), чтобы визуально отличаться.
Обработка стрелок настраивается аналогично, но логика привязана к новому спрайту hotdog в контексте SceneB. Это показывает, что обработчики событий клавиатуры из разных сцен работают независимо, даже если они назначены на одни и те же клавиши.
Для возврата в SceneA используется событие keydown_SPACE. Важно отметить применение метода once вместо on — обработчик сработает только один раз. При нажатии пробела SceneB останавливается (stop()), а SceneA возобновляется (resume()).
create () {
var graphics = this.add.graphics();
graphics.fillStyle(0x000000, 0.5);
graphics.fillRect(0, 0, 800, 600);
var hotdog = this.add.image(400, 300, 'hotdog').setTint(0xff0000);
this.input.keyboard.addCapture('UP, DOWN, LEFT, RIGHT');
// ... обработчики для стрелок
this.input.keyboard.once('keydown_SPACE', function (event) {
this.scene.stop();
this.scene.resume('sceneA');
}, this);
this.hotdog = hotdog;
}
Непрерывная анимация в update и конфигурация игры
В методе update сцены A происходит постоянное вращение спрайта хот-дога (this.hotdog.rotation += 0.001). Поскольку сцена A поставлена на паузу при активации сцены B, её цикл update приостанавливается. При возобновлении (resume) анимация продолжается с того же места.
Конфигурация игры включает обе сцены в массиве scene. Phaser автоматически инициализирует их в порядке указания, но активной будет только первая сцена в списке (SceneA). Остальные сцены можно запускать и останавливать динамически.
update () {
this.hotdog.rotation += 0.001;
}
// ...
var config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
scene: [ SceneA, SceneB ] // Обе сцены переданы в массив
};
var game = new Phaser.Game(config);
Разница между pause/run и stop/resume
В этом примере показаны два подхода к управлению сценами:
1. **Пауза и запуск другой сцены:** `this.scene.pause(); this.scene.run('sceneB');`
* `pause()`: Приостанавливает выполнение текущей сцены (останавливаются `update`, физика, таймеры сцены), но сцена остаётся в памяти, и её объекты отображаются.
* `run('sceneB')`: Запускает сцену B, если она ещё не активна, или возобновляет её, если она была на паузе. Сцены A и B теперь выполняются параллельно (но A на паузе).
2. **Остановка и возобновление:** `this.scene.stop(); this.scene.resume('sceneA');`
* `stop()`: Полностью останавливает и уничтожает текущую сцену (SceneB). Все её игровые объекты, обработчики событий и данные удаляются.
* `resume('sceneA')`: Снимает сцену A с паузы, её цикл `update` возобновляется.
Выбор между pause/run и stop/resume зависит от необходимости сохранять состояние временно скрытой сцены.
Что попробовать дальше
Пример наглядно демонстрирует работу с несколькими активными сценами, независимой обработкой ввода и корректным управлением их жизненным циклом в Phaser. Для экспериментов попробуйте
- Добавить в SceneB свой метод
updateс другой анимацией и понаблюдать за её работой - Использовать
switchвместоrunиstopдля полной замены одной сцены на другую - Передавать данные (например, координаты хот-дога) из SceneA в SceneB через реестр данных игры (
this.registry) или напрямую при запуске сцены
