Implement a generic ability cooldown system in C++ for Unreal Engine.
Implement a generic ability-cooldown component keyed by ability id. It must report whether an ability is ready, consume a use when one fires, and survive variable frame rates without a per-frame counter.
Requirements:
against the cooldown duration — do not count down in Tick or by frame count.
records the timestamp and returns true.
- Gate readiness by comparing world timestamps (
GetWorld()->GetTimeSeconds()) - An ability never used yet is ready (no recorded timestamp ⇒ allowed).
TryUsereturnsfalseand changes nothing if still cooling down; otherwise it
Complete the two member functions (LastUsedTime is a TMap<FName, float>):
bool UCooldownComponent::IsReady(FName AbilityId, float CooldownSeconds) const
{
// your code here
}
bool UCooldownComponent::TryUse(FName AbilityId, float CooldownSeconds)
{
// your code here
}
Write the implementation.
Record the world time when an ability fires, then gate the next use by checking elapsed time against the cooldown. Use GetWorld()->GetTimeSeconds() for the timestamp — comparing times avoids a Tick counter and survives variable frame rates.
- ✗Counting down in
Tickinstead of comparing timestamps — drifts with frame rate - ✗Using frame counts instead of seconds — breaks at variable FPS
- ✗Forgetting the first use should pass when no timestamp has been recorded yet
- →How would you expose remaining cooldown time to drive a UI radial fill?
- →How would you pause cooldowns when the game is paused?
Generic cooldown system
// CooldownComponent.h
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class UCooldownComponent : public UActorComponent
{
GENERATED_BODY()
public:
// true if the ability is ready right now
UFUNCTION(BlueprintCallable, Category="Cooldown")
bool IsReady(FName AbilityId, float CooldownSeconds) const;
// called when an ability fires — starts the cooldown
UFUNCTION(BlueprintCallable, Category="Cooldown")
bool TryUse(FName AbilityId, float CooldownSeconds);
// remaining time for UI (0 if ready)
UFUNCTION(BlueprintCallable, Category="Cooldown")
float GetRemaining(FName AbilityId, float CooldownSeconds) const;
private:
// world-time stamp of each ability's last use
TMap<FName, float> LastUsedTime;
};
// CooldownComponent.cpp
bool UCooldownComponent::IsReady(FName AbilityId, float CooldownSeconds) const
{
const float* Last = LastUsedTime.Find(AbilityId);
if (!Last)
{
return true; // never used — ready
}
const float Now = GetWorld()->GetTimeSeconds();
return (Now - *Last) >= CooldownSeconds;
}
bool UCooldownComponent::TryUse(FName AbilityId, float CooldownSeconds)
{
if (!IsReady(AbilityId, CooldownSeconds))
{
return false; // still cooling down — use rejected
}
LastUsedTime.Add(AbilityId, GetWorld()->GetTimeSeconds());
return true;
}
float UCooldownComponent::GetRemaining(FName AbilityId, float CooldownSeconds) const
{
const float* Last = LastUsedTime.Find(AbilityId);
if (!Last)
{
return 0.f;
}
const float Elapsed = GetWorld()->GetTimeSeconds() - *Last;
return FMath::Max(0.f, CooldownSeconds - Elapsed);
}
The system is Tick-free: it compares world timestamps, so it behaves identically at any frame rate and scales to any number of abilities through FName keys.