О чем этот пример
Перезапуск сцены — частый инструмент в арсенале геймдизайна, будь то рестарт уровня после поражения или быстрый сброс состояния. Однако в Phaser 3 метод `scene.restart()` имеет ключевую особенность, которую важно понимать, чтобы избежать неожиданного поведения и утечек памяти. Этот пример наглядно демонстрирует, как работает перезапуск и какие данные сохраняются между вызовами.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
function preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('apple', 'assets/sprites/apple.png');
this.load.image('image1', 'assets/sprites/mushroom2.png');
}
function create()
{
this.add.tileSprite(400, 300, 800, 600, 'apple');
let x = Phaser.Math.Between(100, 400);
this.add.text(x, 100, 'Phaser', { fontFamily: 'Arial', fontSize: 64, color: '#00ff00' });
this.add.text(x, 200, 'Phaser', { fontFamily: 'Arial', fontSize: 64, color: '#00ff00' });
this.add.text(x, 300, 'Phaser', { fontFamily: 'Arial', fontSize: 64, color: '#00ff00' });
this.input.once('pointerdown', () => {
this.scene.restart();
console.log('restarted');
});
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 800,
height: 600,
scene: {
preload: preload,
create: create
}
};
const game = new Phaser.Game(config);
Что делает метод `scene.restart()`?
При вызове this.scene.restart() текущая сцена не уничтожается полностью. Вместо этого Phaser выполняет "мягкий" перезапуск: останавливает сцену, а затем снова запускает её, вызывая методы preload, create и т.д. Важнейший нюанс: **загруженные ассеты (изображения, звуки) не выгружаются из кэша**. Это сделано для производительности, чтобы избежать повторных загрузок одних и тех же ресурсов.
В примере перезапуск инициируется по клику мыши:
Инициализация и загрузка ресурсов
В методе preload задаётся базовый URL для загрузки и загружаются два изображения. Обратите внимание, что setBaseURL вызывается только один раз при первом запуске сцены. При рестарте метод preload выполнится снова, но повторной загрузки apple и image1 не произойдет, так как они уже находятся в кэше.
function preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('apple', 'assets/sprites/apple.png');
this.load.image('image1', 'assets/sprites/mushroom2.png');
}
Создание игровых объектов и событие рестарта
В методе create создаётся фон-тайлспрайт, три текстовых объекта и назначается обработчик клика. Координата `xдля текстов генерируется случайно при каждом вызовеcreate. Это ключевой момент: при рестартеcreate` выполняется заново, поэтому позиция текстов изменится.
Однако тайлспрайт apple будет отрисован поверх предыдущего, так как старые объекты не удаляются автоматически. Это может привести к наложению и нежелательному накоплению объектов.
function create()
{
this.add.tileSprite(400, 300, 800, 600, 'apple');
let x = Phaser.Math.Between(100, 400);
this.add.text(x, 100, 'Phaser', { fontFamily: 'Arial', fontSize: 64, color: '#00ff00' });
// ... добавлены ещё два текста
this.input.once('pointerdown', () => {
this.scene.restart();
console.log('restarted');
});
}
Проблема накопления объектов и её решение
Главный подводный камень restart() — накопление игровых объектов, созданных в предыдущих запусках сцены. Они остаются в памяти и на дисплее, хотя логически считаются "устаревшими". В данном примере каждый клик добавит новый тайлспрайт и три текста поверх существующих.
Для чистого рестарта необходимо вручную очищать сцену перед перезапуском. Самый надёжный способ — слушать событие shutdown и удалять объекты там, либо использовать scene.start() с другим ключом сцены для полной её пересоздания.
Что попробовать дальше
Метод scene.restart() — это не полный сброс, а быстрый рецикл сцены с сохранением загруженных ресурсов. Используйте его, когда нужно сбросить игровую логику, но не загружать ассеты заново. Для экспериментов попробуйте
- Добавить счётчик кликов в тексте, чтобы визуально отслеживать рестарты
- Вместо
restart()использовать комбинациюscene.stop()иscene.start() - Очищать все объекты сцены в обработчике события
shutdown
