Animation system
A USkeletalMeshComponent is animated by a UAnimInstance — the runtime behind an
Animation Blueprint. The recommended architecture is a C++ UAnimInstance base that
computes animation variables each frame, with the AnimBP's AnimGraph consuming those
variables to produce the final pose.
When to use this skill
- Setting up a character's locomotion (idle, walk, run, jump) with state machines and blend
spaces driven from C++.
- Playing one-off animations (attacks, reloads, hit reactions) via montages with section
control and completion delegates.
- Firing gameplay events (footsteps, hit windows, VFX triggers) at precise animation frames
using custom anim notifies.
- Switching animation sets at runtime with linked anim layers /
LinkAnimClassLayers.
- Integrating the Pose Search (Motion Matching) or Motion Warping plugins.
Core mental model
| Thread |
What runs there |
What to do there |
| Game thread |
NativeInitializeAnimation, NativeUpdateAnimation, event graph |
Cache references, compute simple vars |
| Anim worker thread |
NativeThreadSafeUpdateAnimation, AnimGraph evaluation |
Heavy per-frame logic (read-only, no world queries) |
The AnimGraph evaluates the pose (state machines → blend spaces → IK → final pose). It
runs on the anim worker thread and must only read data the game thread wrote.
The C++ update path computes the variables the graph reads — never drive the final
bone transform directly from game code.
C++ AnimInstance base
// MyAnimInstance.h
#pragma once
#include "Animation/AnimInstance.h"
#include "MyAnimInstance.generated.h"
UCLASS()
class MYGAME_API UMyAnimInstance : public UAnimInstance
{
GENERATED_BODY()
public:
virtual void NativeInitializeAnimation() override;
virtual void NativeUpdateAnimation(float DeltaSeconds) override;
virtual void NativeThreadSafeUpdateAnimation(float DeltaSeconds) override;
// Read by AnimGraph nodes (BlueprintReadOnly keeps them graph-accessible, thread-safe)
UPROPERTY(BlueprintReadOnly, Category="Locomotion") float Speed = 0.f;
UPROPERTY(BlueprintReadOnly, Category="Locomotion") float Direction = 0.f;
UPROPERTY(BlueprintReadOnly, Category="Locomotion") bool bIsFalling = false;
private:
UPROPERTY() TObjectPtr<class ACharacter> OwnerCharacter;
};
// MyAnimInstance.cpp
#include "MyAnimInstance.h"
#include "GameFramework/Character.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "KismetAnimationLibrary.h" // CalculateDirection
void UMyAnimInstance::NativeInitializeAnimation()
{
Super::NativeInitializeAnimation();
OwnerCharacter = Cast<ACharacter>(TryGetPawnOwner()); // cache once; safe on game thread
}
void UMyAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
Super::NativeUpdateAnimation(DeltaSeconds);
// Keep lightweight — prefer NativeThreadSafeUpdateAnimation for heavy logic
if (!OwnerCharacter) { OwnerCharacter = Cast<ACharacter>(TryGetPawnOwner()); }
}
void UMyAnimInstance::NativeThreadSafeUpdateAnimation(float DeltaSeconds)
{
Super::NativeThreadSafeUpdateAnimation(DeltaSeconds);
if (!OwnerCharacter) { return; }
const FVector Vel = OwnerCharacter->GetVelocity();
Speed = Vel.Size2D();
bIsFalling = OwnerCharacter->GetCharacterMovement()->IsFalling();
Direction = UKismetAnimationLibrary::CalculateDirection(Vel, OwnerCharacter->GetActorRotation());
}
Key rules:
NativeInitializeAnimation — cache owner/movement references; runs once on game thread.
NativeUpdateAnimation — game-thread update; keep minimal; call Super first.
NativeThreadSafeUpdateAnimation — worker-thread update; no UWorld queries, no spawning,
no non-thread-safe engine calls. This is where to put heavy per-frame computation.
- Assign variables used by the AnimGraph as
UPROPERTY(BlueprintReadOnly) — the AnimGraph
nodes read them by name. BlueprintThreadSafe meta is needed if accessed in thread-safe
graph functions.
Assign at runtime:
GetMesh()->SetAnimInstanceClass(MyAnimBPClass); // TSubclassOf<UAnimInstance>
UMyAnimInstance* AI = Cast<UMyAnimInstance>(GetMesh()->GetAnimInstance());
Animation assets
| Asset |
Class |
Use |
| Animation Sequence |
UAnimSequence |
Single clip bound to a skeleton |
| Blend Space (2D) |
UBlendSpace |
Blend clips by two parameters (speed × direction) |
| Blend Space 1D |
UBlendSpace1D |
Blend clips by one parameter (speed) |
| Aim Offset |
UAimOffsetBlendSpace |
Additive aim-offset by pitch/yaw |
| Montage |
UAnimMontage |
Sectioned one-off animations with slot blending |
| Composite |
UAnimComposite |
Stitch sequences into one timeline |
| Pose Asset |
UPoseAsset |
Curve-driven morph targets / facial poses |
All assets target a USkeleton — clips are shareable across meshes that use the same
skeleton (or compatible skeletons; see USkeleton::CompatibleSkeletons).
State machines
State machines in the AnimGraph define locomotion or combat states. Each state holds an
animation graph sub-network; transitions carry rule expressions.
From C++, query state machine state via FAnimNode_StateMachine:
GetCurrentStateName() — FName of the active state.
GetStateWeight(int32 StateIndex) — blend weight of a state during transition.
Prefer driving transitions through UPROPERTY variables computed in the C++ update rather
than calling native state machine APIs directly.
Montages (actions on top of locomotion)
Montages play in a named slot the AnimGraph exposes. The slot node blends the montage
over the base locomotion pose — good for attacks, reloads, hit reactions:
UAnimInstance* AI = GetMesh()->GetAnimInstance();
// Play and get the length (or set ReturnValueType to MontageLength / Duration)
float Len = AI->Montage_Play(AttackMontage, 1.f);
// Jump to / stop sections
AI->Montage_JumpToSection(FName("Combo2"), AttackMontage);
AI->Montage_Stop(0.2f, AttackMontage);
// ACharacter convenience wrappers
PlayAnimMontage(AttackMontage, 1.f, FName("Intro"));
StopAnimMontage(AttackMontage);
Bind to completion to know when an action finishes:
AI->OnMontageEnded.AddDynamic(this, &AMyChar::OnMontageEnded);
// or per-instance:
FOnMontageEnded EndDelegate;
EndDelegate.BindUObject(this, &AMyChar::OnMontageEnded);
AI->Montage_SetEndDelegate(EndDelegate, AttackMontage);
See references/montages-and-slots.md for section authoring,
root motion, blend settings, and Montage_PlayWithBlendSettings.
Anim Notifies (events from animation)
UAnimNotify — a point event (footstep SFX, spawn projectile at a bone socket).
UAnimNotifyState — a ranged event with NotifyBegin/NotifyTick/NotifyEnd
(enable weapon collision during a swing).
UCLASS()
class MYGAME_API UAnimNotify_Footstep : public UAnimNotify
{
GENERATED_BODY()
virtual FString GetNotifyName_Implementation() const override { return TEXT("Footstep"); }
virtual void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation,
const FAnimNotifyEventReference& Ref) override;
};
UCLASS()
class MYGAME_API UAnimNotifyState_WeaponTrace : public UAnimNotifyState
{
GENERATED_BODY()
virtual void NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation,
float TotalDuration, const FAnimNotifyEventReference& Ref) override;
virtual void NotifyTick (USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation,
float FrameDelta, const FAnimNotifyEventReference& Ref) override;
virtual void NotifyEnd (USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation,
const FAnimNotifyEventReference& Ref) override;
};
Notifies are the correct way to synchronize gameplay to animation timing. Using timers as a
substitute diverges at variable play rates and when animation blending delays the clip.
Linked anim layers (runtime animation set swapping)
Linked anim layers let you swap a portion of the AnimGraph at runtime — useful for weapon
styles, character class variations, or costume-specific animations:
// Replace the locomotion layer at runtime
GetMesh()->LinkAnimClassLayers(URifleLocomotionLayer::StaticClass());
// Revert to the default layer defined in the AnimBP
GetMesh()->UnlinkAnimClassLayers(URifleLocomotionLayer::StaticClass());
// Retrieve the active layer instance to set variables on it
UAnimInstance* Layer = GetMesh()->GetLinkedAnimLayerInstanceByClass(URifleLocomotionLayer::StaticClass());
The linked AnimBP class must implement the same UAnimLayerInterface interface as the parent.
Modern options (5.x)
- Motion Matching (
PoseSearch plugin) — data-driven locomotion selects poses from a
database matching trajectory and pose queries. Replaces hand-built state machines for
complex locomotion. Enable PoseSearch in the project plugins.
- Motion Warping (
MotionWarping plugin) — warps root-motion clips to hit target
positions/rotations at runtime (ledge grabs, cover transitions). Add
UMotionWarpingComponent to the character; annotate montages with warp windows;
call AddOrUpdateWarpTargetFromTransform before playing.
- Control Rig in the AnimGraph — procedural bone adjustments, IK, foot planting
(see
ue-control-rig-and-ik).
- Layered blend per bone (
FAnimNode_LayeredBoneBlend) — blend an upper-body montage
slot over a lower-body locomotion pose, or blend full-body layers with per-bone masks.
- Inertialization (
AnimNode_Inertialization) — cost-efficient smooth blending by
recording velocity rather than maintaining two full pose evaluations simultaneously.
- Animation Sharing plugin — share a single AnimInstance across multiple distant characters
to reduce per-character animation cost.
Gotchas
- Non-thread-safe calls in
NativeThreadSafeUpdateAnimation — any call that touches the
UWorld, spawns objects, or reads non-thread-safe data will crash under multi-threaded anim
update. Cache all needed references in NativeInitializeAnimation.
- Wrong skeleton — clips won't apply; retarget with the IK Retargeter or add the mesh's
skeleton to
USkeleton::CompatibleSkeletons.
- Driving the pose from game code instead of the AnimGraph — fighting the animation
system. Compute data in C++; let the graph own the pose.
- Using timers for animation-timed events instead of notifies — desync at variable play
rates and when blending delays a clip's effective start.
- Forgetting to call
Super in NativeUpdateAnimation / NativeInitializeAnimation —
the engine does essential bookkeeping in the base implementation.
- Heavy game-thread logic in
NativeUpdateAnimation for many characters — move it to
NativeThreadSafeUpdateAnimation to run in parallel across characters.
- Slot with no weight in the AnimGraph — montage plays but produces no output; ensure
the slot node is wired between source and output and the blend weight is > 0.
bUseMultiThreadedAnimationUpdate disabled on the AnimBP — disables the worker-thread
path; re-enable (default on) unless you deliberately need single-threaded evaluation.
Version notes
TObjectPtr<T> is the modern member UPROPERTY form (UE5+); older code uses raw T*.
NativeThreadSafeUpdateAnimation was introduced in UE 4.15 and is stable across all
UE 5.x versions.
LinkAnimClassLayers / UnlinkAnimClassLayers replaced the deprecated SetLayerOverlay /
ClearLayerOverlay (deprecated since 4.24).
SetAnimInstanceClass (the USkeletalMeshComponent overriding version) deprecated
K2_SetAnimInstanceClass (deprecated 4.23) and a TSubclassOf setter (deprecated 5.5).
- The
PoseSearch plugin (Motion Matching) is production-ready in UE 5.4+ and ships with
the engine (no separate download). API surface stabilized significantly in 5.4.
References & source material
Engine source (UE 5.8):
Runtime/Engine/Classes/Animation/AnimInstance.h — UAnimInstance:358,
NativeInitializeAnimation:1436, NativeUpdateAnimation:1439,
NativeThreadSafeUpdateAnimation:1442, NativePostEvaluateAnimation:1444,
NativeBeginPlay:1457, Montage_Play:626, Montage_Stop:639,
Montage_JumpToSection:663, GetCurrentActiveMontage:754,
OnMontageBlendingOut:758, OnMontageEnded:770, Montage_SetEndDelegate:784,
TryGetPawnOwner:465, GetOwningActor:546, GetOwningComponent:550,
LinkAnimClassLayers:877, UnlinkAnimClassLayers:888,
GetLinkedAnimLayerInstanceByClass:910, bUseMultiThreadedAnimationUpdate:382.
Runtime/Engine/Classes/Animation/AnimMontage.h — UAnimMontage:635,
FCompositeSection:37, FSlotAnimationTrack:83, BlendIn:650, BlendOut:659,
CompositeSections:697, SlotAnimTracks:701.
Runtime/Engine/Classes/Animation/AnimSequenceBase.h — UAnimSequenceBase:36,
Notifies:43, RateScale:61, GetDataModel:243.
Runtime/Engine/Classes/Animation/AnimSequence.h — UAnimSequence:202,
bEnableRootMotion:320, RootMotionRootLock:324.
Runtime/Engine/Classes/Animation/BlendSpace.h — UBlendSpace:470,
BlendParameters:926, NotifyTriggerMode:864.
Runtime/Engine/Classes/Animation/BlendSpace1D.h — UBlendSpace1D:19.
Runtime/Engine/Classes/Animation/AnimNotifies/AnimNotify.h — UAnimNotify:51,
Notify:85, GetNotifyName:59.
Runtime/Engine/Classes/Animation/AnimNotifies/AnimNotifyState.h — UAnimNotifyState:34,
NotifyBegin:74, NotifyTick:75, NotifyEnd:76.
Runtime/Engine/Classes/Animation/AnimNode_StateMachine.h — FAnimNode_StateMachine:119,
GetCurrentStateName:175, GetStateWeight:255.
Runtime/Engine/Classes/Animation/Skeleton.h — USkeleton:294,
CompatibleSkeletons:345, IsCompatibleMesh:766.
Runtime/Engine/Classes/Components/SkeletalMeshComponent.h — SetAnimInstanceClass:1096,
GetAnimInstance:1111, LinkAnimClassLayers:1194, UnlinkAnimClassLayers:1205,
GetLinkedAnimLayerInstanceByClass (via UAnimInstance).
Runtime/Engine/Classes/GameFramework/Character.h — PlayAnimMontage:890,
StopAnimMontage:894, GetCurrentMontage:898.
Runtime/Engine/Public/Animation/AnimNotifyQueue.h — FAnimNotifyEventReference:21.
Runtime/AnimGraphRuntime/Public/KismetAnimationLibrary.h —
UKismetAnimationLibrary::CalculateDirection:225.
Runtime/AnimGraphRuntime/Public/AnimNodes/AnimNode_LayeredBoneBlend.h —
FAnimNode_LayeredBoneBlend:21.
Plugins/Animation/MotionWarping/Source/MotionWarping/Public/MotionWarpingComponent.h —
UMotionWarpingComponent:100, AddOrUpdateWarpTargetFromTransform:186.
Plugins/Animation/PoseSearch/Source/Runtime/Public/PoseSearch/PoseSearchLibrary.h —
UPoseSearchLibrary:141, FMotionMatchingState:56.
Official docs (UE 5.8):
Deep-dive references:
- references/anim-instance-and-update.md — full
update pipeline, thread model, proxy, Property Access, BlueprintThreadSafe functions.
- references/montages-and-slots.md — montage anatomy,
sections, root motion, blend settings, delegates, slot blending.
- references/state-machines-and-blending.md —
state machine internals, sync groups, inertialization, layered blending.
- references/motion-matching-and-warping.md —
Pose Search / Motion Matching setup, Motion Warping integration.
1---2name: ue-animation-system3description: Animate skeletal meshes in Unreal using the AnimInstance / Animation Blueprint model — C++ UAnimInstance base class (NativeInitializeAnimation, NativeUpdateAnimation, NativeThreadSafeUpdateAnimation), AnimGraph with state machines and blend spaces, animation assets (UAnimSequence, UBlendSpace, UAnimMontage, UAnimComposite, UPoseAsset), anim notifies and notify states, montage playback and delegates, linked anim layers, Motion Matching (Pose Search plugin), and Motion Warping. Use when setting up character animation, driving locomotion blends from C++, playing montages for actions, firing gameplay events at precise animation frames (notifies), switching animation sets at runtime, or integrating the Pose Search / Motion Warping plugins.4---56# Animation system78A `USkeletalMeshComponent` is animated by a `UAnimInstance` — the runtime behind an9**Animation Blueprint**. The recommended architecture is a **C++ `UAnimInstance` base** that10computes animation variables each frame, with the AnimBP's **AnimGraph** consuming those11variables to produce the final pose.1213## When to use this skill1415- Setting up a character's locomotion (idle, walk, run, jump) with state machines and blend16 spaces driven from C++.17- Playing one-off animations (attacks, reloads, hit reactions) via montages with section18 control and completion delegates.19- Firing gameplay events (footsteps, hit windows, VFX triggers) at precise animation frames20 using custom anim notifies.21- Switching animation sets at runtime with linked anim layers / `LinkAnimClassLayers`.22- Integrating the Pose Search (Motion Matching) or Motion Warping plugins.2324## Core mental model2526| Thread | What runs there | What to do there |27|---|---|---|28| Game thread | `NativeInitializeAnimation`, `NativeUpdateAnimation`, event graph | Cache references, compute simple vars |29| Anim worker thread | `NativeThreadSafeUpdateAnimation`, AnimGraph evaluation | Heavy per-frame logic (read-only, no world queries) |3031The AnimGraph **evaluates the pose** (state machines → blend spaces → IK → final pose). It32runs on the anim worker thread and must only read data the game thread wrote.33The **C++ update path computes the variables** the graph reads — never drive the final34bone transform directly from game code.3536## C++ AnimInstance base3738```cpp39// MyAnimInstance.h40#pragma once41#include "Animation/AnimInstance.h"42#include "MyAnimInstance.generated.h"4344UCLASS()45class MYGAME_API UMyAnimInstance : public UAnimInstance46{47 GENERATED_BODY()48public:49 virtual void NativeInitializeAnimation() override;50 virtual void NativeUpdateAnimation(float DeltaSeconds) override;51 virtual void NativeThreadSafeUpdateAnimation(float DeltaSeconds) override;5253 // Read by AnimGraph nodes (BlueprintReadOnly keeps them graph-accessible, thread-safe)54 UPROPERTY(BlueprintReadOnly, Category="Locomotion") float Speed = 0.f;55 UPROPERTY(BlueprintReadOnly, Category="Locomotion") float Direction = 0.f;56 UPROPERTY(BlueprintReadOnly, Category="Locomotion") bool bIsFalling = false;5758private:59 UPROPERTY() TObjectPtr<class ACharacter> OwnerCharacter;60};61```6263```cpp64// MyAnimInstance.cpp65#include "MyAnimInstance.h"66#include "GameFramework/Character.h"67#include "GameFramework/CharacterMovementComponent.h"68#include "KismetAnimationLibrary.h" // CalculateDirection6970void UMyAnimInstance::NativeInitializeAnimation()71{72 Super::NativeInitializeAnimation();73 OwnerCharacter = Cast<ACharacter>(TryGetPawnOwner()); // cache once; safe on game thread74}7576void UMyAnimInstance::NativeUpdateAnimation(float DeltaSeconds)77{78 Super::NativeUpdateAnimation(DeltaSeconds);79 // Keep lightweight — prefer NativeThreadSafeUpdateAnimation for heavy logic80 if (!OwnerCharacter) { OwnerCharacter = Cast<ACharacter>(TryGetPawnOwner()); }81}8283void UMyAnimInstance::NativeThreadSafeUpdateAnimation(float DeltaSeconds)84{85 Super::NativeThreadSafeUpdateAnimation(DeltaSeconds);86 if (!OwnerCharacter) { return; }87 const FVector Vel = OwnerCharacter->GetVelocity();88 Speed = Vel.Size2D();89 bIsFalling = OwnerCharacter->GetCharacterMovement()->IsFalling();90 Direction = UKismetAnimationLibrary::CalculateDirection(Vel, OwnerCharacter->GetActorRotation());91}92```9394Key rules:95- `NativeInitializeAnimation` — cache owner/movement references; runs once on game thread.96- `NativeUpdateAnimation` — game-thread update; keep minimal; call `Super` first.97- `NativeThreadSafeUpdateAnimation` — worker-thread update; no `UWorld` queries, no spawning,98 no non-thread-safe engine calls. This is where to put heavy per-frame computation.99- Assign variables used by the AnimGraph as `UPROPERTY(BlueprintReadOnly)` — the AnimGraph100 nodes read them by name. `BlueprintThreadSafe` meta is needed if accessed in thread-safe101 graph functions.102103Assign at runtime:104```cpp105GetMesh()->SetAnimInstanceClass(MyAnimBPClass); // TSubclassOf<UAnimInstance>106UMyAnimInstance* AI = Cast<UMyAnimInstance>(GetMesh()->GetAnimInstance());107```108109## Animation assets110111| Asset | Class | Use |112|---|---|---|113| Animation Sequence | `UAnimSequence` | Single clip bound to a skeleton |114| Blend Space (2D) | `UBlendSpace` | Blend clips by two parameters (speed × direction) |115| Blend Space 1D | `UBlendSpace1D` | Blend clips by one parameter (speed) |116| Aim Offset | `UAimOffsetBlendSpace` | Additive aim-offset by pitch/yaw |117| Montage | `UAnimMontage` | Sectioned one-off animations with slot blending |118| Composite | `UAnimComposite` | Stitch sequences into one timeline |119| Pose Asset | `UPoseAsset` | Curve-driven morph targets / facial poses |120121All assets target a **`USkeleton`** — clips are shareable across meshes that use the same122skeleton (or compatible skeletons; see `USkeleton::CompatibleSkeletons`).123124## State machines125126State machines in the AnimGraph define locomotion or combat states. Each state holds an127animation graph sub-network; transitions carry rule expressions.128From C++, query state machine state via `FAnimNode_StateMachine`:129- `GetCurrentStateName()` — `FName` of the active state.130- `GetStateWeight(int32 StateIndex)` — blend weight of a state during transition.131132Prefer driving transitions through `UPROPERTY` variables computed in the C++ update rather133than calling native state machine APIs directly.134135## Montages (actions on top of locomotion)136137Montages play in a named **slot** the AnimGraph exposes. The slot node blends the montage138over the base locomotion pose — good for attacks, reloads, hit reactions:139140```cpp141UAnimInstance* AI = GetMesh()->GetAnimInstance();142143// Play and get the length (or set ReturnValueType to MontageLength / Duration)144float Len = AI->Montage_Play(AttackMontage, 1.f);145146// Jump to / stop sections147AI->Montage_JumpToSection(FName("Combo2"), AttackMontage);148AI->Montage_Stop(0.2f, AttackMontage);149150// ACharacter convenience wrappers151PlayAnimMontage(AttackMontage, 1.f, FName("Intro"));152StopAnimMontage(AttackMontage);153```154155Bind to completion to know when an action finishes:156```cpp157AI->OnMontageEnded.AddDynamic(this, &AMyChar::OnMontageEnded);158// or per-instance:159FOnMontageEnded EndDelegate;160EndDelegate.BindUObject(this, &AMyChar::OnMontageEnded);161AI->Montage_SetEndDelegate(EndDelegate, AttackMontage);162```163164See [references/montages-and-slots.md](references/montages-and-slots.md) for section authoring,165root motion, blend settings, and `Montage_PlayWithBlendSettings`.166167## Anim Notifies (events from animation)168169- **`UAnimNotify`** — a point event (footstep SFX, spawn projectile at a bone socket).170- **`UAnimNotifyState`** — a ranged event with `NotifyBegin`/`NotifyTick`/`NotifyEnd`171 (enable weapon collision during a swing).172173```cpp174UCLASS()175class MYGAME_API UAnimNotify_Footstep : public UAnimNotify176{177 GENERATED_BODY()178 virtual FString GetNotifyName_Implementation() const override { return TEXT("Footstep"); }179 virtual void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation,180 const FAnimNotifyEventReference& Ref) override;181};182183UCLASS()184class MYGAME_API UAnimNotifyState_WeaponTrace : public UAnimNotifyState185{186 GENERATED_BODY()187 virtual void NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation,188 float TotalDuration, const FAnimNotifyEventReference& Ref) override;189 virtual void NotifyTick (USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation,190 float FrameDelta, const FAnimNotifyEventReference& Ref) override;191 virtual void NotifyEnd (USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation,192 const FAnimNotifyEventReference& Ref) override;193};194```195196Notifies are the correct way to synchronize gameplay to animation timing. Using timers as a197substitute diverges at variable play rates and when animation blending delays the clip.198199## Linked anim layers (runtime animation set swapping)200201Linked anim layers let you swap a portion of the AnimGraph at runtime — useful for weapon202styles, character class variations, or costume-specific animations:203204```cpp205// Replace the locomotion layer at runtime206GetMesh()->LinkAnimClassLayers(URifleLocomotionLayer::StaticClass());207// Revert to the default layer defined in the AnimBP208GetMesh()->UnlinkAnimClassLayers(URifleLocomotionLayer::StaticClass());209// Retrieve the active layer instance to set variables on it210UAnimInstance* Layer = GetMesh()->GetLinkedAnimLayerInstanceByClass(URifleLocomotionLayer::StaticClass());211```212213The linked AnimBP class must implement the same `UAnimLayerInterface` interface as the parent.214215## Modern options (5.x)216217- **Motion Matching** (`PoseSearch` plugin) — data-driven locomotion selects poses from a218 database matching trajectory and pose queries. Replaces hand-built state machines for219 complex locomotion. Enable `PoseSearch` in the project plugins.220- **Motion Warping** (`MotionWarping` plugin) — warps root-motion clips to hit target221 positions/rotations at runtime (ledge grabs, cover transitions). Add222 `UMotionWarpingComponent` to the character; annotate montages with warp windows;223 call `AddOrUpdateWarpTargetFromTransform` before playing.224- **Control Rig** in the AnimGraph — procedural bone adjustments, IK, foot planting225 (see `ue-control-rig-and-ik`).226- **Layered blend per bone** (`FAnimNode_LayeredBoneBlend`) — blend an upper-body montage227 slot over a lower-body locomotion pose, or blend full-body layers with per-bone masks.228- **Inertialization** (`AnimNode_Inertialization`) — cost-efficient smooth blending by229 recording velocity rather than maintaining two full pose evaluations simultaneously.230- **Animation Sharing plugin** — share a single AnimInstance across multiple distant characters231 to reduce per-character animation cost.232233## Gotchas234235- **Non-thread-safe calls in `NativeThreadSafeUpdateAnimation`** — any call that touches the236 `UWorld`, spawns objects, or reads non-thread-safe data will crash under multi-threaded anim237 update. Cache all needed references in `NativeInitializeAnimation`.238- **Wrong skeleton** — clips won't apply; retarget with the IK Retargeter or add the mesh's239 skeleton to `USkeleton::CompatibleSkeletons`.240- **Driving the pose from game code** instead of the AnimGraph — fighting the animation241 system. Compute data in C++; let the graph own the pose.242- **Using timers for animation-timed events** instead of notifies — desync at variable play243 rates and when blending delays a clip's effective start.244- **Forgetting to call `Super`** in `NativeUpdateAnimation` / `NativeInitializeAnimation` —245 the engine does essential bookkeeping in the base implementation.246- **Heavy game-thread logic in `NativeUpdateAnimation`** for many characters — move it to247 `NativeThreadSafeUpdateAnimation` to run in parallel across characters.248- **Slot with no weight in the AnimGraph** — montage plays but produces no output; ensure249 the slot node is wired between source and output and the blend weight is > 0.250- **`bUseMultiThreadedAnimationUpdate` disabled** on the AnimBP — disables the worker-thread251 path; re-enable (default on) unless you deliberately need single-threaded evaluation.252253## Version notes254255- `TObjectPtr<T>` is the modern member UPROPERTY form (UE5+); older code uses raw `T*`.256- `NativeThreadSafeUpdateAnimation` was introduced in UE 4.15 and is stable across all257 UE 5.x versions.258- `LinkAnimClassLayers` / `UnlinkAnimClassLayers` replaced the deprecated `SetLayerOverlay` /259 `ClearLayerOverlay` (deprecated since 4.24).260- `SetAnimInstanceClass` (the `USkeletalMeshComponent` overriding version) deprecated261 `K2_SetAnimInstanceClass` (deprecated 4.23) and a `TSubclassOf` setter (deprecated 5.5).262- The `PoseSearch` plugin (Motion Matching) is production-ready in UE 5.4+ and ships with263 the engine (no separate download). API surface stabilized significantly in 5.4.264265## References & source material266267Engine source (UE 5.8):268- `Runtime/Engine/Classes/Animation/AnimInstance.h` — `UAnimInstance`:358,269 `NativeInitializeAnimation`:1436, `NativeUpdateAnimation`:1439,270 `NativeThreadSafeUpdateAnimation`:1442, `NativePostEvaluateAnimation`:1444,271 `NativeBeginPlay`:1457, `Montage_Play`:626, `Montage_Stop`:639,272 `Montage_JumpToSection`:663, `GetCurrentActiveMontage`:754,273 `OnMontageBlendingOut`:758, `OnMontageEnded`:770, `Montage_SetEndDelegate`:784,274 `TryGetPawnOwner`:465, `GetOwningActor`:546, `GetOwningComponent`:550,275 `LinkAnimClassLayers`:877, `UnlinkAnimClassLayers`:888,276 `GetLinkedAnimLayerInstanceByClass`:910, `bUseMultiThreadedAnimationUpdate`:382.277- `Runtime/Engine/Classes/Animation/AnimMontage.h` — `UAnimMontage`:635,278 `FCompositeSection`:37, `FSlotAnimationTrack`:83, `BlendIn`:650, `BlendOut`:659,279 `CompositeSections`:697, `SlotAnimTracks`:701.280- `Runtime/Engine/Classes/Animation/AnimSequenceBase.h` — `UAnimSequenceBase`:36,281 `Notifies`:43, `RateScale`:61, `GetDataModel`:243.282- `Runtime/Engine/Classes/Animation/AnimSequence.h` — `UAnimSequence`:202,283 `bEnableRootMotion`:320, `RootMotionRootLock`:324.284- `Runtime/Engine/Classes/Animation/BlendSpace.h` — `UBlendSpace`:470,285 `BlendParameters`:926, `NotifyTriggerMode`:864.286- `Runtime/Engine/Classes/Animation/BlendSpace1D.h` — `UBlendSpace1D`:19.287- `Runtime/Engine/Classes/Animation/AnimNotifies/AnimNotify.h` — `UAnimNotify`:51,288 `Notify`:85, `GetNotifyName`:59.289- `Runtime/Engine/Classes/Animation/AnimNotifies/AnimNotifyState.h` — `UAnimNotifyState`:34,290 `NotifyBegin`:74, `NotifyTick`:75, `NotifyEnd`:76.291- `Runtime/Engine/Classes/Animation/AnimNode_StateMachine.h` — `FAnimNode_StateMachine`:119,292 `GetCurrentStateName`:175, `GetStateWeight`:255.293- `Runtime/Engine/Classes/Animation/Skeleton.h` — `USkeleton`:294,294 `CompatibleSkeletons`:345, `IsCompatibleMesh`:766.295- `Runtime/Engine/Classes/Components/SkeletalMeshComponent.h` — `SetAnimInstanceClass`:1096,296 `GetAnimInstance`:1111, `LinkAnimClassLayers`:1194, `UnlinkAnimClassLayers`:1205,297 `GetLinkedAnimLayerInstanceByClass` (via `UAnimInstance`).298- `Runtime/Engine/Classes/GameFramework/Character.h` — `PlayAnimMontage`:890,299 `StopAnimMontage`:894, `GetCurrentMontage`:898.300- `Runtime/Engine/Public/Animation/AnimNotifyQueue.h` — `FAnimNotifyEventReference`:21.301- `Runtime/AnimGraphRuntime/Public/KismetAnimationLibrary.h` —302 `UKismetAnimationLibrary::CalculateDirection`:225.303- `Runtime/AnimGraphRuntime/Public/AnimNodes/AnimNode_LayeredBoneBlend.h` —304 `FAnimNode_LayeredBoneBlend`:21.305- `Plugins/Animation/MotionWarping/Source/MotionWarping/Public/MotionWarpingComponent.h` —306 `UMotionWarpingComponent`:100, `AddOrUpdateWarpTargetFromTransform`:186.307- `Plugins/Animation/PoseSearch/Source/Runtime/Public/PoseSearch/PoseSearchLibrary.h` —308 `UPoseSearchLibrary`:141, `FMotionMatchingState`:56.309310Official docs (UE 5.8):311- Skeletal Mesh Animation System —312 <https://dev.epicgames.com/documentation/unreal-engine/skeletal-mesh-animation-system-in-unreal-engine>313- Animation Blueprints —314 <https://dev.epicgames.com/documentation/unreal-engine/animation-blueprints-in-unreal-engine>315- Animation Assets and Features —316 <https://dev.epicgames.com/documentation/unreal-engine/animation-assets-and-features-in-unreal-engine>317- Animating Characters and Objects —318 <https://dev.epicgames.com/documentation/unreal-engine/animating-characters-and-objects-in-unreal-engine>319320Deep-dive references:321- [references/anim-instance-and-update.md](references/anim-instance-and-update.md) — full322 update pipeline, thread model, proxy, Property Access, BlueprintThreadSafe functions.323- [references/montages-and-slots.md](references/montages-and-slots.md) — montage anatomy,324 sections, root motion, blend settings, delegates, slot blending.325- [references/state-machines-and-blending.md](references/state-machines-and-blending.md) —326 state machine internals, sync groups, inertialization, layered blending.327- [references/motion-matching-and-warping.md](references/motion-matching-and-warping.md) —328 Pose Search / Motion Matching setup, Motion Warping integration.