О чем этот пример
Плавность анимации — ключевой элемент геймдизайна, влияющий на восприятие игрового процесса. В Phaser за неё отвечают функции плавности (eases), которые контролируют скорость изменения параметров твина. Этот пример наглядно показывает, как работают три основные вариации квадро-функции плавности (`quart.in`, `quart.out`, `quart.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/background-crt.jpg');
}
create ()
{
this.add.image(400, 300, 'bg').setScale(0.78);
this.add.text(400, 28, 'Quartic Ease').setColor('#00ff00').setFontSize(32).setShadow(2, 2).setOrigin(0.5, 0);
const types = [ 'quart.in', 'quart.out', 'quart.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);
Структура сцены и загрузка
Класс Example расширяет Phaser.Scene. В методе preload задаётся базовый URL для загрузки ассетов и загружается фоновое изображение. Важно: setBaseURL используется для указания корневого пути, что удобно при работе с внешними ресурсами.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('bg', 'assets/tweens/background-crt.jpg');
}
Инициализация графических элементов
В методе create размещается фон, заголовок и создаются все необходимые объекты для визуализации. Обратите внимание на роли каждого:
- label — текстовый блок для отображения текущего типа плавности.
- graph — объект Graphics для отрисовки линии графика.
- rect — маленький прямоугольник (2x2 пикселя), который будет двигаться и служить пером для графика.
- rt — RenderTexture, которая рисует след движения прямоугольника.
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);
Функция визуализации графика
Функция graphEase — сердце примера. Она сбрасывает предыдущую анимацию, очищает холсты и запускает новый твин для прямоугольника rect. Ключевой момент — твин задаётся для двух свойств с разными функциями плавности: движение по X (`x) использует линейную плавность ('linear'), а по Y (y`) — выбранный квадро-тип.
tween = this.tweens.add({
targets: rect,
x: { value: 750, ease: 'linear' },
y: { value: 100, ease: types[type] },
duration: 4000,
// ... обработчики onUpdate и onComplete
});
Логика отрисовки в реальном времени
В коллбэке onUpdate происходит магия. Он срабатывает на каждом кадре обновления твина. Условие if (key === 'x') гарантирует, что отрисовка выполняется только один раз за кадр (при обновлении свойства `x). Движущийся прямоугольник рисуется наRenderTexture(rt.draw(rect)), а его текущие координаты добавляются в путь объектаGraphics(graph.lineTo(rect.x, rect.y)). По завершении анимации (onComplete) путь обводится линией (graph.stroke()`), финализируя график.
onUpdate: (tween, target, key) => {
if (key === 'x')
{
rt.draw(rect);
graph.lineTo(rect.x, rect.y);
}
},
onComplete: () => {
graph.stroke();
}
Обработка ввода и переключение типа
Слушатель события pointerdown на сцене (this.input.on) позволяет циклически переключаться между типами плавности из массива types. При каждом клике индекс type увеличивается, а по достижении конца массива сбрасывается в ноль, после чего вызывается graphEase() для перезапуска анимации с новыми параметрами.
this.input.on('pointerdown', () => {
type++;
if (type === types.length)
{
type = 0;
}
graphEase();
});
Что попробовать дальше
Пример демонстрирует мощный и наглядный подход к изучению функций плавности в Phaser. Вы можете экспериментировать: попробуйте другие имена eases из документации (например, sine, cubic, back), измените длительность (duration) или траекторию движения, добавив больше свойств в твин. Это поможет интуитивно понять, какая кривая лучше всего подходит для конкретной игровой ситуации — будь то подпрыгивание мяча или плавное появление меню.
