【UE】记录当前播放的动画序列名(AnimSequence-Montage)
记录当前播放的动画序列名(AnimSequence/Montage)
适用范围:Unreal Engine 5.x。本文示例使用自定义动画实例类
UBaseAnimInstance。
在调试动画蓝图时,我们经常需要知道角色当前使用的是哪一个动画资源。状态机只能告诉我们当前状态,而一个状态内部可能同时包含 Sequence Player、Sequence Evaluator、混合节点以及 Slot,因此仅记录状态名还不够。
下面实现一个统一入口:
Sequence Player:从动画节点引用中取得正在更新的AnimSequence。Sequence Evaluator:从动画节点引用中取得当前求值的AnimSequence。Montage:在播放 Montage 或动态 Montage 时,直接传入对应动画资源。- 最终把资源名保存到
CurrentAnimSequenceName,供蓝图、屏幕调试信息或日志读取。
这套功能更适合作为调试工具。发生动画混合时,可能有多个节点在同一帧更新,最终显示的是最后一次写入的资源名,并不一定是混合权重最大的动画。
效果




一、添加模块依赖
在项目模块的 Build.cs 中加入 AnimGraphRuntime:
PrivateDependencyModuleNames.AddRange(new string[]
{
"AnimGraphRuntime"
});
如果原本已经有 PrivateDependencyModuleNames.AddRange,直接把模块名合并进去即可,不需要重复添加一行配置。
二、头文件声明
下面以 UBaseAnimInstance 为例。generated.h 必须保持为该头文件最后一个 #include。
BaseAnimInstance.h
#pragma once
#include "CoreMinimal.h"
#include "Animation/AnimInstance.h"
#include "Animation/AnimNodeReference.h"
#include "BaseAnimInstance.generated.h"
class UAnimSequenceBase;
UCLASS()
class THIRDPERSONCONTROL_API UBaseAnimInstance : public UAnimInstance
{
GENERATED_BODY()
public:
// 当前记录到的动画资源名,便于在动画蓝图或调试界面中读取。
UPROPERTY(BlueprintReadWrite, Category = "AnimSequence")
FString CurrentAnimSequenceName;
#pragma region 记录当前播放的动画序列名
// 用于 AnimGraph 节点函数,也可通过 Montage 参数直接传入动画资源。
UFUNCTION(BlueprintCallable, Category = "Animation|Sequence",
meta = (BlueprintThreadSafe))
void UpdateCurrentAnimSequenceName(
FAnimNodeReference Node,
UAnimSequenceBase* Montage);
// C++ 便捷重载:播放 Montage 或动态 Montage 时无需构造节点引用。
void UpdateCurrentAnimSequenceName(UAnimSequenceBase* Montage)
{
UpdateCurrentAnimSequenceName(FAnimNodeReference(), Montage);
}
#pragma endregion
};
这里将 Montage 参数声明为 UAnimSequenceBase*,是为了同时兼容普通动画序列和 Montage。UAnimMontage 最终继承自 UAnimSequenceBase,因此可以直接传入。
同名重载没有添加 UFUNCTION,它只是给 C++ 调用的便捷入口。蓝图侧使用的是带 FAnimNodeReference 参数的版本。
三、实现函数
BaseAnimInstance.cpp
#include "BaseAnimInstance.h"
#include "Animation/AnimNode_SequencePlayer.h"
#include "Animation/AnimSequenceBase.h"
#include "AnimNodes/AnimNode_SequenceEvaluator.h"
#include "Kismet/KismetSystemLibrary.h"
void UBaseAnimInstance::UpdateCurrentAnimSequenceName(
FAnimNodeReference Node,
UAnimSequenceBase* Montage)
{
const UAnimSequenceBase* Sequence = nullptr;
// Sequence Player
if (const FAnimNode_SequencePlayer* SequencePlayer =
Node.GetAnimNodePtr<FAnimNode_SequencePlayer>())
{
Sequence = SequencePlayer->GetSequence();
}
// Sequence Evaluator
else if (const FAnimNode_SequenceEvaluator* SequenceEvaluator =
Node.GetAnimNodePtr<FAnimNode_SequenceEvaluator>())
{
Sequence = SequenceEvaluator->GetSequence();
}
CurrentAnimSequenceName =
UKismetSystemLibrary::GetDisplayName(Sequence);
// 直接传入 Montage 时,让 Montage 名称覆盖节点解析结果。
if (Montage)
{
CurrentAnimSequenceName =
UKismetSystemLibrary::GetDisplayName(Montage);
}
}
函数首先尝试判断 FAnimNodeReference 实际引用的节点类型:
- 如果是
FAnimNode_SequencePlayer,调用GetSequence()获取动画序列。 - 如果是
FAnimNode_SequenceEvaluator,同样调用GetSequence()获取当前求值的动画序列。 - 如果额外传入了
Montage,则用该资源名覆盖前面从节点中取得的结果。
之所以让 Montage 拥有更高优先级,是因为 C++ 便捷重载会传入一个空的 FAnimNodeReference:
UpdateCurrentAnimSequenceName(FAnimNodeReference(), Montage);
此时节点解析自然会失败,但随后仍能正确记录 Montage 名称。
四、在 AnimGraph 中记录 AnimSequence
在动画蓝图的 AnimGraph 中选择需要观察的 Sequence Player 或 Sequence Evaluator 节点,然后在 Details 面板的节点函数区域,将更新回调绑定到 Update Current Anim Sequence Name。
执行回调时,引擎会把当前节点包装为 FAnimNodeReference 传入函数。Montage 引脚不传值,保持为空即可。函数会根据实际节点类型提取动画资源并更新 CurrentAnimSequenceName。
如果多个节点都绑定了这个函数,在状态切换或 Blend 期间它们可能会在同一帧先后写入变量。因此这个值表达的是“最近一次执行回调的动画节点”,而不是严格意义上的“当前唯一播放动画”。
五、记录 Montage
播放普通 Montage
确认播放成功后,将 Montage 传给便捷重载:
if (MontageToPlay && Montage_Play(MontageToPlay) > 0.0f)
{
UpdateCurrentAnimSequenceName(MontageToPlay);
}
播放动态 Montage
PlaySlotAnimationAsDynamicMontage 接收的是 UAnimSequenceBase*,因此可以直接记录传入的源动画:
if (SequenceToPlay)
{
UAnimMontage* DynamicMontage = PlaySlotAnimationAsDynamicMontage(
SequenceToPlay,
SlotName,
0.2f,
0.25f,
1.0f);
if (DynamicMontage)
{
UpdateCurrentAnimSequenceName(SequenceToPlay);
}
}
上面的动态 Montage 示例记录的是源 AnimSequence 名称。如果希望显示运行时创建的动态 Montage 名称,也可以改为传入 DynamicMontage。
六、读取和显示结果
CurrentAnimSequenceName 已使用 BlueprintReadWrite 暴露给蓝图,可以直接在动画蓝图中读取。例如将它接入自定义屏幕调试节点,运行时便可能看到:
CurrentAnimSequence: AS_Run_F
或:
CurrentAnimSequence: AM_TurnLeft_90
如果只是读取而不希望蓝图改写变量,可以把属性标记改为 BlueprintReadOnly:
UPROPERTY(BlueprintReadOnly, Category = "AnimSequence")
FString CurrentAnimSequenceName;
七、注意事项
1. Sequence Evaluator 不一定真的在“播放”
Sequence Evaluator 通常根据显式时间对动画进行求值。本文仍将它纳入记录范围,是因为调试时我们关心的是该节点当前使用了哪个动画资源。
2. 混合期间可能出现名称跳动
状态过渡、Blend Space、Linked Anim Graph 或 Slot 混合期间,多个节点可能同时参与更新。所有节点写入同一个字符串时,结果取决于本帧最后一次调用。
如果需要显示主导动画,应进一步记录各节点的混合权重,再选择权重最大的动画;本文的轻量实现不处理权重。
3. Montage 停止后不会自动清空
便捷重载只在调用时记录资源名。Montage 播放结束后,如果没有其他 AnimGraph 节点继续更新变量,旧名称仍会保留。需要严格同步生命周期时,应在 Montage 的 Blend Out 或 End 回调中清空:
CurrentAnimSequenceName.Reset();
4. BlueprintThreadSafe 不会自动保证线程安全
该元数据表示函数允许出现在动画蓝图的线程安全调用环境中,并不代表函数内部任意操作都会自动变得线程安全。本文写入的是动画实例自身的调试变量;不要在函数中访问 Actor、World、组件状态,或调用只能在游戏线程执行的逻辑。
如果其他游戏线程代码会同时读取这个 FString,应通过动画实例的标准数据同步方式传递结果,或将读取与写入安排在同一线程。不要把这个调试字符串作为关键玩法逻辑的状态依据。
5. 不要缓存节点内部指针
GetAnimNodePtr 返回的指针只应在当前回调中立即使用,不要保存到成员变量,也不要跨帧访问。
八、总结
这套实现把 AnimGraph 节点和 Montage 两种来源统一到了 CurrentAnimSequenceName:
Sequence Player / Sequence Evaluator
└─ FAnimNodeReference ─┐
├─ UpdateCurrentAnimSequenceName
Montage / Dynamic Montage ─────────────┘
│
└─ CurrentAnimSequenceName
它的优点是代码量少、蓝图读取方便,并且同时覆盖常见的 Sequence Player、Sequence Evaluator 和 Montage 调试场景。需要注意的是,这里记录的是最近一次更新到的动画资源名;当多个动画同时混合时,它不能替代真正的动画权重分析。

浙公网安备 33010602011771号