О чем этот пример
Плавность анимации — ключ к приятному игровому опыту. Резкие, линейные движения выглядят неестественно и механически. Phaser предлагает мощную систему твинов (tweens) с поддержкой easing-функций, которые позволяют задавать нелинейную интерполяцию свойств объектов. В этой статье мы разберем, как работают базовые вариации одной из таких функций — `Sine` — и как их применять для создания органичного движения. Наглядный пример с тремя НЛО показывает разницу между типами easing: `in`, `out` и `inOut`. Понимание этих отличий позволит вам осознанно выбирать тип плавности для различных игровых ситуаций: от плавного появления интерфейса до реалистичного прыжка персонажа.
Версия 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('bg', 'assets/tweens/space.png');
this.load.image('ufo1', 'assets/tweens/ufo1.png');
this.load.image('ufo2', 'assets/tweens/ufo2.png');
this.load.image('ufo3', 'assets/tweens/ufo3.png');
}
create ()
{
this.add.image(400, 300, 'bg');
this.add.text(20, 20, 'Sine Ease').setFontSize(32).setShadow(2, 2);
this.add.text(750, 70, 'Sine.in').setShadow(2, 2).setOrigin(1, 0);
this.add.text(750, 255, 'Sine.out').setShadow(2, 2).setOrigin(1, 0);
this.add.text(750, 440, 'Sine.inOut').setShadow(2, 2).setOrigin(1, 0);
const ufo1 = this.add.image(100, 140, 'ufo1');
const ufo2 = this.add.image(100, 325, 'ufo2');
const ufo3 = this.add.image(100, 510, 'ufo3');
this.tweens.add({
targets: ufo1,
x: 700,
duration: 2000,
repeat: -1,
hold: 500,
repeatDelay: 500,
ease: 'sine.in'
});
this.tweens.add({
targets: ufo2,
x: 700,
duration: 2000,
repeat: -1,
hold: 500,
repeatDelay: 500,
ease: 'sine.out'
});
this.tweens.add({
targets: ufo3,
x: 700,
duration: 2000,
repeat: -1,
hold: 500,
repeatDelay: 500,
ease: 'sine.inout'
});
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Что такое easing и зачем он нужен?
Easing (или плавность) — это математическая функция, которая определяет, как изменяется значение анимируемого свойства во времени. Вместо линейного изменения (когда объект движется с постоянной скоростью), easing позволяет задать ускорение, замедление или их комбинацию.
В Phaser easing-функции передаются в свойство ease конфигурации твина. Встроенные функции, такие как sine, quad, cubic, expo, имитируют естественные движения, подчиняющиеся законам физики (например, инерцию). Это делает анимацию визуально привлекательной и "живой".
Разбор примера: загрузка ресурсов и создание сцены
Код начинается с загрузки изображений в методе preload. Обратите внимание на использование this.load.setBaseURL — это удобный способ задать базовый путь для всех последующих загрузок, чтобы не указывать полные URL.
В методе create мы размещаем фон, текстовые подписи и три спрайта НЛО, которые станут целями для анимации. Важный момент: позиционирование текста с помощью .setOrigin(1, 0) выравнивает его по правому краю (origin X=1), что удобно для создания подписей у края экрана.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('bg', 'assets/tweens/space.png');
this.load.image('ufo1', 'assets/tweens/ufo1.png');
this.load.image('ufo2', 'assets/tweens/ufo2.png');
this.load.image('ufo3', 'assets/tweens/ufo3.png');
}
create ()
{
this.add.image(400, 300, 'bg');
this.add.text(20, 20, 'Sine Ease').setFontSize(32).setShadow(2, 2);
this.add.text(750, 70, 'Sine.in').setShadow(2, 2).setOrigin(1, 0);
this.add.text(750, 255, 'Sine.out').setShadow(2, 2).setOrigin(1, 0);
this.add.text(750, 440, 'Sine.inOut').setShadow(2, 2).setOrigin(1, 0);
const ufo1 = this.add.image(100, 140, 'ufo1');
const ufo2 = this.add.image(100, 325, 'ufo2');
const ufo3 = this.add.image(100, 510, 'ufo3');
// ... создание твинов
}
Создание и настройка твинов
Анимация создается с помощью метода this.tweens.add. Каждый твин принимает объект конфигурации. Давайте разберем ключевые свойства:
* targets: объект или массив объектов для анимации.
* x: 700: целевое значение свойства (движение по горизонтали).
* duration: длительность одного цикла анимации в миллисекундах.
* repeat: -1: бесконечное повторение. Можно задать положительное число для ограниченного количества повторов.
* hold: пауза (в мс) при достижении конечного значения перед началом повторения.
* repeatDelay: пауза (в мс) перед началом каждого нового цикла после hold.
* ease: строка, определяющая функцию плавности. Именно здесь мы задаем разные варианты sine.
this.tweens.add({
targets: ufo1,
x: 700,
duration: 2000,
repeat: -1,
hold: 500,
repeatDelay: 500,
ease: 'sine.in'
});
В чем разница между Sine.in, Sine.out и Sine.inOut?
Все три варианта используют синусоидальную кривую для расчета плавности, но распределение ускорения разное:
1. **sine.in** (Ускорение): Анимация начинается медленно и плавно набирает скорость к концу. Визуально НЛО трогается еле-еле, но к финишу летит быстро. Идеально для объектов, на которые действует сила (например, запуск снаряда).
2. **sine.out** (Замедление): Анимация начинается быстро и плавно замедляется к концу. НЛО резко стартует и мягко "припарковывается". Отлично подходит для завершающих действий, UI-элементов, которые выезжают на экран.
3. **sine.inout** (Комбинация): Анимация плавно ускоряется в первой половине и плавно замедляется во второй. Это создает наиболее естественное и сбалансированное движение. Универсальный вариант для многих цикличных или переходных анимаций.
Выбор типа зависит от желаемого визуального эффекта и контекста движения в игре.
Инициализация игры: объект конфигурации
За пределами класса сцены создается объект конфигурации config, который передается в конструктор Phaser.Game. Это стандартная точка входа для любого проекта на Phaser.
* type: Определяет рендерер (WebGL или Canvas). Phaser.AUTO позволяет движку выбрать лучший из доступных.
* width и height: Размер игрового холста.
* backgroundColor: Цвет фона, который отобразится до загрузки и отрисовки сцены.
* parent: ID HTML-элемента, в который будет встроен игровой холст.
* scene: Класс или экземпляр основной сцены.
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Что попробовать дальше
Использование easing-функций — простой, но мощный способ значительно улучшить качество анимаций в вашей игре. Начните с sine.in, sine.out и sine.inOut, чтобы почувствовать разницу.
**Идеи для экспериментов:**
1. Поменяйте функцию плавности на quad, cubic или expo и сравните характер движения.
2. Анимируйте не только `x, но и другие свойства:y,scale,angle,alpha`.
3. Создайте цепочку твинов с помощью chain или yoyo: true, чтобы объект возвращался в исходную точку с другой ease-функцией.
4. Используйте delay для создания последовательных анимаций нескольких объектов.
