О чем этот пример
Плавность анимации — ключевой фактор для игрового комфорта. В этой статье мы разберем, как в Phaser создавать независимые от частоты кадров движения объектов, используя delta time и хранение пользовательских данных. Это особенно полезно для создания фоновых элементов, врагов или декораций, которые должны двигаться с постоянной скоростью на любых устройствах, от мощных ПК до мобильных телефонов.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class SceneF extends Phaser.Scene {
constructor ()
{
super('SceneF');
this.mines = [];
}
create ()
{
this.cameras.main.setViewport(0, 136, 1024, 465);
for (let i = 0; i < 8; i++)
{
let x = Phaser.Math.Between(400, 1400);
let y = Phaser.Math.Between(0, 460);
let mine = this.add.sprite(x, y, 'mine').play('mine');
mine.setData('vx', Phaser.Math.FloatBetween(0.08, 0.14));
this.mines.push(mine);
}
}
update (time, delta)
{
for (let i = 0; i < this.mines.length; i++)
{
let mine = this.mines[i];
mine.x -= mine.getData('vx') * delta;
if (mine.x <= -100)
{
mine.x = 1224;
mine.y = Phaser.Math.Between(0, 460);
}
}
}
}
Подготовка сцены и контейнера
В конструкторе сцены мы задаем её ключ и инициализируем массив для хранения создаваемых спрайтов. Это стандартный подход для организации кода в Phaser.
constructor ()
{
super('SceneF');
this.mines = [];
}
Создание объектов с данными
В методе create мы сначала устанавливаем область просмотра для камеры. Затем в цикле создаем несколько спрайтов, используя случайные координаты. Ключевой момент — сохранение скорости для каждого объекта через метод setData. Это встроенная в Phaser система для хранения произвольной информации прямо на игровом объекте.
create ()
{
this.cameras.main.setViewport(0, 136, 1024, 465);
for (let i = 0; i < 8; i++)
{
let x = Phaser.Math.Between(400, 1400);
let y = Phaser.Math.Between(0, 460);
let mine = this.add.sprite(x, y, 'mine').play('mine');
mine.setData('vx', Phaser.Math.FloatBetween(0.08, 0.14));
this.mines.push(mine);
}
}
Независимое от FPS движение
Метод update вызывается на каждом кадре. Для плавного движения, которое не зависит от частоты кадров, мы умножаем скорость объекта на параметр delta. Это время в миллисекундах, прошедшее с предыдущего кадра. Если игра замедлится, delta увеличится, и объект пройдет большее расстояние за один вызов update, сохраняя общую визуальную скорость.
update (time, delta)
{
for (let i = 0; i < this.mines.length; i++)
{
let mine = this.mines[i];
mine.x -= mine.getData('vx') * delta;
Респаун объектов за пределами экрана
Когда объект полностью покинул видимую область слева, мы перемещаем его обратно за правую границу и задаем новую случайную позицию по вертикали. Это создает эффект бесконечного цикла движения.
if (mine.x <= -100)
{
mine.x = 1224;
mine.y = Phaser.Math.Between(0, 460);
}
}
}
Что попробовать дальше
Использование delta time и setData/getData — это основа для создания предсказуемой и плавной игровой анимации. Для экспериментов попробуйте изменить логику респауна, чтобы объекты появлялись не сразу, а с задержкой, или добавьте вертикальное движение, также управляемое через данные объекта. Можно реализовать разные типы движения для разных групп объектов, храня в данных не только скорость, но и тип траектории.
