О чем этот пример
Плавное и реалистичное движение — основа приятной игровой механики. В Phaser за это отвечают Tween и их ease-функции. В этой статье мы разберем официальный пример, который наглядно показывает, как работают три варианта функции `back`: `in`, `out` и `inout`. Вы поймете, как создать интерактивную визуализацию для любой ease-функции, что поможет вам осознанно выбирать тип анимации для прыжков, отскоков или появления объектов в вашей игре.
Версия 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, 'Back Ease').setColor('#00ff00').setFontSize(32).setShadow(2, 2).setOrigin(0.5, 0);
const types = [ 'back.in', 'back.out', 'back.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 мы создаем базовые элементы интерфейса.
this.add.image(400, 300, 'bg').setScale(0.78);
this.add.text(400, 28, 'Back Ease').setColor('#00ff00').setFontSize(32).setShadow(2, 2).setOrigin(0.5, 0);
Затем определяем массив с названиями функций, которые будем перебирать, и создаем несколько ключевых объектов:
- label — текстовый элемент для отображения текущего типа функции.
- graph — объект Graphics для отрисовки графика движения.
- rect — зеленый прямоугольник, который будет движущейся точкой.
- rt — RenderTexture, на которую будет наноситься траектория движения прямоугольника. Это позволяет сохранить нарисованный путь.
Эти объекты — холсты и маркеры для нашей будущей анимации.
Сердце примера: функция graphEase
Вся анимация и отрисовка инкапсулированы в функцию graphEase. Она запускается при старте и при каждом клике.
const graphEase = () => {
if (tween) {
tween.stop();
}
rt.clear();
graph.clear();
rect.setPosition(50, 450);
label.setText([types[type], 'Click to change type']);
Сначала функция останавливает предыдущий твин (если он был), очищает RenderTexture и графику, сбрасывает позицию прямоугольника в стартовую точку и обновляет текст подсказки.
Затем создается новый твин, который является главным двигателем визуализации:
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();
}
});
Здесь задается движение прямоугольника от точки (50, 450) к точке (750, 100) за 4 секунды. Ключевой момент — разная ease-функция для осей: по X движение линейное ('linear'), а по Y применяется выбранная функция из семейства back. Это создает классический график зависимости Y от X.
В колбэке onUpdate при каждом изменении координаты X (key === 'x') прямоугольник отрисовывается в RenderTexture (чтобы оставался "шлейф"), и его текущая позиция добавляется в путь объекта graph. По завершении анимации (onComplete) этот путь обводится линией.
Интерактивность: обработка кликов
Чтобы можно было переключаться между типами функций, в сцене слушается событие клика (pointerdown).
this.input.on('pointerdown', () => {
type++;
if (type === types.length) {
type = 0;
}
graphEase();
});
Обработчик увеличивает индекс type, зацикливая его по достижении конца массива, и вызывает graphEase(). Это приводит к сбросу анимации и началу новой с уже выбранной функцией ease. Таким образом, один клик меняет поведение анимации по оси Y.
Что показывают графики Back-функций?
Запустив пример, вы увидите три разных графика:
- back.in: Движение начинается с небольшого "разгона" в обратную сторону (как бы откатывается назад), прежде чем пойти к цели. Подходит для анимации "замаха" перед ударом.
- back.out: Движение к цели проходит её точку, делая небольшой "перелёт", и затем возвращается. Идеально для эффектов перескока или упругого столкновения.
- back.inout: Комбинация двух предыдущих эффектов — движение начинается с отката и заканчивается перелётом. Создаёт ощущение инерции.
Использование RenderTexture (rt.draw(rect)) позволяет накапливать траекторию, а Graphics (graph.lineTo) — отображать её как чистую линию. Это отличный приём для создания визуализаций и следов.
Сборка и запуск игры
Код завершается стандартной конфигурацией игры и её созданием.
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000000',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Конфиг задаёт чёрный фон, размеры холста и указывает на нашу сцену Example. После создания экземпляра Phaser.Game автоматически запускается жизненный цикл сцены: preload, create, а затем игровой цикл update.
Что попробовать дальше
Этот пример — мощный инструмент для понимания ease-функций. Вы можете адаптировать этот код для визуализации любого другого типа easing (bounce, elastic, sine), просто изменив массив types. Попробуйте заменить RenderTexture на частицы (this.add.particles) для создания более эффектного шлейфа или привяжите анимацию к спрайту персонажа, чтобы сразу видеть, как разные функции влияют на прыжок.
