In this article we will discuss tick order of following Actor/Component classes:
AController
and its subclasses
APawn
and its subclasses
UPawnMovementComponent
and its subclasses
USkeletalMeshComponent
USpringArmComponent
All subclasses share the same tick order with its base class.
TL;DR
The tick order of these actor/components is demonstrated in the following graph, the rest of this article is an explanation of how this relationship is setup.
Tick Dependency
AController
, APawn
, UPawnMovementComponent
and USkeletalMeshComponent
have tick group TG_PrePhysics
, and USpringArmComponent
has tick group TG_PostPhysics
, so USpringArmComponent
is ticked after the other ones.USkeletalMeshComponent
and UCharacterMovementComponent
have some extra tick functions, we won’t discuss them here.The tick order of
AController
, APawn
, UPawnMovementComponent
and USkeletalMeshComponent
is setup by tick dependency.AController
, APawn
and UPawnMovementComponent
// Controller.cpp void AController::AddPawnTickDependency(APawn* NewPawn) { if (NewPawn != NULL) { bool bNeedsPawnPrereq = true; UPawnMovementComponent* PawnMovement = NewPawn->GetMovementComponent(); if (PawnMovement && PawnMovement->PrimaryComponentTick.bCanEverTick) { PawnMovement->PrimaryComponentTick.AddPrerequisite(this, this->PrimaryActorTick); // Don't need a prereq on the pawn if the movement component already sets up a prereq. if (PawnMovement->bTickBeforeOwner || NewPawn->PrimaryActorTick.GetPrerequisites().Contains(FTickPrerequisite(PawnMovement, PawnMovement->PrimaryComponentTick))) { bNeedsPawnPrereq = false; } } if (bNeedsPawnPrereq) { NewPawn->PrimaryActorTick.AddPrerequisite(this, this->PrimaryActorTick); } } } // MovementComponent.cpp void UMovementComponent::RegisterComponentTickFunctions(bool bRegister) { Super::RegisterComponentTickFunctions(bRegister); // Super may start up the tick function when we don't want to. UpdateTickRegistration(); // If the owner ticks, make sure we tick first AActor* Owner = GetOwner(); if (bTickBeforeOwner && bRegister && PrimaryComponentTick.bCanEverTick && Owner && Owner->CanEverTick()) { Owner->PrimaryActorTick.AddPrerequisite(this, PrimaryComponentTick); } }
In
AController::AddPawnTickDependency
and UMovementComponent::RegisterComponentTickFunctions
the following tick dependencies are setup:AController
is ticked beforeUPawnMovementComponent
.
AController
is ticked beforeAPawn
.
- By default
UPawnMovementComponent
is ticked before its owningAPawn
. But you can turn offbTickBeforeOwner
if the tick order doesn’t matter.
UpdatedComponent
and UMovementComponent
void UMovementComponent::SetUpdatedComponent(USceneComponent* NewUpdatedComponent) { ... // Assign delegates if (UpdatedComponent) { ... // force ticks after movement component updates UpdatedComponent->PrimaryComponentTick.AddPrerequisite(this, PrimaryComponentTick); } UpdateTickRegistration(); if (bSnapToPlaneAtStart) { SnapUpdatedComponentToPlane(); } }
In
UMovementComponent::SetUpdatedComponent
, UpdatedComponent
is setup to tick after UMovementComponent
, by default UpdatedComponent
is the root capsule component, but it can be assigned to other component at runtime.USkeletalMeshComponent
and UCharacterMovementComponent
void ACharacter::PostInitializeComponents() { QUICK_SCOPE_CYCLE_COUNTER(STAT_Character_PostInitComponents); Super::PostInitializeComponents(); if (!IsPendingKill()) { if (Mesh) { ... // force animation tick after movement component updates if (Mesh->PrimaryComponentTick.bCanEverTick && CharacterMovement) { Mesh->PrimaryComponentTick.AddPrerequisite(CharacterMovement, CharacterMovement->PrimaryComponentTick); } } ... } }
In
ACharacter::PostInitializeComponents
, USkeletalMeshComponent
is setup to tick after UCharacterMovementComponent
. Even without this rule a child USceneComponent
is ticked after its parent USceneComponent
.USceneComponent
Child and Parent
There is another rule in Unreal,
USceneComponent
child always ticks after its parent, this is setup in USceneComponent::AttachToComponent
.bool USceneComponent::AttachToComponent(USceneComponent* Parent, const FAttachmentTransformRules& AttachmentRules, FName SocketName) { ... if(Parent != nullptr) { ... // Detach removes all Prerequisite, so will need to add after Detach happens PrimaryComponentTick.AddPrerequisite(Parent, Parent->PrimaryComponentTick); // force us to tick after the parent does ... return true; } return false; }