О чем этот пример
Бывают ситуации, когда игру на Phaser нужно не просто поставить на паузу, а полностью перезагрузить — например, для сброса состояния, перехода на новую сцену с другим набором ассетов или перезапуска после Game Over. Простое обновление страницы — неоптимальное решение. Встроенный метод `game.destroy()` позволяет корректно завершить работу текущего экземпляра игры, освободить ресурсы и создать новый. Эта статья на практическом примере покажет, как безопасно уничтожить игру и инициировать её перезапуск по действию игрока, что полезно для создания перезапускаемых мини-игр или смены режимов.
Версия 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('taikodrummaster', 'assets/pics/taikodrummaster.jpg');
this.load.image('sukasuka-chtholly', 'assets/pics/sukasuka-chtholly.png');
}
create ()
{
this.add.image(400, 300, 'taikodrummaster');
var chtholly = this.add.image(400, 500, 'sukasuka-chtholly');
this.tweens.add({
targets: chtholly,
y: Math.random() * 600,
x: Math.random() * 200,
ease: 'Sine.easeInOut',
duration: 2000,
yoyo: true,
repeat: -1
});
this.input.on('pointerdown', function () {
this.sys.game.destroy(true);
document.addEventListener('mousedown', function newGame () {
game = new Phaser.Game(config);
document.removeEventListener('mousedown', newGame);
});
}, this);
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
scene: Example
};
let game = new Phaser.Game(config);
Подготовка сцены и анимация объектов
В примере создаётся простая сцена с двумя изображениями. Одно служит фоном, а второе — анимируемым объектом.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('taikodrummaster', 'assets/pics/taikodrummaster.jpg');
this.load.image('sukasuka-chtholly', 'assets/pics/sukasuka-chtholly.png');
}
В методе preload задаётся базовый URL для загрузки и загружаются два изображения. Это стандартная практика подготовки ресурсов.
create ()
{
this.add.image(400, 300, 'taikodrummaster');
var chtholly = this.add.image(400, 500, 'sukasuka-chtholly');
this.tweens.add({
targets: chtholly,
y: Math.random() * 600,
x: Math.random() * 200,
ease: 'Sine.easeInOut',
duration: 2000,
yoyo: true,
repeat: -1
});
В create сначала добавляется фоновое изображение. Затем создаётся спрайт chtholly и на него навешивается бесконечная твин-анимация (repeat: -1), которая случайным образом перемещает его по полю. Это создаёт динамичную картинку, которую мы будем сбрасывать.
Обработчик клика и вызов game.destroy()
Ключевая логика происходит по клику мыши (или касанию). В обработчике события pointerdown вызывается метод, который полностью уничтожает текущий экземпляр игры.
this.input.on('pointerdown', function () {
this.sys.game.destroy(true);
// ... Действия после уничтожения
}, this);
Обратите внимание на путь this.sys.game. this здесь — это контекст сцены (Scene), this.sys — её системный менеджер, а this.sys.game — ссылка на главный объект игры (Phaser.Game). Вызов destroy(true) с параметром true гарантирует, что будут удалены не только игровые циклы и рендерер, но и очищен Canvas-элемент и его родительский контейнер в DOM. Без этого флага DOM-элементы останутся в памяти, что может привести к утечкам при многократных перезапусках.
Механизм перезапуска игры
После уничтожения игры её объект больше не работает. Чтобы начать заново, нужно создать новый экземпляр Phaser.Game. Однако делать это сразу в том же потоке выполнения нельзя — браузер может не успеть завершить очистку. Поэтому создание новой игры оборачивается в обработчик нативного события.
document.addEventListener('mousedown', function newGame () {
game = new Phaser.Game(config);
document.removeEventListener('mousedown', newGame);
});
Этот код вешает на документ временный слушатель события mousedown. Как только пользователь кликнет в любом месте страницы (после уничтожения старой игры), выполнится функция newGame. Она создаёт новую игру, используя ту же конфигурацию config, и сразу удаляет себя из слушателей, чтобы не срабатывать повторно. Переменная game объявлена глобально (let game = ...), поэтому её перезапись корректно создаёт новый экземпляр.
Конфигурация игры и глобальная переменная
Для работы механизма перезапуска конфиг игры и ссылка на её экземпляр должны быть доступны глобально (в области видимости модуля или скрипта).
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
scene: Example
};
let game = new Phaser.Game(config);
Константа config определяет настройки игры, включая корневую сцену Example. Переменная game хранит текущий рабочий экземпляр. Именно её перезаписывает функция newGame. Важно, что config остаётся неизменным и используется повторно для инициализации идентичной игры.
Что попробовать дальше
Метод this.sys.game.destroy(true) — это правильный способ полной остановки игры и очистки её ресурсов. В связке с отложенным созданием нового экземпляра через событие (например, mousedown) это позволяет реализовать плавный перезапуск. Для экспериментов попробуйте
- Уничтожать игру не по клику, а по таймеру или условию (окончание уровня)
- Перед перезапуском динамически менять параметры в объекте
config(например,sceneилиwidth) - Реализовать перезапуск не с нуля, а с передачей каких-либо данных в новую сцену через глобальное хранилище
