Using C++ Timers in Unreal Engine
Timers are incredibly helpful for gameplay programming in Unreal Engine. However, the syntax can be a little tricky if you’re unfamiliar with C++. This blog post will cover all the essential features and syntax for using C++ timers effectively in your game.
For a code example of timers, check out my C++ Action Roguelike (delegate with parameters). Timers as part of game performance are covered in my game optimization course. More on that later.
Set Timer
You set timers through the global timer manager which is available through GetWorld()->GetTimerManager()
or the shorthand available in any Actor, GetWorldTimerManager()
which returns the same timer manager. There are a couple of overloads (function variations) available to pass the function to execute, the interval between triggers (if looped), a flag to enable looping, and the optional first delay. You can also set a timer to run the next frame by calling SetTimerForNextTick()
.
Code sample from my Survival Game BombActor where we set a timer for a delayed explosion:
/* Activate the fuze to explode the bomb after several seconds */
void ASBombActor::OnUsed(APawn* InstigatorPawn)
{
GetWorld()->GetTimerManager().SetTimer(
FuzeTimerHandle, // handle to cancel timer at a later time
this, // the owning object
&ASBombActor::OnExplode, // function to call on elapsed
MaxFuzeTime, // float delay until elapsed
false); // looping?
}
The FTimerHandle
is in the header file. Although you are not required to keep a reference to the handle, it’s recommended to put this in your header to properly clear or pause your timer instance.
/* Handle to manage the timer */
FTimerHandle FuzeTimerHandle;
The function OnExplode()
has no parameters in this example. To pass along parameters on timer elapsed, there is a different way to bind the function…
Using SetTimer() on a Function with Parameters
It’s possible to pass parameters into timer functions (delegates). The example is from Action Roguelike’s Projectile Attack. In this case, we bind the function by name instead.
FTimerHandle TimerHandle_AttackDelay;
FTimerDelegate Delegate; // Delegate to bind function with parameters
Delegate.BindUFunction(this, "AttackDelay_Elapsed", Character); // Character is the parameter we wish to pass with the function.
GetWorld()->GetTimerManager().SetTimer(TimerHandle_AttackDelay, Delegate, AttackAnimDelay, false);
To bind functions with parameters to a timer, it must be specified in the header with UFUNCTION()
. You can remember this since the FTimerDelegate
from the above example calls a function literally named .BindUFunction()
.
UFUNCTION()
void AttackDelay_Elapsed(ACharacter* InstigatorCharacter);
Clearing Timer(s)
When destroying or deactivating objects, make sure you clear any active timers. There are two ways of dealing with timer removal. You don’t need to do this for timers that have elapsed and aren’t looping.
void ASBombActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
// Ensure the fuze timer is cleared by using the timer handle
GetWorld()->GetTimerManager().ClearTimer(FuzeTimerHandle);
// Alternatively you can clear ALL timers that belong to this (Actor) instance.
GetWorld()->GetTimerManager().ClearAllTimersForObject(this);
}
Debugging & Profiling
You can dump the currently active timers by using the listtimers
console command. You can also dump expensive timer functions to the log by setting TimerManager.DumpTimerLogsThreshold
to anything higher than 0 (where the number is the time threshold in seconds)
When does it Tick?
The TickManager itself ticks between TickGroups. Specifically, between PostPhysics and PostUpdateWork. That’s relatively late in the GameThread frame. Keep this in mind for any potential dependencies as you might introduce a one-frame delay if implicit dependent objects have already ticked that frame.
Advanced Considerations
There are a couple of things to consider that aren’t immediately obvious and/or are more relevant once you are optimizing your game.
High-Frequency Timers
It’s important to note that while you can run very high-frequency timers, these do not actually run asynchronously or on a higher ‘real’ framerate than your game. Let’s say your game runs on 60 FPS and you have a timer on 0.005 looping intervals. That’s about 200 times per second, internally it will still run approx. 200 times per second even at 60 frames per second! It’s important to realize though that this will execute multiple times in a loop, immediately after each other, and NOT spaced out smoothly every 0.005.
It will instead run about 3 times per frame in a burst, which is just a waste of execution and could be done 1 time per frame with a higher DeltaTime to compensate.
New in 5.4: ‘Max Once Per Frame’ to avoid the catch-up behavior where the timer may be called multiple times per frame.
In C++ this is available too of course, but is a bit more hidden inside FTimerManagerTimerParameters
with bMaxOncePerFrame
. Here is the example usage I could find in the engine:
// Example found in UKismetSystemLibrary::K2_SetTimerDelegate
TimerManager.SetTimer(Handle, Delegate, Time,
FTimerManagerTimerParameters { .bLoop = bLooping, .bMaxOncePerFrame = bMaxOncePerFrame }); // Creates the FTimerManagerTimerParameters struct inline
Frame Pacing
You should never use TickManager as an excuse to not optimize badly performing code or naturally expensive operations. Running them as timer functions on a lower frequency may cause an instable framerate rather than smooth performance which hinders player experience. There are a couple of alternatives such as time slicing (spreading the workload across multiple frames) or running the entire function asynchronous using Unreal’s Task System.
(Lack of) CPU Cache
If you are updating many of the same objects using the TickManager using individual timers, you have no control over their execution order. Much like Unreal’s standard Ticking system. There are enormous performance benefits to maximizing the CPU instruction and data cache. While this is out of the scope of this article, the most relevant resource to learn more is UnrealFest’s Aggregating Ticks to Manage Scale in Sea of Thieves. Consider combining many small timer functions into a single manager to maximize performance.
Closing
Timers are great for triggering delayed events and handling other time-based events that you may be inclined to put in your Tick() function instead. Be mindful to not abuse this convenient solution in places where you really need a more well considered system.
Interested to learn more in-depth about performance & optimization concepts like timers, tickgroups, frame pacing, cpu cache, and many more? I just launched my Complete Game Optimization Course for Unreal Engine 5!
Check out the API for FTimerManager API Documentation for more useful functions on timers. Including time remaining, finding the tick rate, active timers, pause/continue, etc.
I’m surprised there’s no mention of lambda functions in C++ timers. They’re great for defining small pieces of functionality without needing separate named functions. For example:
Example:
GetWorldTimerManager().SetTimer(TimerHandle_ResetShield, [this]()
{
// Access the Flipbook’s material and set ‘SetBlinking’ to false
if (IsValid(DynamicMaterial))
{
DynamicMaterial->SetScalarParameterValue(FName(“SetBlinking”), 0.0f);
}
// Reset shield status
bShieldWasActive = false;
bShieldActive = false;
bCharacterHurt = false;
}, 1.0f, false);
I appreciate that the article is kept up to date though! Having the bMaxOncePerFrame parameter in UE 5.4 is great.
Good point, lambdas can be very convenient at times!
Thanks for this article!!!
Thanks for the coupon on your course Tom, looks very useful. I have been coding my own UE4 C++ stuff for over a year but only noticed the timers today in the content examples. Going to do your course in case I’m missing out on any other useful tricks.
Great to hear! Happy learning! ;)
– Tom
Having some trouble. Have a Timer in my ACharacter class and it gives me the following error Images: https://ibb.co/gYycdy https://ibb.co/kYwkWJ I really don’t know what I should do. I’ve already included World.h and TimerManager.h, world is null but I don’t understand why. Please help me!
Make sure that you are not using the GetWorldTimerManager().SetTimer(…) in the Constructor of the class you are trying to bind the function for. I found after a ton of time that it is not corrupted, and that rather you just need to move it into the BeginPlay() and it is ok. If you cannot open the Editor (via the launcher): 1. Move/comment the code out of your constructor 2. Recompile the project 3. Open via the editor.
My best guess is that in the editor, it sees a null_ptr which is causing the crashes (from a decent amount of googling) when the GetWorldTimerManager().SetTimer(…) is in the constructor.
For those of you saying the code crashes. Make sure the function you’re binding to is an UFUNCTION(), I’m not sure if it’s explicitly said in any tutorial or official documentation itself, but I found out it was crashing if it’s not and UFUNCTION().
Best way to discover that kind of crashes is to launch the editor from VS (DebugGame Editor & Win64, compile and Play), so when editor crashes it throws a breakpoint in VS and you can sometimes figure out what’s going on based on call stack and some keywords.
Hope it helps.
Good tutorial btw, not much info about that topic.
Thanks for clarifying this!
– Tom
No problem at all. Hope it helps if someone gets stuck with it as I did!
Thank you for your posts, they’re nice.
Regards.
Exactly, which is what I did, just did a check for a nullpointer. But I’m curious as well as to what point in the init process, that a plugin, can start assuming that the system is indeed loaded loaded enough to make calls.
I mean if GEngine is nullptr then we know nothing is going to be happy! lol
Thank you Tom, very much appreciated.
There is a problem in World.H, at GEngine->GetWorld()->GetTimerManager(), in that when it’s being called, when the editor is loading and the call is being made from a static library for blueprints, i.e. a plugin. Yeah, it blows up with a C5, null pointer in some fashion. Haven’t had the time to debug yet. Not really for sure why it’s upset, there is very little code in the function. It is just checking for GameInstance. This is on 4.10.4
Could very well happen GetWorld() returns null when editor is still loading.
This code crashes the editor and corrupts the project. It is impossible to recover. Be sure to backup first.
That sounds highly unlikely mate. When in doubt, remove and recompile and see what happens. There really isn’t much going on in this small piece of code that can touch or corrupt your project, especially unlikely to make it impossible to recover.
What else did you change? Can you recompile the project successfully and re-open the editor? More importantly, what line of code does the editor crash on specifically, with more information I can try to help.
– Tom
This might sound crazy but it happened to me too. No matter what UE4 crashes on launch. To fix it I had to open Visual Studio, remove the setTimer code, and then rebuild the project to get UE4 to launch without crashing.
What engine version are you guys using? Without specific information like the exact error and line it’s failing on there isn’t much I can help you guys with.
– Tom