定时器管理

定时器在 全局定时器管理器FTimerManager 类型)中管理。全局定时器管理器存在于 游戏实例 对象上以及每个 场景 中。
如果要对其调用定时器的对象(如 Actor)在时间结束前被销毁,则相关定时器会自动取消。在此情况下,定时器句柄将变为无效,并且不会调用该函数。

  • 定时器句柄(**FTimerHandle** 类):可用于暂停(和恢复)倒计时,查询或更改剩余时间,甚至可以取消定时器。
  • 访问定时器管理器:可以使用 AActor 函数 **GetWorldTimerManager()**,它会在 UWorld 中调用 GetTimerManager() 函数,来访问 FTimerManager
  • 访问全局定时器管理器:使用 UGameInstance 函数 GetTimerManager()。如果场景因为任何原因而没有自己的定时器管理器,也可以退而求其次,使用全局定时器管理器。全局管理器可以用于与任何特定场景的存在没有相关性或依赖性的函数调用。
  • 使用定时器管理器来设置定时器SetTimerSetTimerForNextTick,它们各自都有一些重载。每个函数都可以连接到任意类型的对象或函数委托,SetTimer 可以设为根据需要定期重复。请参阅定时器管理器 API 页面以了解有关这两个函数的更多详细信息。
  • 定时器可以与标准的 C++函数指针、TFunction对象委托一起使用。

设置和清空定时器

以下函数为 FTimerManager 类的成员函数,用 GetWorldTimerManager() 即可调用:

  • SetTimer() :将定时器设置为在一段延迟后调用函数或委托
    • 可以设置为不限次重复调用该函数。该函数将填充 定时器句柄FTimerHandle 类型)
    • 使用现有定时器句柄调用 SetTimer 将清空该定时器句柄引用的定时器,并将它换成新定时器。
  • SetTimerForNextTick设置定时器在下一帧运行,而不是按固定间隔。但需要注意的是,该函数不填充定时器句柄
  • ClearTimer:清空定时器,将 SetTimer 期间填充的 FTimerHandle 传递到  ClearTimer 中。定时器句柄将在此刻失效,并可以再次用于管理新定时器。
  • ClearAllTimersForObject清空所有与特定对象关联的所有定时器。

C++实现:

1
2
3
//声明
int32 Count = 3;
FTimerHandle TimerHandle;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void ACountdown::BeginPlay()
{
Super::BeginPlay();
// 5.0s后,每1.0s调用一次回调函数
GetWorldTimerManager().SetTimer(TimerHandle, this, &ACountdown::TimerCallback, 1.0f, true, 5.0f);
}

void ACountdown::TimerCallback()
{
//回调执行三次后,清空定时器
--Count;
if(Count<=0)
{
GetWorldTimerManager().ClearTimer(TimerHandle);
}
}

蓝图等价实现:
Pasted image 20231202161427

[!warning]
以小于等于 0 的速率调用 SetTimer 等效于调用 ClearTimer

暂停和恢复定时器

**PauseTimer**:使用定时器句柄来暂停正在运行的定时器。这样可阻止定时器执行其函数调用,但经过的时间和剩余时间将保持暂停时的状态。
**UnPauseTimer**:使暂停的定时器恢复运行。

定时器信息

除了管理定时器,定时器管理器还提供了用于获取特定定时器信息的函数,如速率、经过的时间和剩余时间等。

定时器是否活跃

  • **IsTimerActive**:用于确定指定定时器当前是否活跃且未暂停。
1
2
// 这个武器是否正在等待再次射击?
GetWorldTimerManager().IsTimerActive(this, &AUTWeapon::RefireCheckTimer);

定时器速率

  • GetTimerRate :用于从定时器句柄获取定时器的当前速率(两次激活之间的时间)。
    • 定时器速率不能直接更改,但可以使用其定时器句柄调用 SetTimer 来清空定时器并创建新定时器,新定时器除了速率不同,其他保持不变。来模拟更改定时器速率。
    • 如果定时器句柄无效,则 GetTimerRate 将返回值 -1
1
2
// 该武器的射击速率在预热时变化。当前是否正在等待射击,如果是,两次射击之间的当前间隔是多久?
GetWorldTimerManager().GetTimerRate(this, &AUTWeapon::RefireCheckTimer);

经过时间和剩余时间

  • GetTimerElapsed :返回与定时器句柄关联的定时器的经过时间
  • GetTimerRemaining:返回与定时器句柄关联的定时器的剩余时间
  • 如果定时器句柄无效,则这两个函数将返回 -1
  • 定时器的经过时间和剩余时间之和应该等于定时器的速率
1
2
// 该武器准备好再次射击之前将经过多长时间?如果答案为-1,则表示现在已准备就绪。
GetWorldTimerManager().GetTimerElapsed(this, &AUTWeapon::RefireCheckTimer);

理解 DeltaTime

UE 世界坐标单位是 cm
涉及移动旋转计算时,我们通常声明一个常量乘以 DeltaTime,原理如下:

1
2
3
4
5
6
7
8
9
10
void AMyClass::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
float MovementRate = 50.0f; //移动速度为50cm/s
float RotationRate = 45.0f; //旋转速度为45°/s
//Speed * DeltaTime (cm/s)*(s/frame) = (cm/frame) 即每帧移动多少cm
//通过这种方法可以保证在不同帧率下移动速度一致
AddActorWorldOffset(FVector(0.f, 0.f, MovementRate * DeltaTime));
AddActorWorldRotation(FRotator(0.f, RotationRate*DeltaTime, 0.f));
}