定时器
在 Unreal Engine 中,定时器用在指定延迟后执行某个操作,或者以固定的频率重复执行某个操作。
在不需要高频率实时的情况下,相比于在 Tick() 函数中每帧手动计算时间累加,使用定时器更加高效而且代码结构更加清晰。
在上一篇文章中我们已经实现了玩家的开火,在这篇文章中我们实现炮塔的开火。炮塔的开火是 "AI" 行为,需要我们编写逻辑控制。我们准备使用定时器实现,每隔一段时间检查玩家是否在开火范围内,如果在,就开火。
我们先看代码清单 1 和清单 2 的实现。
- UCLASS()
- class TOONTANKS_API ATower : public ABasePawn
- {
- GENERATED_BODY()
- private:
- ATank* Tank;
- UPROPERTY(EditDefaultsOnly, Category = "Combat")
- float FireRange = 300.0f;
- FTimerHandle FireRateTimerHandle;
- float FireRate = 2.0f;
- void CheckFireCondition();
- bool InFireRange();
- };
- // Called when the game starts or when spawned
- void ATower::BeginPlay()
- {
- Super::BeginPlay();
- Tank = Cast<ATank>(UGameplayStatics::GetPlayerPawn(this, 0));
- GetWorldTimerManager().SetTimer(FireRateTimerHandle, this, &ATower::CheckFireCondition, FireRate, true);
- }
- void ATower::CheckFireCondition()
- {
- if (InFireRange())
- {
- Fire();
- }
- }
- bool ATower::InFireRange()
- {
- if (Tank)
- {
- float Distance = FVector::Dist(GetActorLocation(), Tank->GetActorLocation());
- return (Distance <= FireRange);
- }
- return false;
- }
定时器系统由全局的 FTimerManager 管理,可以通过 GetWorld()->GetTimerManager() 访问到它。
FTimerHandle 是一个轻量级的结构体,充当定时器的唯一标识符。我们需要保存这个句柄,以便之后暂停、恢复或取消这个定时器。
我们可以使用 SetTimer() 函数,让定时器定时执行指定的函数。它的一个重载版本如下:
- template< class UserClass >
- FORCEINLINE void SetTimer(
- FTimerHandle& InOutHandle,
- UserClass* InObj,
- typename FTimerDelegate::TMethodPtr< UserClass > InTimerMethod,
- float InRate,
- bool InbLoop = false,
- float InFirstDelay = -1.f);
其中,InOutHandle 参数是引用传递,用于存储新创建定时器的唯一 ID。
InObj 参数是回调函数的对象指针,此处我们传递 this。
InTimerMethod 参数指定回调函数的成员函数指针。
InRate 参数指定定时器触发的时间间隔。
InbLoop 参数指定是否循环触发。
InFirstDelay 参数指定第一次触发等待的时间。默认值 -1,表示等待时间为 InRate。