О чем этот пример
Визуализация прогресса — ключевой элемент игрового интерфейса. Жизнь героя, заряд способности, заполнение шкалы опыта — всё это требует интуитивно понятного и плавного отображения. В этой статье мы разберем, как создать анимированные прогресс-бары в Phaser 3, используя технику Nine Slice для идеального растягивания текстур без искажений и встроенную систему твинов для плавной анимации заполнения.
Версия 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.atlas('ui', 'assets/ui/nine-slice.png', 'assets/ui/nine-slice.json');
}
create ()
{
const bar1 = this.add.nineslice(400, 200, 'ui', 'ButtonOrange');
const fill1 = this.add.nineslice(286, 198, 'ui', 'ButtonOrangeFill1', 13, 39, 6, 6);
fill1.setOrigin(0, 0.5);
const bar2 = this.add.nineslice(400, 400, 'ui', 'ButtonOrange');
const fill2 = this.add.nineslice(286, 398, 'ui', 'ButtonOrangeFill2', 13, 39, 6, 6);
fill2.setOrigin(0, 0.5);
this.tweens.add({
targets: fill1,
width: 228,
duration: 3000,
ease: 'sine.inout',
yoyo: true,
repeat: -1,
});
this.tweens.add({
targets: fill2,
width: 228,
duration: 2000,
ease: 'bounce.out',
yoyo: true,
repeat: -1,
hold: 1000
});
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#00746b',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Что такое Nine Slice и зачем он нужен?
Nine Slice (9-slice scaling) — это метод масштабирования двумерных спрайтов, при котором текстура делится на 9 частей: 4 угла, 4 стороны и центральная часть. Углы не масштабируются, стороны растягиваются только по одной оси, а центр — по обеим. Это позволяет растягивать UI-элементы (кнопки, панели, рамки) до любых размеров без "размытия" или "расползания" закругленных краев или декоративных элементов.
В Phaser 3 объект Nine Slice создается через this.add.nineslice(). Именно его мы будем использовать для создания рамки бара и его заполняемой части.
Подготовка ресурсов и загрузка атласа
В нашем примере используется атлас текстур ui, который содержит два спрайта для рамки и два для заполнения. Атлас — это один большой файл изображения и JSON-файл с координатами отдельных кадров (спрайтов) внутри него. Это оптимизирует загрузку и рендеринг.
В методе preload() мы загружаем этот атлас.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.atlas('ui', 'assets/ui/nine-slice.png', 'assets/ui/nine-slice.json');
}
Создание статичной рамки прогресс-бара
Рамка (контейнер) бара — это статичный элемент, который определяет границы нашего прогресс-бара. Мы создаем его как объект Nine Slice, используя текстуру 'ButtonOrange'. Первые два аргумента метода — координаты X и Y центра объекта на сцене.
const bar1 = this.add.nineslice(400, 200, 'ui', 'ButtonOrange');
Здесь мы не передаем последние четыре аргумента (width, height, leftWidth, rightHeight), поэтому объект принимает размеры по умолчанию из текстуры. Это готовая кнопка или рамка с уже заданными в текстуре размерами.
Создание анимируемой полосы заполнения
Сама полоса заполнения — это второй объект Nine Slice. Ключевые отличия от рамки:
1. **Текстура:** Используется 'ButtonOrangeFill1' (или Fill2 для второго бара) — это сплошная полоса без рамки.
2. **Размеры:** Явно задаются ширина и высота (13 и 39 пикселей), а также размеры левой и правой "не растягиваемых" частей (оба по 6 пикселей). Это нужно, чтобы концы полосы (возможно, с закруглениями или эффектами) не искажались при растягивании.
3. **Точка трансформации (origin):** Устанавливается в (0, 0.5) с помощью setOrigin(0, 0.5). Это значит, что все трансформации (в нашем случае — изменение ширины) будут происходить относительно левого края (x=0) и центра по вертикали (y=0.5). Так полоса будет увеличиваться вправо, оставаясь выровненной по левому краю рамки.
const fill1 = this.add.nineslice(286, 198, 'ui', 'ButtonOrangeFill1', 13, 39, 6, 6);
fill1.setOrigin(0, 0.5);
Координаты fill1 (286, 198) рассчитаны так, чтобы левый край полосы совпадал с внутренним левым краем рамки bar1 (400 - (ширина_рамки/2) + небольшой отступ).
Анимация заполнения с помощью Tween
Phaser предоставляет мощную систему анимаций Tweens. Мы создаем твин для объекта fill1, который анимирует его свойство width от исходного значения до 228 пикселей.
this.tweens.add({
targets: fill1,
width: 228,
duration: 3000,
ease: 'sine.inout',
yoyo: true,
repeat: -1,
});
Разберем параметры:
- targets: объект или массив объектов для анимации.
- width: целевое значение свойства.
- duration: длительность анимации в миллисекундах.
- ease: функция плавности ('sine.inout' для плавного ускорения и замедления).
- yoyo: true: после достижения цели анимация проиграется в обратном порядке.
- repeat: -1: бесконечное повторение (включая йо-йо-эффект).
Второй твин для fill2 использует функцию 'bounce.out' (эффект отскока в конце) и параметр hold: 1000, который добавляет паузу в 1 секунду перед началом обратной анимации.
Что попробовать дальше
Комбинируя объекты Nine Slice для отрисовки и систему Tweens для анимации, вы можете легко создавать в Phaser 3 любые прогресс-бары, полосы здоровья или индикаторы загрузки, которые идеально масштабируются и анимируются. Для экспериментов попробуйте:
1. Изменить функции плавности (ease) на 'quad.inout', 'back.out' или 'elastic.out' для разных визуальных эффектов.
2. Анимировать не только ширину, но и, например, цвет заполнения (tint).
3. Привязать ширину полосы fill1.width к реальному игровому параметру (например, здоровью игрока) и обновлять ее в реальном времени, убрав твин.
