О чем этот пример
Плавная анимация числовых значений — будь то счётчик очков, здоровья или таймер — делает игру визуально приятнее и отзывчивее. Вместо резких скачков числа могут плавно перетекать от одного значения к другому, создавая эффект живого интерфейса. В этой статье мы разберём, как реализовать такую анимацию в Phaser 3 с помощью объекта `Tween` для счётчика (`addCounter`). Этот метод идеально подходит для отображения прогресса, начисления очков или любого другого изменения числовых параметров с анимацией.
Версия 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/skies/pixelsky.png');
this.load.atlas('ui', 'assets/ui/nine-slice.png', 'assets/ui/nine-slice.json');
}
create ()
{
this.add.image(400, 300, 'bg');
this.add.nineslice(400, 200, 'ui', 'RedButtonSml', 640, 98, 64, 64, 48, 48);
const score = this.add.text(400, 200, 'Score: 0', { font: '64px Courier', fill: '#ffffff' }).setOrigin(0.5, 0.5);
const button = this.add.nineslice(400, 500, 'ui', 'GreenButtonSml', 300, 98, 64, 64, 48, 48);
this.add.text(400, 500, 'Add 500 to Score', { font: '24px Arial', fill: '#ffffff' }).setOrigin(0.5, 0.5);
button.setInteractive();
let currentScore = 0;
let newScore = 500;
let updateTween = this.tweens.addCounter({
from: currentScore,
to: newScore,
duration: 2000,
ease: 'linear',
onUpdate: tween =>
{
const value = Math.round(tween.getValue());
score.setText(`Score: ${value}`);
}
});
button.on('pointerdown', () => {
currentScore = newScore;
newScore += 500;
if (updateTween.isPlaying())
{
// The tween is already running, so we'll update the end value with resetting it
updateTween.updateTo('value', newScore);
}
else
{
// The tween has finished, so create a new one
updateTween = this.tweens.addCounter({
from: currentScore,
to: newScore,
duration: 2000,
ease: 'linear',
onUpdate: tween =>
{
const value = Math.round(tween.getValue());
score.setText(`Score: ${value}`);
}
});
}
});
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены: фон, интерфейс и начальное значение
В начале кода мы загружаем фоновое изображение и UI-атлас для кнопок. В методе create() создаётся статичный фон, панель для отображения счёта и интерактивная кнопка.
Ключевой объект для анимации — текстовый элемент score. Счётчик очков инициализируется нулевым значением.
const score = this.add.text(400, 200, 'Score: 0', { font: '64px Courier', fill: '#ffffff' }).setOrigin(0.5, 0.5);
Кнопка создаётся с помощью add.nineslice для масштабируемого графического элемента и помечается как интерактивная с помощью setInteractive().
Создание анимации счётчика: Tween Counter
Phaser предоставляет специальный тип твина для работы с числами: this.tweens.addCounter(). В отличие от стандартного твина, который анимирует свойства объектов, addCounter анимирует числовое значение от одной точки (from) до другой (to).
Мы создаём твин, который будет анимировать значение от currentScore (0) до newScore (500) за 2000 миллисенд с линейной функцией плавности (ease: 'linear').
let updateTween = this.tweens.addCounter({
from: currentScore,
to: newScore,
duration: 2000,
ease: 'linear',
onUpdate: tween =>
{
const value = Math.round(tween.getValue());
score.setText(`Score: ${value}`);
}
});
Колбэк onUpdate вызывается на каждом кадре анимации. Внутри него мы получаем текущее анимированное значение через tween.getValue(), округляем его и обновляем текстовый объект score.
Динамическое управление анимацией: обновление на лету
Особенность этого примера — возможность динамически изменять целевое значение счётчика, даже если анимация ещё не завершилась. Это реализовано в обработчике клика по кнопке.
При нажатии на кнопку мы увеличиваем целевое значение newScore на 500. Далее логика разделяется:
- Если твин updateTween всё ещё воспроизводится (isPlaying()), мы не создаём новый, а обновляем его конечное значение (to) с помощью метода updateTo(). Это позволяет плавно продолжить анимацию к новой цели без сброса и рывков.
- Если твин уже завершился, мы создаём новый экземпляр твина с актуальными значениями currentScore и newScore.
button.on('pointerdown', () => {
currentScore = newScore;
newScore += 500;
if (updateTween.isPlaying())
{
updateTween.updateTo('value', newScore);
}
else
{
updateTween = this.tweens.addCounter({
from: currentScore,
to: newScore,
duration: 2000,
ease: 'linear',
onUpdate: tween =>
{
const value = Math.round(tween.getValue());
score.setText(`Score: ${value}`);
}
});
}
});
Метод updateTo('value', newScore) изменяет свойство to внутреннего счётчика твина. Обратите внимание, что первым аргументом передаётся строка 'value' — это фиксированное имя свойства, которое анимирует addCounter.
Конфигурация игры и запуск
Код завершается стандартной конфигурацией игры Phaser. Указывается тип рендерера, размеры холста, цвет фона и класс главной сцены.
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Что попробовать дальше
Использование this.tweens.addCounter() — это мощный и эффективный способ анимировать любые числовые значения в вашей игре. Вы можете экспериментировать: изменить функцию плавности (ease) на 'Power2' для более естественного ускорения, добавить визуальные эффекты (например, изменение цвета или масштаба текста при обновлении счёта) внутри колбэка onUpdate, или привязать такую анимацию к полоске здоровья, которая также является числовым значением. Этот паттерн отлично масштабируется для сложных систем прогресса и статистики.
