О чем этот пример
Визуализация кривых easing — ключ к пониманию того, как оживить игровые объекты. Плавное ускорение и замедление делают движение естественным, а не механическим. В этой статье мы разберем официальный пример Phaser, который наглядно демонстрирует три варианта кубической функции easing и позволяет интерактивно переключаться между ними, сразу наблюдая разницу в траектории движения спрайта. Этот практический подход поможет вам осознанно выбирать тип плавности для своих твинов.
Версия 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/background-crt.jpg');
}
create ()
{
this.add.image(400, 300, 'bg').setScale(0.78);
this.add.text(400, 28, 'Cubic Ease').setColor('#00ff00').setFontSize(32).setShadow(2, 2).setOrigin(0.5, 0);
const types = [ 'cubic.in', 'cubic.out', 'cubic.inout' ];
let type = 0;
let tween;
const label = this.add.text(400, 530).setColor('#00ff00').setFontSize(22).setShadow(1, 1).setOrigin(0.5, 0).setAlign('center');
const graph = this.add.graphics();
const rect = this.add.rectangle(100, 400, 2, 2, 0x00ff00);
const rt = this.add.renderTexture(400, 300, 800, 600);
const graphEase = () => {
if (tween)
{
tween.stop();
}
rt.clear();
graph.clear();
graph.lineStyle(3, 0x00ff00);
graph.beginPath();
rect.setPosition(50, 450);
label.setText([
types[type],
'Click to change type'
]);
tween = this.tweens.add({
targets: rect,
x: { value: 750, ease: 'linear' },
y: { value: 100, ease: types[type] },
duration: 4000,
onUpdate: (tween, target, key) => {
if (key === 'x')
{
rt.draw(rect);
graph.lineTo(rect.x, rect.y);
}
},
onComplete: () => {
graph.stroke();
}
});
}
this.input.on('pointerdown', () => {
type++;
if (type === types.length)
{
type = 0;
}
graphEase();
});
graphEase();
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000000',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и создание визуальных элементов
В методе preload загружается фоновая картинка. В create происходит основная настройка: добавляется фон, заголовок и несколько важных игровых объектов.
const graph = this.add.graphics();
const rect = this.add.rectangle(100, 400, 2, 2, 0x00ff00);
const rt = this.add.renderTexture(400, 300, 800, 600);
Объект graph типа Graphics будет рисовать график-траекторию. Маленький зеленый прямоугольник rect — это цель анимации (наш движущийся маркер). RenderTexture (rt) — это специальный текстуруемый объект, который будет в реальном времени сохранять отрисованные кадры движения прямоугольника, создавая эффект "шлейфа". Также создается текстовый label для отображения текущего типа easing.
Сердце примера: функция graphEase и настройка твина
Вся логика анимации и отрисовки инкапсулирована в функции graphEase. При каждом ее вызове старый твин останавливается, а RenderTexture и Graphics очищаются. Прямоугольник возвращается в стартовую позицию.
tween = this.tweens.add({
targets: rect,
x: { value: 750, ease: 'linear' },
y: { value: 100, ease: types[type] },
duration: 4000,
Здесь создается твин, управляющий прямоугольником rect. Ключевой момент: движение по оси X использует линейную (linear) функцию плавности, а по оси Y — одну из кубических (cubic.in, cubic.out, cubic.inout), выбранную из массива types. Это позволяет увидеть разницу между линейным и плавным движением на одном объекте.
Магия отрисовки траектории в реальном времени
Чтобы увидеть график функции easing, используется обратный вызов onUpdate в конфигурации твина.
onUpdate: (tween, target, key) => {
if (key === 'x')
{
rt.draw(rect);
graph.lineTo(rect.x, rect.y);
}
},
Коллбэк срабатывает на каждом обновлении твина. Условие if (key === 'x') гарантирует, что операции отрисовки выполняются только один раз за кадр (при обновлении свойства X). rt.draw(rect) добавляет текущее изображение прямоугольника в RenderTexture, оставляя след. graph.lineTo(rect.x, rect.y) продолжает линию графика Graphics до текущей позиции маркера. В onComplete эта линия окончательно отрисовывается методом stroke().
Интерактивность: переключение типов easing по клику
Пример становится исследовательским инструментом благодаря обработчику клика.
this.input.on('pointerdown', () => {
type++;
if (type === types.length) { type = 0; }
graphEase();
});
При каждом клике (или касании) индекс type увеличивается, циклически перебирая все элементы массива types. После обновления индекса вызывается graphEase(), которая пересоздает анимацию с новым типом плавности для оси Y, очищая старый график и след. Это позволяет мгновенно сравнить поведение cubic.in (ускорение от начала), cubic.out (замедление к концу) и cubic.inout (ускорение и замедление).
Что попробовать дальше
Пример блестяще демонстрирует, как разные easing-функции влияют на характер движения. Cubic.in создает эффект разгона, cubic.out — мягкой остановки, а cubic.inout — наиболее естественное для глаза движение с инерцией. Для экспериментов попробуйте заменить кубические функции на другие, например, sine, back или bounce, из того же массива. Или измените твин, задав кубическую плавность для обеих осей, но с разными типами (cubic.in для X, cubic.out для Y), чтобы получить сложные криволинейные траектории.
