О чем этот пример
При разработке игр часто возникает необходимость перезапустить игровую сцену или отдельные анимации. Пример с багом #6253 наглядно демонстрирует тонкость работы с системой анимаций Tween в Phaser 3 при использовании `Scene.restart()`. Понимание этого механизма поможет избежать скрытых ошибок и неожиданного поведения анимаций при управлении состоянием игры.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
var config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: {
init: init,
preload: preload,
create: create
}
};
var game = new Phaser.Game(config);
function init ()
{
this.input.once('pointerdown', () => {
console.log('restarting');
this.scene.restart();
});
}
function preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('block', 'assets/sprites/block.png');
}
function create ()
{
var image = this.add.image(100, 300, 'block');
var tween = this.tweens.add({
delay: 100,
targets: image,
x: 700
});
console.log(tween);
}
В чём суть примера и потенциальной проблемы?
В представленном коде создаётся базовая сцена Phaser 3. В её функции create() запускается Tween-анимация, которая перемещает спрайт с координаты x=100 до x=700. Уникальность примера в обработчике события pointerdown в функции init(), который по клику перезапускает всю сцену с помощью this.scene.restart().
Ключевой вопрос: что происходит с объектом tween при таком перезапуске? Перезапуск сцены повторно выполняет её функции preload() и create(), что означает повторную загрузку ассетов (хотя они могут быть кэшированы) и повторное создание всех игровых объектов.
this.scene.restart();
Жизненный цикл Tween и перезапуск сцены
Система Tween в Phaser (this.tweens) управляется на уровне игры или сцены. Когда создаётся Tween через this.tweens.add(), он добавляется в менеджер анимаций.
Однако метод Scene.restart() останавливает и уничтожает текущую сцену, а затем создаёт её экземпляр заново. При этом все созданные в предыдущей инкарнации сцены объекты, включая Tween'ы, должны быть корректно удалены. В идеале старый Tween должен быть автоматически уничтожен, а в новой сцене создан новый.
Исходный код фиксирует созданный объект tween в переменную и выводит её в консоль. Это полезно для отладки.
var tween = this.tweens.add({
delay: 100,
targets: image,
x: 700
});
console.log(tween);
Практические рекомендации по управлению Tween
Хотя в данном примере код работает, при более сложной логике могут возникнуть проблемы, если ссылка на старый Tween сохранилась где-то ещё. Лучшие практики для избежания ошибок:
1. **Не храните долгоживущие ссылки на Tween'ы**, если сцена может быть перезапущена. Ссылка станет недействительной.
2. **Для принудительной остановки** анимации перед перезапуском сцены можно использовать методы менеджера this.tweens. Например, можно остановить все твины сцены.
3. **Для перезапуска конкретной анимации** лучше не перезапускать всю сцену, а управлять жизненным циклом Tween через его методы.
Пример безопасной остановки всех твинов сцены перед перезапуском:
// В обработчике события перед restart()
this.tweens.killAll();
this.scene.restart();
Или перезапуск конкретного твина:
// Если tween — это ваша сохранённая ссылка
if (tween) {
tween.stop();
tween.seek(0); // Возврат к началу
tween.play(); // Запуск с начала
}
Что попробовать дальше
Перезапуск сцены через Scene.restart() — мощный инструмент для сброса состояния, но он требует внимательного отношения к созданным анимациям и другим объектам. Система Phaser в большинстве случаев корректно очищает их, однако для полного контроля и избежания утечек памяти или конфликтов рекомендуется явно управлять жизненным циклом Tween-анимаций.
**Идеи для экспериментов:** Попробуйте создать несколько независимых твинов и перезапустить сцену, отслеживая их состояние в консоли. Или реализуйте кнопку 'Пауза', которая будет останавливать твины через this.tweens.pauseAll(), а не перезапускать сцену.
