О чем этот пример
Когда вы создаете много анимаций в Phaser, важно понимать, как менеджер твинов управляет памятью. Этот пример наглядно демонстрирует процесс создания и автоматического удаления сотен одноразовых анимаций (твинов). Мы разберем, как Phaser справляется с нагрузкой, и почему такой подход полезен для спецэффектов, частиц или любых объектов с коротким жизненным циклом.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
var x = 0;
var i = 0;
var blitter;
var text;
var scene = null;
var add = false;
var blitter;
var idx = 1;
var frame = 'veg01';
var numbers = [];
var text;
var tween;
class Example extends Phaser.Scene
{
constructor()
{
super();
}
preload()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.spritesheet('balls', 'assets/sprites/balls.png', { frameWidth: 17, frameHeight: 17 });
}
// This test will create lots of single-fire tweens and not re-use them, to test Tween Manager GC
create()
{
window.scene = this;
blitter = this.add.blitter(0, 0, 'balls');
text = this.add.text(10, 720);
this.time.addEvent({ delay: 2, callback: this.launch, callbackScope: this, repeat: 100000 });
}
launch()
{
i++;
var bob = blitter.create(x, 700, Phaser.Math.Between(0, 5));
x += 0.5;
if (x >= 1024)
{
x = 0;
}
if (Phaser.VERSION.substr(0, 4) === '3.60')
{
text.setText('Active Tweens: ' + this.tweens.tweens.length + '\nTotal Tweens created: ' + i);
}
else
{
text.setText('Active Tweens: ' + this.tweens.tweens.length + '\nTotal Tweens created: ' + i);
}
this.tweens.add({
targets: bob,
y: 10,
duration: Phaser.Math.Between(500, 1000),
ease: 'Power1',
yoyo: true,
onComplete: function ()
{
bob.destroy();
}
});
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Суть примера: нагрузочный тест для менеджера твинов
Пример создает простую нагрузочную систему: каждые 2 миллисекунды запускается новая анимация (tween) для спрайта. Твин перемещает спрайт вверх, затем обратно вниз, после чего спрайт уничтожается. Ключевой момент — твины не переиспользуются. Они создаются, выполняются и затем автоматически удаляются сборщиком мусора (GC) менеджера твинов. Это позволяет проверить, насколько эффективно Phaser управляет памятью при высокой частоте создания анимаций.
В интерфейсе отображаются два важных показателя: количество активных твинов в данный момент и общее количество созданных за время работы. Это помогает отслеживать нагрузку на систему.
Создание массового эффекта с помощью Blitter и таймера
Для эффективного отображения множества спрайтов используется Blitter — оптимизированный объект для рендеринга множества одинаковых или похожих изображений. Спрайтшит 'balls' загружается и служит источником кадров.
Основной механизм запуска заключен в событии таймера, которое повторяется 100 000 раз с минимальной задержкой.
this.time.addEvent({
delay: 2,
callback: this.launch,
callbackScope: this,
repeat: 100000
});
Функция launch вызывается этим событием. В ней создается новый спрайт (bob) через метод blitter.create в случайной горизонтальной позиции `x` и с случайным кадром из спрайтшита.
var bob = blitter.create(x, 700, Phaser.Math.Between(0, 5));
Сердце теста: создание и конфигурация одноразового твина
Для каждого созданного спрайта bob немедленно создается новый твин. Его задача — анимировать перемещение по оси Y.
Конфигурация твина содержит несколько ключевых параметров:
- targets: объект, к которому применяется анимация (наш спрайт bob).
- `y`: целевое значение свойства (движение к координате Y=10).
- duration: случайная длительность от 500 до 1000 мс, что создает разнообразие в анимации.
- ease: функция плавности 'Power1' для базового easing.
- yoyo: если true, твин проиграется в обратном направлении после завершения, вернув объект в исходную позицию.
- onComplete: колбэк, который срабатывает после окончания всей анимации (вперед и назад, если yoyo=true). В нем спрайт уничтожается.
this.tweens.add({
targets: bob,
y: 10,
duration: Phaser.Math.Between(500, 1000),
ease: 'Power1',
yoyo: true,
onComplete: function () {
bob.destroy();
}
});
После вызова onComplete твин автоматически удаляется из менеджера this.tweens. Именно этот процесс удаления и проверяется в тесте.
Мониторинг производительности в реальном времени
Для наглядности в левом нижнем углу сцены выводится текстовый счетчик. Он обновляется при каждом новом запуске твина и показывает два ключевых числа:
1. Количество активных твинов в массиве this.tweens.tweens. Это твины, которые в данный момент выполняются или ожидают запуска.
2. Общее количество созданных твинов за все время (`i`).
text.setText('Active Tweens: ' + this.tweens.tweens.length + '\nTotal Tweens created: ' + i);
Разница между этими числами — это количество твинов, которые уже завершили работу и были удалены сборщиком мусора менеджера. В стабильной системе количество активных твинов должно колебаться вокруг некоторого значения, а не бесконечно расти, что и подтверждает эффективность работы GC.
Что попробовать дальше
Этот пример — отличный способ убедиться, что Phaser надежно управляет памятью даже при интенсивном создании короткоживущих анимаций. Для экспериментов попробуйте изменить задержку таймера с 2 мс на большее значение, чтобы увидеть, как снижается нагрузка. Или уберите параметр yoyo: true и измените onComplete так, чтобы спрайт уничтожался сразу после достижения верха — это симулирует поведение частиц. Также можно заменить Blitter на обычные Sprite объекты и сравнить производительность.
