О чем этот пример
Управление временем — мощный инструмент для создания спецэффектов, паузы, слоу-мо или ускоренных событий в играх. В Phaser за это отвечает свойство `timeScale` у таймеров и событий времени. Эта статья покажет, как использовать `timeScale` на практическом примере с визуализацией, чтобы вы могли гибко управлять течением времени в своих проектах.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
clockSize = 100;
timerEvent3;
timerEvent2;
timerEvent1;
graphics;
create ()
{
// All 3 Timers are set for 5 seconds
// However the timeScale property scales the delay,
// causing the first timer to run for 10 seconds (half speed),
// and the 3rd to run for 2.5 seconds (twice speed)
this.timerEvent1 = this.time.addEvent({ delay: 5000, timeScale: 0.5 });
this.timerEvent2 = this.time.addEvent({ delay: 5000, timeScale: 1.0 });
this.timerEvent3 = this.time.addEvent({ delay: 5000, timeScale: 2.0 });
this.graphics = this.add.graphics({ x: 0, y: 0 });
}
update ()
{
this.graphics.clear();
this.drawClock(150, 300, this.timerEvent1);
this.drawClock(400, 300, this.timerEvent2);
this.drawClock(650, 300, this.timerEvent3);
}
drawClock (x, y, timer)
{
// Progress is between 0 and 1, where 0 = the hand pointing up and then rotating clockwise a full 360
// The frame
this.graphics.lineStyle(3, 0xffffff, 1);
this.graphics.strokeCircle(x, y, this.clockSize);
let angle;
let dest;
let p1;
let p2;
let size;
// The overall progress hand (only if repeat > 0)
if (timer.repeat > 0)
{
size = this.clockSize * 0.9;
angle = (360 * timer.getOverallProgress()) - 90;
dest = Phaser.Math.RotateAroundDistance({ x: x, y: y }, x, y, Phaser.Math.DegToRad(angle), size);
this.graphics.lineStyle(2, 0xff0000, 1);
this.graphics.beginPath();
this.graphics.moveTo(x, y);
p1 = Phaser.Math.RotateAroundDistance({ x: x, y: y }, x, y, Phaser.Math.DegToRad(angle - 5), size * 0.7);
this.graphics.lineTo(p1.x, p1.y);
this.graphics.lineTo(dest.x, dest.y);
this.graphics.moveTo(x, y);
p2 = Phaser.Math.RotateAroundDistance({ x: x, y: y }, x, y, Phaser.Math.DegToRad(angle + 5), size * 0.7);
this.graphics.lineTo(p2.x, p2.y);
this.graphics.lineTo(dest.x, dest.y);
this.graphics.strokePath();
this.graphics.closePath();
}
// The current iteration hand
size = this.clockSize * 0.95;
angle = (360 * timer.getProgress()) - 90;
dest = Phaser.Math.RotateAroundDistance({ x: x, y: y }, x, y, Phaser.Math.DegToRad(angle), size);
this.graphics.lineStyle(2, 0xffff00, 1);
this.graphics.beginPath();
this.graphics.moveTo(x, y);
p1 = Phaser.Math.RotateAroundDistance({ x: x, y: y }, x, y, Phaser.Math.DegToRad(angle - 5), size * 0.7);
this.graphics.lineTo(p1.x, p1.y);
this.graphics.lineTo(dest.x, dest.y);
this.graphics.moveTo(x, y);
p2 = Phaser.Math.RotateAroundDistance({ x: x, y: y }, x, y, Phaser.Math.DegToRad(angle + 5), size * 0.7);
this.graphics.lineTo(p2.x, p2.y);
this.graphics.lineTo(dest.x, dest.y);
this.graphics.strokePath();
this.graphics.closePath();
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Что такое TimeScale?
Свойство timeScale — это множитель, который применяется к задержке (delay) таймера или события времени. Оно масштабирует реальное время выполнения.
- timeScale: 1.0 — нормальная скорость (1 секунда реального времени = 1 секунда игрового).
- timeScale: 0.5 — замедление в два раза (таймер будет идти в два раза дольше).
- timeScale: 2.0 — ускорение в два раза (таймер завершится в два раза быстрее).
В примере создаются три таймера с одинаковой задержкой 5000 мс (5 секунд), но разными значениями timeScale.
Создание таймеров с разным масштабом
В методе create() сцены инициализируются три таймера через this.time.addEvent(). Ключевой параметр — timeScale.
this.timerEvent1 = this.time.addEvent({ delay: 5000, timeScale: 0.5 });
this.timerEvent2 = this.time.addEvent({ delay: 5000, timeScale: 1.0 });
this.timerEvent3 = this.time.addEvent({ delay: 5000, timeScale: 2.0 });
Хотя задержка у всех одинаковая (5000 мс), из-за timeScale фактическое время работы будет разным:
- Таймер 1 (timeScale: 0.5): выполнится за 10 секунд.
- Таймер 2 (timeScale: 1.0): выполнится за 5 секунд.
- Таймер 3 (timeScale: 2.0): выполнится за 2.5 секунды.
Также создается объект Graphics для отрисовки визуализации.
Визуализация прогресса: обновление и отрисовка
В методе update() каждый кадр очищается холст Graphics и рисуются три циферблата, отражающие состояние каждого таймера.
update ()
{
this.graphics.clear();
this.drawClock(150, 300, this.timerEvent1);
this.drawClock(400, 300, this.timerEvent2);
this.drawClock(650, 300, this.timerEvent3);
}
Метод drawClock() принимает координаты для отрисовки и объект таймера. Он рисует два вида стрелок:
- Красная стрелка: отображает общий прогресс (getOverallProgress()), если таймер повторяется (repeat > 0). В данном примере таймеры не повторяются, поэтому эта стрелка не рисуется.
- Желтая стрелка: отображает прогресс текущей итерации (getProgress()). Именно она видна на экране и показывает, какая часть задержки уже прошла.
Ключевые методы для работы с прогрессом таймера
Для получения прогресса таймера используются два метода:
- timer.getProgress(): возвращает значение от 0 до 1, где 0 — начало текущей итерации таймера, 1 — её завершение. Именно это значение определяет угол желтой стрелки.
- timer.getOverallProgress(): возвращает общий прогресс по всем повторениям таймера (если repeat задан). Значение также от 0 до 1.
В коде угол для стрелки вычисляется так:
angle = (360 * timer.getProgress()) - 90;
Умножение на 360 преобразует долю (0...1) в градусы. Вычитание 90 градусов нужно, чтобы начальное положение стрелки (при прогрессе 0) было направлено вверх (12 часов).
Для поворота точки вокруг центра используется утилита Phaser.Math.RotateAroundDistance().
Практическое применение timeScale в играх
timeScale — это не просто демо-эффект. Вот несколько практических сценариев:
1. **Слоу-мо (замедление времени):** При активации способности или получении урона можно установить timeScale: 0.3 для всех врагов или определенных событий.
2. **Пауза:** Установка timeScale: 0 для сцены или конкретной группы объектов фактически останавливает их таймеры и анимации.
3. **Ускорение:** Для спешных уровней или бонусов можно временно ускорить игровой процесс (timeScale: 1.5).
Важно: timeScale можно задавать не только отдельным событиям, но и всей временной системе сцены через this.time.timeScale. Это мгновенно повлияет на все активные таймеры и анимации, созданные через this.time и this.anims.
Что попробовать дальше
Свойство timeScale в Phaser предоставляет простой и эффективный контроль над временем в игре. Вы можете гибко управлять скоростью отдельных процессов или всей сцены, создавая динамичные и захватывающие игровые моменты.
**Идеи для экспериментов:**
1. Создайте сцену, где timeScale плавно меняется от 0.1 до 2.0 и обратно с помощью Tween.
2. Реализуйте систему паузы, которая устанавливает this.time.timeScale = 0, но при этом UI-элементы (меню паузы) остаются интерактивными.
3. Сделайте способность «часовой пузырь», которая замедляет (timeScale: 0.2) всех врагов в определенной области, оставляя игрока с нормальной скоростью.
