Fancier Ticking in Unreal
Unreal’s ticking system is a core part of the engine, fulfilling the role of the classic game loop. Despite being so central, though, I’ve found it to be a bit complex, with lots of flags and knobs that aren’t always very well documented. And while there are several resources that explain how to do basic ticking, there are a few fancier techniques that don’t have as much coverage, which this page aims to help explain.
I’ll primarily be focusing on usage in C++, but many of these options are also available in Blueprint (albeit not all). At the time of writing, the latest version of Unreal is 5.4, so that’s what this will be based on.
The official documentation on Actor ticking is a good introduction to the core concepts.
Tick configuration
If you’ve used Unreal for any length of time, you’ve probably run across C++
code that enables and configures ticking for an Actor or ActorComponent. This is
typically done inside the constructor so that it applies to the
CDO and is set correctly before
BeginPlay
. It’ll look something like this:
AMyActor::AMyActor()
{
PrimaryActorTick.bCanEverTick = true;
PrimaryActorTick.bStartWithTickEnabled = true;
PrimaryActorTick.TickInterval = 1.0f; // Only tick every second.
}
This PrimaryActorTick
field is an instance of an FTickFunction
struct, which
is the core API for configuring ticking in Unreal. Actors and ActorComponents
define a default member struct (PrimaryActorTick
and PrimaryComponentTick
,
respectively, which are slightly specialized subclasses of FTickFunction
) and
register it with the engine during BeginPlay
.1
You might see several other functions present on Actors and ActorComponents that
refer to ticking (such as AActor::SetActorTickEnabled
or
AActor::CanEverTick
), but most of these are just convenience methods that
operate on the respective FTickFunction
, which is the source of truth.
The following are some of the most common or interesting fields you can set on
an FTickFunction
(but this list is not exhaustive). The defaults are the same
for both Actors and ActorComponents, unless otherwise specified.
Field | Default (Actor / ActorComponent) | Usage |
---|---|---|
bCanEverTick |
false | Whether ticking is ever possible. If this is false, the other flags are ignored. Think of this as a setup flag rather than a runtime flag. |
bStartWithTickEnabled |
true | Whether ticking begins upon creation. |
TickInterval |
0.0f | The interval, in seconds, between ticks. If <= 0, or less than frame time, will tick every frame. |
TickGroup |
TG_PrePhysics / TG_DuringPhysics | The coarse tick group during which to tick. Semantically important.2 |
bHighPriority |
false | Whether to run this tick first inside the tick group (see section below on controlling tick order). |
bRunOnAnyThread |
false | Whether to parallelize this tick with other “async ticks” (see section bellow on parallelization). |
Some of these are fairly self-explanatory, others less so (which we’ll cover in later sections).
Tick methods
The actual methods that get called on Actors and ActorComponents look something like this. For whatever reason, the names and signatures are different.
// For Actors
void AMyActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// ...
}
// For ActorComponents
void UMyComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// ...
}
In either case, DeltaTime
is the time in seconds since the last tick (and this
will be affected by your TickInterval
, if it isn’t 0).
Toggling tick at runtime
There are cases where your tick function will only sometimes need to do useful work. For example, perhaps you have an ActorComponent that does a visual animation by repeatedly modifying a material parameter, but it only needs to do this when the animation is active.
For performance reasons, instead of just early-exiting when the animation isn’t
active, you can dynamically enable and disable ticking using
SetActorTickEnabled
and SetComponentTickEnabled
.3
UMyAnimationComponent::UMyAnimationComponent()
{
PrimaryComponentTick.bCanEverTick = true;
// Start with ticking off.
PrimaryComponentTick.bStartWithTickEnabled = false;
}
void UMyAnimationComponent::BeginAnimation()
{
SetComponentTickEnabled(true);
ResetAnimationParams();
}
void UMyAnimationComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// Do the animation ...
AnimationMaterial->SetScalarParameterValue(FName("Color"), GetLerpedColor());
// Disable tick once done.
if (IsAnimationDone(DeltaTime))
{
SetComponentTickEnabled(false);
}
}
Controlling tick order
Sometimes you’ll want to control the order that Actors and ActorComponents tick relative to each other because there’s some dependency between them (either explicit or implicit).
Tick groups
The most common way to control tick timing is via tick groups, which let you assign the “phase” of the frame you want to tick in. While a bit coarse, this gives you a simple way to control when ticks occur. The different available tick groups have important consequences around how up-to-date physics and camera state are, among other things, so controlling order isn’t the only reason you might want to change tick groups. The Unreal docs give more details on the available tick groups and when you might want to use them.
The default tick group is different for Actors and ActorComponents:
- Actors:
TG_PrePhysics
- ActorComponents:
TG_DuringPhysics
Prerequisites
Sometimes you might need more precise control of tick order, even within the
same group. For this, both Actors and ActorComponents have the ability to add
prerequisite Actors and ActorComponents via the AddTickPrerequisiteActor
and
AddTickPrerequisiteComponent
methods.
These take an actual AActor*
or UActorComponent*
pointer respectively, and
configure the ticking system so that the prerequisite Actor/Component gets
ticked first. This can usually be done in BeginPlay
or even later at runtime,
as new Actors or ActorComponents are created. For example, here’s how you’d make
an ActorComponent tick after its owner Actor.
void UMyComponent::BeginPlay()
{
Super::BeginPlay();
AddTickPrerequisiteActor(GetOwner());
}
High priority
The last way you can control ordering is by setting certain tick functions as high priority.
There are two ways to do this: either set bHighPriority
, or call
SetPriorityIncludingPrerequisites
. If your Actor/Component has prerequisites,
it’s usually best to set priority using SetPriorityIncludingPrerequisites
after configuring said prerequisites.
UMyComponent::UMyComponent()
{
PrimaryComponentTick.bCanEverTick = true;
// Set high priority on the tick function.
PrimaryComponentTick.bHighPriority = true;
}
void UMyComponent::BeginPlay()
{
Super::BeginPlay();
AddTickPrerequisiteActor(GetOwner());
// Or set high priority after configuring prerequisites.
PrimaryComponentTick.SetPriorityIncludingPrerequisites(true);
}
The result is very simple: for each tick group, tick functions are separated into two sets, normal priority or high priority, and the high priority ones are executed first.
Actor/Component tick order
At a high level, there’s a precedence between the different ways of controlling tick order:
- Actor/Component prerequisites are most important, and can even override tick groups.
- Next,
TickGroup
controls the tick group a tick function runs in. - Finally,
bHighPriority
controls when ticks run within a tick group (either in the “high priority” set, or normal set).
It’s important to note that generally there is no inherent tick ordering between Actors and their ActorComponents! If you need guaranteed ordering, you should use one of the techniques above to configure it.
The main exception to this are SceneComponents, which automatically add their parent attachments as prerequisites, causing parent SceneComponents to tick before children.
Parallel tick
Normally, tick functions are called synchronously on the game thread, one at a time. Naturally, this can limit performance when there’s a lot of work to do.
Unreal lets you configure some of your tick functions so that they execute
asynchronously and in parallel. You can opt into this by setting
bRunOnAnyThread
to true
, which will cause Unreal to run your tick functions
inside parallel async tasks. As with any kind of multithreading, this requires
care to not run into synchronization issues, so you should only use it when
necessary.
Note that Unreal will still wait for your tick to finish before moving on to
the next tick group if it runs out of other non-async tick functions to run (so,
your async tick still can’t take longer than a frame without causing hitching).
There’s an additional field you can use, EndTickGroup
, to control when Unreal
will block on your tick function’s completion (by default, this is the same as
whatever you set TickGroup
to).
Here’s a simple example:
UMyComponent::UMyComponent()
{
PrimaryComponentTick.bCanEverTick = true;
// This component's tick will be called asynchronously and in parallel.
// It will be started during TG_PrePhysics and will have until TG_PostUpdateWork to complete its work.
PrimaryComponentTick.TickGroup = TG_PrePhysics;
PrimaryComponentTick.EndTickGroup = TG_PostUpdateWork;
PrimaryComponentTick.bRunOnAnyThread = true;
}
FTickableGameObject
At times you may be working with a class that isn’t an Actor or ActorComponent
(for example, a plain old C++ class) but which you want to tick. Unreal lets you
enable ticking for any C++ class using FTickableGameObject
.4 Simply inherit
from it and implement the two required methods Tick
(the important one) and
GetStatId
(used for performance monitoring), and voila.
Here is a basic example:
#include "Tickable.h"
class FMyTickableObject : public FTickableGameObject
{
public:
virtual void Tick(float DeltaTime) override
{
// ...
}
virtual TStatId GetStatId() const override
{
RETURN_QUICK_DECLARE_CYCLE_STAT(FMyTickableObject, STATGROUP_Tickables);
}
}
A few important notes and limitations to keep in mind:
FTickableGameObject
doesn’t use a typicalFTickFunction
under the hood, which means you’ll need to implement some tick features yourself (for example, since there is noTickInterval
you’ll need to throttle yourself by taking into accountDeltaTime
, if you need that feature).- Tick groups aren’t supported; instead, all
FTickableGameObject
objects are ticked together after the normal tick groups at the end of the frame. This means, for example, that you can’t affect the current frame’s physics from anFTickableGameObject
. - By default, UObjects that inherit from
FTickableGameObject
will run their tick functions even in the CDO. To avoid this, you can overrideIsTickable
orGetTickableTickType
and check the object’s flags to filter out CDOs.
-
You can also define your own
FTickFunction
and register it yourself, or even have more than one, but in most cases the default primary tick function should suffice. ↩︎ -
The Unreal docs give some examples of how you might choose which tick group to run in, depending on what you need. For example, tick logic that moves Actors with colliders should run in TG_PrePhysics so that they affect the physics simulation. ↩︎
-
You might be tempted to try using
bCanEverTick
to turn ticking on/off dynamically at runtime, but this doesn’t work, as there’s a bit of extra bookkeeping that needs to be updated. ↩︎ -
Incidentally, this is used internally by the engine to implement ticking for some common UObject classes like
UTickableWorldSubsystem
. ↩︎