ue-animation-system
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseUE Animation System
UE动画系统
You are an expert in Unreal Engine's animation system.
你是Unreal Engine动画系统方面的专家。
Context Check
上下文检查
Read first. Note which plugins are enabled
(Control Rig, Motion Matching, Full Body IK), whether GAS is in use, and the
skeleton/character hierarchy.
.agents/ue-project-context.md请先阅读。注意已启用的插件(Control Rig、Motion Matching、Full Body IK)、是否使用GAS,以及骨骼/角色层级结构。
.agents/ue-project-context.mdInformation to Gather
需要收集的信息
- What animation need? (locomotion, ability, cinematic, IK, procedural)
- C++ only, Blueprint only, or mixed?
- Does the character use with an existing
ACharacter?USkeletalMeshComponent - Is GAS active? (affects montage replication)
- Multiplayer? (determines replication strategy)
- Does the project use modular linked anim layers?
- 动画需求是什么?(locomotion、技能、过场动画、IK、程序化动画)
- 仅使用C++、仅使用Blueprint,还是混合使用?
- 角色是否使用带有现有的
USkeletalMeshComponent?ACharacter - 是否启用GAS?(会影响montage的复制)
- 是否为多人游戏?(决定复制策略)
- 项目是否使用模块化的链接动画层?
Architecture
架构
ACharacter / AActor
└── USkeletalMeshComponent
└── UAnimInstance subclass
├── NativeInitializeAnimation() [setup, game thread]
├── NativeUpdateAnimation(float dt) [game thread]
├── NativeThreadSafeUpdateAnimation(dt) [worker thread]
├── FAnimInstanceProxy [worker thread eval]
└── Montage API / Linked LayersAnimation updates run in two phases. Game thread: —
safe to read gameplay state. Worker thread: blend tree evaluation. Write all
shared state as members in ;
read those cached values in .
NativeUpdateAnimationUPROPERTY() TransientNativeUpdateAnimationNativeThreadSafeUpdateAnimationACharacter / AActor
└── USkeletalMeshComponent
└── UAnimInstance子类
├── NativeInitializeAnimation() [初始化,游戏线程]
├── NativeUpdateAnimation(float dt) [更新,游戏线程]
├── NativeThreadSafeUpdateAnimation(dt) [线程安全更新,工作线程]
├── FAnimInstanceProxy [工作线程计算]
└── Montage API / 链接层动画更新分为两个阶段。游戏线程: — 可安全读取游戏玩法状态。工作线程:混合树计算。在中将所有共享状态写入成员;在中读取这些缓存值。
NativeUpdateAnimationNativeUpdateAnimationUPROPERTY() TransientNativeThreadSafeUpdateAnimationAnimInstance
AnimInstance
Subclass Pattern
子类模式
cpp
// MyAnimInstance.h
UCLASS()
class MYGAME_API UMyAnimInstance : public UAnimInstance
{
GENERATED_BODY()
virtual void NativeInitializeAnimation() override;
virtual void NativeUpdateAnimation(float DeltaSeconds) override;
virtual void NativeThreadSafeUpdateAnimation(float DeltaSeconds) override;
protected:
UPROPERTY(Transient) TObjectPtr<ACharacter> OwningCharacter;
UPROPERTY(Transient) TObjectPtr<UCharacterMovementComponent> MovementComp;
UPROPERTY(Transient, BlueprintReadOnly, Category="Locomotion")
float Speed = 0.f;
UPROPERTY(Transient, BlueprintReadOnly, Category="Locomotion")
float Direction = 0.f;
UPROPERTY(Transient, BlueprintReadOnly, Category="Locomotion")
bool bIsInAir = false;
};cpp
// MyAnimInstance.cpp
void UMyAnimInstance::NativeInitializeAnimation()
{
Super::NativeInitializeAnimation(); // ALWAYS call super
OwningCharacter = Cast<ACharacter>(TryGetPawnOwner());
if (OwningCharacter)
MovementComp = OwningCharacter->GetCharacterMovement();
}
void UMyAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
Super::NativeUpdateAnimation(DeltaSeconds);
if (!OwningCharacter || !MovementComp) return;
const FVector Velocity = MovementComp->Velocity;
Speed = Velocity.Size2D();
bIsInAir = MovementComp->IsFalling();
if (Speed > 3.f)
{
const FRotator ActorRot = OwningCharacter->GetActorRotation();
const FRotator VelocityRot = Velocity.ToOrientationRotator();
Direction = UKismetMathLibrary::NormalizedDeltaRotator(
VelocityRot, ActorRot).Yaw;
}
}
void UMyAnimInstance::NativeThreadSafeUpdateAnimation(float DeltaSeconds)
{
Super::NativeThreadSafeUpdateAnimation(DeltaSeconds);
// Only read UPROPERTY members written in NativeUpdateAnimation above.
// Do NOT call any UObject functions not marked BlueprintThreadSafe.
}cpp
// MyAnimInstance.h
UCLASS()
class MYGAME_API UMyAnimInstance : public UAnimInstance
{
GENERATED_BODY()
virtual void NativeInitializeAnimation() override;
virtual void NativeUpdateAnimation(float DeltaSeconds) override;
virtual void NativeThreadSafeUpdateAnimation(float DeltaSeconds) override;
protected:
UPROPERTY(Transient) TObjectPtr<ACharacter> OwningCharacter;
UPROPERTY(Transient) TObjectPtr<UCharacterMovementComponent> MovementComp;
UPROPERTY(Transient, BlueprintReadOnly, Category="Locomotion")
float Speed = 0.f;
UPROPERTY(Transient, BlueprintReadOnly, Category="Locomotion")
float Direction = 0.f;
UPROPERTY(Transient, BlueprintReadOnly, Category="Locomotion")
bool bIsInAir = false;
};cpp
// MyAnimInstance.cpp
void UMyAnimInstance::NativeInitializeAnimation()
{
Super::NativeInitializeAnimation(); // 务必调用父类方法
OwningCharacter = Cast<ACharacter>(TryGetPawnOwner());
if (OwningCharacter)
MovementComp = OwningCharacter->GetCharacterMovement();
}
void UMyAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
Super::NativeUpdateAnimation(DeltaSeconds);
if (!OwningCharacter || !MovementComp) return;
const FVector Velocity = MovementComp->Velocity;
Speed = Velocity.Size2D();
bIsInAir = MovementComp->IsFalling();
if (Speed > 3.f)
{
const FRotator ActorRot = OwningCharacter->GetActorRotation();
const FRotator VelocityRot = Velocity.ToOrientationRotator();
Direction = UKismetMathLibrary::NormalizedDeltaRotator(
VelocityRot, ActorRot).Yaw;
}
}
void UMyAnimInstance::NativeThreadSafeUpdateAnimation(float DeltaSeconds)
{
Super::NativeThreadSafeUpdateAnimation(DeltaSeconds);
// 仅读取上述NativeUpdateAnimation中写入的UPROPERTY成员。
// 请勿调用任何未标记为BlueprintThreadSafe的UObject函数。
}FAnimInstanceProxy — Thread-Safe Access
FAnimInstanceProxy — 线程安全访问
Heavy animation logic can run on worker threads via
. Access shared data through the proxy:
NativeThreadSafeUpdateAnimationcpp
void UMyAnimInstance::NativeThreadSafeUpdateAnimation(float DeltaSeconds)
{
FMyAnimInstanceProxy& Proxy = GetProxyOnAnyThread<FMyAnimInstanceProxy>();
Proxy.Speed = Proxy.Velocity.Size();
Proxy.bIsFalling = Proxy.MovementMode == EMovementMode::MOVE_Falling;
}cpp
// FAnimInstanceProxy declaration — worker thread data container
USTRUCT()
struct FMyAnimInstanceProxy : public FAnimInstanceProxy
{
GENERATED_BODY()
FMyAnimInstanceProxy() = default;
explicit FMyAnimInstanceProxy(UAnimInstance* Instance) : FAnimInstanceProxy(Instance) {}
float Speed = 0.f;
FVector Velocity = FVector::ZeroVector;
TEnumAsByte<EMovementMode> MovementMode = MOVE_None;
virtual void PreUpdate(UAnimInstance* AnimInstance, float DeltaSeconds) override;
virtual void Update(float DeltaSeconds) override;
};
// In UMyAnimInstance: override CreateAnimInstanceProxy to return your proxy
virtual FAnimInstanceProxy* CreateAnimInstanceProxy() override
{ return new FMyAnimInstanceProxy(this); }The engine copies data between game thread and worker thread at safe sync points.
复杂的动画逻辑可通过在工作线程运行。通过代理访问共享数据:
NativeThreadSafeUpdateAnimationcpp
void UMyAnimInstance::NativeThreadSafeUpdateAnimation(float DeltaSeconds)
{
FMyAnimInstanceProxy& Proxy = GetProxyOnAnyThread<FMyAnimInstanceProxy>();
Proxy.Speed = Proxy.Velocity.Size();
Proxy.bIsFalling = Proxy.MovementMode == EMovementMode::MOVE_Falling;
}cpp
// FAnimInstanceProxy声明 — 工作线程数据容器
USTRUCT()
struct FMyAnimInstanceProxy : public FAnimInstanceProxy
{
GENERATED_BODY()
FMyAnimInstanceProxy() = default;
explicit FMyAnimInstanceProxy(UAnimInstance* Instance) : FAnimInstanceProxy(Instance) {}
float Speed = 0.f;
FVector Velocity = FVector::ZeroVector;
TEnumAsByte<EMovementMode> MovementMode = MOVE_None;
virtual void PreUpdate(UAnimInstance* AnimInstance, float DeltaSeconds) override;
virtual void Update(float DeltaSeconds) override;
};
// 在UMyAnimInstance中:重写CreateAnimInstanceProxy以返回自定义代理
virtual FAnimInstanceProxy* CreateAnimInstanceProxy() override
{ return new FMyAnimInstanceProxy(this); }引擎会在安全的同步点在游戏线程与工作线程之间复制数据。
Montages
Montages
Source: ,
AnimMontage.hAnimInstance.hKey API ():
UAnimInstancecpp
float Montage_Play(UAnimMontage*, float PlayRate=1.f,
EMontagePlayReturnType=MontageLength, float StartAt=0.f, bool bStopAll=true);
void Montage_Stop(float BlendOut, const UAnimMontage* Montage=nullptr);
void Montage_Pause(const UAnimMontage* Montage=nullptr);
void Montage_Resume(const UAnimMontage* Montage);
void Montage_JumpToSection(FName Section, const UAnimMontage* Montage=nullptr);
void Montage_SetNextSection(FName From, FName To, const UAnimMontage* Montage=nullptr);
bool Montage_IsActive(const UAnimMontage*) const;
bool Montage_IsPlaying(const UAnimMontage*) const;
FName Montage_GetCurrentSection(const UAnimMontage* Montage=nullptr) const;
float Montage_GetPosition(const UAnimMontage*) const;来源:,
AnimMontage.hAnimInstance.h核心API():
UAnimInstancecpp
float Montage_Play(UAnimMontage*, float PlayRate=1.f,
EMontagePlayReturnType=MontageLength, float StartAt=0.f, bool bStopAll=true);
void Montage_Stop(float BlendOut, const UAnimMontage* Montage=nullptr);
void Montage_Pause(const UAnimMontage* Montage=nullptr);
void Montage_Resume(const UAnimMontage* Montage);
void Montage_JumpToSection(FName Section, const UAnimMontage* Montage=nullptr);
void Montage_SetNextSection(FName From, FName To, const UAnimMontage* Montage=nullptr);
bool Montage_IsActive(const UAnimMontage*) const;
bool Montage_IsPlaying(const UAnimMontage*) const;
FName Montage_GetCurrentSection(const UAnimMontage* Montage=nullptr) const;
float Montage_GetPosition(const UAnimMontage*) const;Playing + Delegate Pattern
播放 + 委托模式
cpp
void UMyComponent::PlayAttackMontage(UAnimMontage* Montage)
{
UAnimInstance* AnimInst = GetMesh()->GetAnimInstance();
if (!AnimInst || !Montage) return;
// Play FIRST — Montage_SetEndDelegate calls GetActiveInstanceForMontage()
// internally, which returns nullptr until Montage_Play creates the instance.
if (AnimInst->Montage_Play(Montage) <= 0.f) return;
FOnMontageEnded EndDelegate;
EndDelegate.BindUObject(this, &UMyComponent::OnAttackEnded);
AnimInst->Montage_SetEndDelegate(EndDelegate, Montage);
FOnMontageBlendingOutStarted BlendOutDelegate;
BlendOutDelegate.BindUObject(this, &UMyComponent::OnAttackBlendingOut);
AnimInst->Montage_SetBlendingOutDelegate(BlendOutDelegate, Montage);
}
void UMyComponent::OnAttackEnded(UAnimMontage* Montage, bool bInterrupted) { }
void UMyComponent::OnAttackBlendingOut(UAnimMontage* Montage, bool bInterrupted) { }cpp
void UMyComponent::PlayAttackMontage(UAnimMontage* Montage)
{
UAnimInstance* AnimInst = GetMesh()->GetAnimInstance();
if (!AnimInst || !Montage) return;
// 先播放 — Montage_SetEndDelegate内部调用GetActiveInstanceForMontage()
// 该方法在Montage_Play创建实例前会返回nullptr。
if (AnimInst->Montage_Play(Montage) <= 0.f) return;
FOnMontageEnded EndDelegate;
EndDelegate.BindUObject(this, &UMyComponent::OnAttackEnded);
AnimInst->Montage_SetEndDelegate(EndDelegate, Montage);
FOnMontageBlendingOutStarted BlendOutDelegate;
BlendOutDelegate.BindUObject(this, &UMyComponent::OnAttackBlendingOut);
AnimInst->Montage_SetBlendingOutDelegate(BlendOutDelegate, Montage);
}
void UMyComponent::OnAttackEnded(UAnimMontage* Montage, bool bInterrupted) { }
void UMyComponent::OnAttackBlendingOut(UAnimMontage* Montage, bool bInterrupted) { }Dynamic Slot Montage
动态插槽Montage
cpp
UAnimMontage* DynMontage = AnimInst->PlaySlotAnimationAsDynamicMontage(
SomeSequence, FName("UpperBody"), 0.25f, 0.25f, 1.f, 1);cpp
UAnimMontage* DynMontage = AnimInst->PlaySlotAnimationAsDynamicMontage(
SomeSequence, FName("UpperBody"), 0.25f, 0.25f, 1.f, 1);Multiplayer Replication
多人游戏复制
- With GAS: use — GAS handles replication via
UAbilitySystemComponent::PlayMontage().FGameplayAbilityRepAnimMontage - Without GAS: replicate a montage pointer or a custom rep struct; server
calls , clients play on
Montage_Play.OnRep_ - Never call independently on all net roles without sync.
Montage_Play
- 使用GAS:调用— GAS通过
UAbilitySystemComponent::PlayMontage()处理复制。FGameplayAbilityRepAnimMontage - 不使用GAS:复制montage指针或自定义复制结构体;服务器调用,客户端在
Montage_Play方法中播放。OnRep_ - 切勿在所有网络角色上独立调用而不同步。
Montage_Play
GAS Integration — PlayMontageAndWait
GAS集成 — PlayMontageAndWait
cpp
// GAS ability task — PlayMontageAndWait (requires GameplayAbilities module)
UAbilityTask_PlayMontageAndWait* Task =
UAbilityTask_PlayMontageAndWait::CreatePlayMontageAndWaitProxy(
this, NAME_None, AttackMontage, 1.f);
Task->OnCompleted.AddDynamic(this, &UMyAbility::OnMontageCompleted);
Task->OnInterrupted.AddDynamic(this, &UMyAbility::OnMontageInterrupted);
Task->ReadyForActivation(); // must call to start the taskcpp
// GAS能力任务 — PlayMontageAndWait(需要GameplayAbilities模块)
UAbilityTask_PlayMontageAndWait* Task =
UAbilityTask_PlayMontageAndWait::CreatePlayMontageAndWaitProxy(
this, NAME_None, AttackMontage, 1.f);
Task->OnCompleted.AddDynamic(this, &UMyAbility::OnMontageCompleted);
Task->OnInterrupted.AddDynamic(this, &UMyAbility::OnMontageInterrupted);
Task->ReadyForActivation(); // 必须调用以启动任务Blend Spaces
Blend Spaces
Blend spaces are data assets sampled in the AnimGraph. Drive them by setting
members on the AnimInstance that the AnimGraph reads.
UPROPERTY- 1D (): single axis, typically Speed (0–600). Use
UBlendSpace1DwithFInterpolationParameter,InterpolationType=SpringDamper,InterpolationTime=0.15.DampingRatio=1.0 - 2D (): two axes — Direction (-180 to 180) and Speed (0–600). Cardinal direction samples at each speed tier.
UBlendSpace - Aim Offset (): additive blend space for Yaw/Pitch aiming, placed after the base locomotion pose in the AnimGraph.
UAimOffsetBlendSpace
See for complete axis configuration and
sample placement.
references/locomotion-setup.mdBlend spaces是在AnimGraph中采样的数据资源。通过设置AnimInstance上的成员供AnimGraph读取来驱动它们。
UPROPERTY- 1D():单轴,通常为Speed(0–600)。使用
UBlendSpace1D,设置FInterpolationParameter、InterpolationType=SpringDamper、InterpolationTime=0.15。DampingRatio=1.0 - 2D():双轴 — Direction(-180至180)和Speed(0–600)。在每个速度层级采样基本方向。
UBlendSpace - Aim Offset():用于Yaw/Pitch瞄准的叠加混合空间,放置在AnimGraph中的基础locomotion姿态之后。
UAimOffsetBlendSpace
完整的轴配置和采样点设置请查看。
references/locomotion-setup.mdState Machines
State Machines
State machines live in the AnimGraph. Bind native C++ logic to transition rules
and state entry/exit without Blueprint:
cpp
// In NativeInitializeAnimation()
AddNativeTransitionBinding(
FName("LocomotionSM"), FName("Idle"), FName("Walk/Run"),
FCanTakeTransition::CreateUObject(this, &UMyAnimInstance::CanStartMoving),
FName("IdleToMoving"));
AddNativeStateEntryBinding(
FName("LocomotionSM"), FName("Land"),
FOnGraphStateChanged::CreateUObject(this, &UMyAnimInstance::OnLandEntered));Query state machine at runtime:
cpp
const FAnimNode_StateMachine* SM =
GetStateMachineInstanceFromName(FName("LocomotionSM"));
float RunWeight = GetInstanceStateWeight(
GetStateMachineIndex(FName("LocomotionSM")), SM->GetCurrentState());状态机存在于AnimGraph中。无需Blueprint即可将原生C++逻辑绑定到过渡规则以及状态进入/退出事件:
cpp
// 在NativeInitializeAnimation()中
AddNativeTransitionBinding(
FName("LocomotionSM"), FName("Idle"), FName("Walk/Run"),
FCanTakeTransition::CreateUObject(this, &UMyAnimInstance::CanStartMoving),
FName("IdleToMoving"));
AddNativeStateEntryBinding(
FName("LocomotionSM"), FName("Land"),
FOnGraphStateChanged::CreateUObject(this, &UMyAnimInstance::OnLandEntered));运行时查询状态机:
cpp
const FAnimNode_StateMachine* SM =
GetStateMachineInstanceFromName(FName("LocomotionSM"));
float RunWeight = GetInstanceStateWeight(
GetStateMachineIndex(FName("LocomotionSM")), SM->GetCurrentState());Conduit Nodes
Conduit节点
Conduits evaluate a single boolean rule and fan out to multiple destination
states — replacing duplicated transition logic. Add a Conduit in the AnimGraph
editor; its runs once and all outgoing transitions share
the result. Use conduits when three or more states need the same entry condition
(e.g., "is grounded?"). For simple A-to-B transitions, a direct rule is clearer.
CanEnterTransitionConduit节点评估单个布尔规则并分支到多个目标状态 — 替代重复的过渡逻辑。在AnimGraph编辑器中添加Conduit;其仅运行一次,所有 outgoing 过渡共享结果。当三个或更多状态需要相同的进入条件(例如“是否在地面?”)时使用Conduit。对于简单的A到B过渡,直接规则更清晰。
CanEnterTransitionAnim Notifies
Anim Notifies
Source: ,
AnimNotify.hAnimNotifyState.h来源:,
AnimNotify.hAnimNotifyState.hUAnimNotify — Point-in-Time
UAnimNotify — 时间点通知
cpp
UCLASS(meta=(DisplayName="Footstep"))
class MYGAME_API UFootstepNotify : public UAnimNotify
{
GENERATED_BODY()
public:
// Always override the UE5 3-argument signature
virtual void Notify(USkeletalMeshComponent* MeshComp,
UAnimSequenceBase* Animation,
const FAnimNotifyEventReference& EventReference) override;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Footstep")
FName FootSocket = FName("foot_l");
};cpp
UCLASS(meta=(DisplayName="Footstep"))
class MYGAME_API UFootstepNotify : public UAnimNotify
{
GENERATED_BODY()
public:
// 务必重写UE5的3参数签名
virtual void Notify(USkeletalMeshComponent* MeshComp,
UAnimSequenceBase* Animation,
const FAnimNotifyEventReference& EventReference) override;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Footstep")
FName FootSocket = FName("foot_l");
};UAnimNotifyState — Duration (Begin/Tick/End)
UAnimNotifyState — 持续时间通知(开始/ Tick /结束)
cpp
UCLASS(meta=(DisplayName="Weapon Collision Window"))
class MYGAME_API UWeaponCollisionState : public UAnimNotifyState
{
GENERATED_BODY()
public:
virtual void NotifyBegin(USkeletalMeshComponent*, UAnimSequenceBase*,
float TotalDuration, const FAnimNotifyEventReference&) override;
virtual void NotifyEnd(USkeletalMeshComponent*, UAnimSequenceBase*,
const FAnimNotifyEventReference&) override;
};cpp
UCLASS(meta=(DisplayName="Weapon Collision Window"))
class MYGAME_API UWeaponCollisionState : public UAnimNotifyState
{
GENERATED_BODY()
public:
virtual void NotifyBegin(USkeletalMeshComponent*, UAnimSequenceBase*,
float TotalDuration, const FAnimNotifyEventReference&) override;
virtual void NotifyEnd(USkeletalMeshComponent*, UAnimSequenceBase*,
const FAnimNotifyEventReference&) override;
};BranchingPoint (Synchronous)
BranchingPoint(同步)
Set in the constructor. Override
instead of . Fires synchronously during
— use for section jumps and precise timeline control. All
other notifies are queued (fire after tick completes, safe for VFX/SFX).
bIsNativeBranchingPoint = trueBranchingPointNotify()Notify()Montage_Advance在构造函数中设置。重写而非。在期间同步触发 — 用于章节跳转和精确时间线控制。所有其他通知均为队列触发(在tick完成后触发,适合VFX/SFX)。
bIsNativeBranchingPoint = trueBranchingPointNotify()Notify()Montage_AdvanceNamed Notify Delegate
命名通知委托
cpp
AnimInst->OnPlayMontageNotifyBegin.AddDynamic(
this, &UMyComponent::HandleNotifyBegin);
void UMyComponent::HandleNotifyBegin(FName NotifyName,
const FBranchingPointNotifyPayload& Payload)
{
if (NotifyName == FName("EnableHitbox")) ActivateHitDetection();
}See for built-in notify catalog and more
custom patterns.
references/anim-notify-reference.mdcpp
AnimInst->OnPlayMontageNotifyBegin.AddDynamic(
this, &UMyComponent::HandleNotifyBegin);
void UMyComponent::HandleNotifyBegin(FName NotifyName,
const FBranchingPointNotifyPayload& Payload)
{
if (NotifyName == FName("EnableHitbox")) ActivateHitDetection();
}内置通知目录和更多自定义模式请查看。
references/anim-notify-reference.mdIK and Procedural
IK与程序化动画
Foot IK with Line Traces (NativeUpdateAnimation — game thread)
基于线Trace的脚部IK(NativeUpdateAnimation — 游戏线程)
cpp
FVector UMyAnimInstance::GetFootTarget(FName SocketName) const
{
const FVector Foot = GetOwningComponent()->GetSocketLocation(SocketName);
FHitResult Hit;
FCollisionQueryParams P(SCENE_QUERY_STAT(FootIK), true);
P.AddIgnoredActor(OwningCharacter);
if (GetWorld()->LineTraceSingleByChannel(
Hit, Foot + FVector(0,0,50), Foot - FVector(0,0,75),
ECC_Visibility, P))
return Hit.ImpactPoint;
return Foot;
}Feed results into a Control Rig asset (UE5 recommended) or a
Two Bone IK skeletal control node in the AnimGraph.
Skeletal control nodes (AnimGraph):
| Node | Purpose |
|---|---|
| Two Bone IK | Two-joint IK (arm, leg) — effector + joint target |
| FABRIK | Multi-bone chain IK — tip bone + effector |
| Look At | Single bone tracks target location with clamp |
| Copy Bone | Copies transform components between bones |
| Spline IK | Bones follow a spline curve (spine, tail) |
cpp
FVector UMyAnimInstance::GetFootTarget(FName SocketName) const
{
const FVector Foot = GetOwningComponent()->GetSocketLocation(SocketName);
FHitResult Hit;
FCollisionQueryParams P(SCENE_QUERY_STAT(FootIK), true);
P.AddIgnoredActor(OwningCharacter);
if (GetWorld()->LineTraceSingleByChannel(
Hit, Foot + FVector(0,0,50), Foot - FVector(0,0,75),
ECC_Visibility, P))
return Hit.ImpactPoint;
return Foot;
}将结果输入Control Rig资源(UE5推荐)或AnimGraph中的Two Bone IK骨骼控制节点。
骨骼控制节点(AnimGraph):
| 节点 | 用途 |
|---|---|
| Two Bone IK | 双关节IK(手臂、腿部)— effector + 关节目标 |
| FABRIK | 多骨骼链IK — 末端骨骼 + effector |
| Look At | 单骨骼跟踪目标位置并限制角度 |
| Copy Bone | 在骨骼之间复制变换组件 |
| Spline IK | 骨骼跟随样条曲线(脊柱、尾巴) |
Layered Blend Per Bone (Upper/Lower Split)
Layered Blend Per Bone(上/下身分离)
In the AnimGraph:
- Connect state machine output to Base Pose.
- Connect slot output to Blend Poses 0.
UpperBody - Layer Setup: Bone=, Depth=0, MeshPoseBlendFactor=1.0.
spine_01
Attack montages use the slot; locomotion plays uninterrupted below.
UpperBody在AnimGraph中:
- 将状态机输出连接到Base Pose。
- 将插槽输出连接到Blend Poses 0。
UpperBody - 层设置:Bone=,Depth=0,MeshPoseBlendFactor=1.0。
spine_01
攻击montages使用插槽;locomotion在下方持续播放不受影响。
UpperBodyAim Offset
Aim Offset
cpp
// In NativeUpdateAnimation:
const FRotator Delta = UKismetMathLibrary::NormalizedDeltaRotator(
OwningCharacter->GetBaseAimRotation(),
OwningCharacter->GetActorRotation());
AimYaw = FMath::Clamp(Delta.Yaw, -90.f, 90.f);
AimPitch = FMath::Clamp(Delta.Pitch, -90.f, 90.f);Place an Aim Offset node after the base pose in the AnimGraph, feeding
and .
AimYawAimPitchcpp
// 在NativeUpdateAnimation中:
const FRotator Delta = UKismetMathLibrary::NormalizedDeltaRotator(
OwningCharacter->GetBaseAimRotation(),
OwningCharacter->GetActorRotation());
AimYaw = FMath::Clamp(Delta.Yaw, -90.f, 90.f);
AimPitch = FMath::Clamp(Delta.Pitch, -90.f, 90.f);在AnimGraph中的基础姿态之后放置Aim Offset节点,传入和。
AimYawAimPitchLinked Anim Graphs and Layers
Linked Anim Graphs与层
Source: ,
AnimNode_LinkedAnimGraph.hAnimNode_LinkedAnimLayer.h来源:,
AnimNode_LinkedAnimGraph.hAnimNode_LinkedAnimLayer.hLinked Anim Layer (Recommended)
Linked Anim Layer(推荐)
- Create a Blueprint with layer function signatures.
UAnimLayerInterface - Main AnimInstance has Linked Anim Layer nodes referencing the interface.
- Separate AnimInstance subclasses implement the interface per mode.
- Swap at runtime:
cpp
// Switch locomotion implementation
AnimInst->LinkAnimClassLayers(UClimbingLocomotionLayer::StaticClass());
// Reset all layers to defaults:
AnimInst->LinkAnimClassLayers(nullptr);
// Retrieve a linked instance:
UAnimInstance* Layer =
AnimInst->GetLinkedAnimLayerInstanceByClass(UClimbingLocomotionLayer::StaticClass());- 创建带有层函数签名的Blueprint。
UAnimLayerInterface - 主AnimInstance包含引用该接口的Linked Anim Layer节点。
- 独立的AnimInstance子类按模式实现该接口。
- 运行时切换:
cpp
// 切换locomotion实现
AnimInst->LinkAnimClassLayers(UClimbingLocomotionLayer::StaticClass());
// 将所有层重置为默认值:
AnimInst->LinkAnimClassLayers(nullptr);
// 获取链接实例:
UAnimInstance* Layer =
AnimInst->GetLinkedAnimLayerInstanceByClass(UClimbingLocomotionLayer::StaticClass());Linked Anim Graph (by Tag)
Linked Anim Graph(按标签)
cpp
AnimInst->LinkAnimGraphByTag(FName("CombatGraph"), UMyCombatAnimInstance::StaticClass());
UAnimInstance* Sub = AnimInst->GetLinkedAnimGraphInstanceByTag(FName("CombatGraph"));cpp
AnimInst->LinkAnimGraphByTag(FName("CombatGraph"), UMyCombatAnimInstance::StaticClass());
UAnimInstance* Sub = AnimInst->GetLinkedAnimGraphInstanceByTag(FName("CombatGraph"));Notify Propagation
通知传播
cpp
AnimInst->SetReceiveNotifiesFromLinkedInstances(true);
AnimInst->SetPropagateNotifiesToLinkedInstances(true);
// UE5.2+: let linked layers share main instance montage evaluation:
// AnimInst->SetUseMainInstanceMontageEvaluationData(true);cpp
AnimInst->SetReceiveNotifiesFromLinkedInstances(true);
AnimInst->SetPropagateNotifiesToLinkedInstances(true);
// UE5.2+:让链接层共享主实例的montage计算数据:
// AnimInst->SetUseMainInstanceMontageEvaluationData(true);Root Motion
Root Motion
cpp
// Options: NoRootMotionExtraction, IgnoreRootMotion,
// RootMotionFromMontagesOnly, RootMotionFromEverything
RootMotionMode = ERootMotionMode::RootMotionFromMontagesOnly;
// Per-montage disable
FAnimMontageInstance* Inst = AnimInst->GetActiveInstanceForMontage(Montage);
if (Inst) { Inst->PushDisableRootMotion(); /* ... */ Inst->PopDisableRootMotion(); }For networked root motion, set the movement component's smoothing mode to
and enable
if rotation comes from animation.
The server runs root motion authoritatively; clients predict and correct via
.
ENetworkSmoothingMode::ExponentialbAllowPhysicsRotationDuringAnimRootMotionFRootMotionMovementParamscpp
// 选项:NoRootMotionExtraction, IgnoreRootMotion,
// RootMotionFromMontagesOnly, RootMotionFromEverything
RootMotionMode = ERootMotionMode::RootMotionFromMontagesOnly;
// 按montage禁用
FAnimMontageInstance* Inst = AnimInst->GetActiveInstanceForMontage(Montage);
if (Inst) { Inst->PushDisableRootMotion(); /* ... */ Inst->PopDisableRootMotion(); }对于联网Root Motion,将移动组件的平滑模式设置为,如果旋转来自动画则启用。服务器权威运行Root Motion;客户端通过进行预测和修正。
ENetworkSmoothingMode::ExponentialbAllowPhysicsRotationDuringAnimRootMotionFRootMotionMovementParamsCommon Mistakes
常见错误
| Anti-Pattern | Fix |
|---|---|
Reading gameplay state in | Cache values as |
Polling | Bind |
| Two montages sharing the same slot group | Use distinct slot names ( |
Skipping | Always call super — it initializes the proxy and skeleton |
| Calling non-thread-safe UObject methods in thread-safe update | Only read primitive |
| 反模式 | 修复方案 |
|---|---|
在 | 在 |
在循环中轮询 | 在调用 |
| 两个montages共享同一插槽组 | 使用不同的插槽名称( |
跳过 | 务必调用父类方法 — 它会初始化代理和骨骼 |
| 在线程安全更新中调用非线程安全的UObject方法 | 仅读取原始 |
Build.cs
Build.cs
csharp
PublicDependencyModuleNames.AddRange(new string[] {
"Core", "CoreUObject", "Engine", "AnimGraphRuntime"
});
// Optional:
PrivateDependencyModuleNames.Add("ControlRig"); // Control Rig IK
PrivateDependencyModuleNames.Add("GameplayAbilities"); // GAS montage taskscsharp
PublicDependencyModuleNames.AddRange(new string[] {
"Core", "CoreUObject", "Engine", "AnimGraphRuntime"
});
// 可选:
PrivateDependencyModuleNames.Add("ControlRig"); // Control Rig IK
PrivateDependencyModuleNames.Add("GameplayAbilities"); // GAS montage任务Related Skills
相关技能
- —
ue-gameplay-abilitiestask, GAS montage replication.PlayMontageAndWait - — SkeletalMeshComponent setup, Character hierarchy, component tick ordering.
ue-actor-component-architecture - — delegate binding,
ue-cpp-foundationsspecifiers,UPROPERTY.TWeakObjectPtr
- —
ue-gameplay-abilities任务、GAS montage复制。PlayMontageAndWait - — SkeletalMeshComponent设置、角色层级结构、组件tick顺序。
ue-actor-component-architecture - — 委托绑定、
ue-cpp-foundations说明符、UPROPERTY。TWeakObjectPtr
Reference Files
参考文件
- — built-in notify catalog and custom notify implementation patterns.
references/anim-notify-reference.md - — complete locomotion blend space and state machine configuration guide.
references/locomotion-setup.md
- — 内置通知目录和自定义通知实现模式。
references/anim-notify-reference.md - — 完整的locomotion混合空间和状态机配置指南。
references/locomotion-setup.md