AnimGraph 模块全面系统分析
源文档:https://gitee.com/chouchouxia/share-ue5
目录
1 模块概述
1.1 基本信息
| 属性 | 值 |
|---|---|
| 模块名称 | AnimGraph |
| 类型 | Editor(EngineDeveloper) |
| 位置 | Engine/Source/Editor/AnimGraph |
| 描述 | 动画蓝图图形编辑器的核心模块,负责动画蓝图节点的可视化编辑、编译、属性绑定和预览 |
AnimGraph 是 UE5 动画蓝图编辑器的"心脏"。它一端连接着动画蓝图(UAnimBlueprint),另一端连接着动画蓝图运行时(AnimGraphRuntime),把"在编辑器中拖拽连线"这个直观操作,翻译成"运行时高效执行的动画节点图"。
这个模块的代码量非常大——Public 和 Private 目录下加起来超过 200 个文件,涵盖了上百种动画蓝图节点的编辑器表现、完整的编译管线、编辑器模式、属性绑定、图属性系统等方方面面。
1.2 模块文件结构
AnimGraph/
├── AnimGraph.Build.cs
├── Public/
│ ├── AnimGraphNode_Base.h # 所有动画蓝图节点的编辑器基类
│ ├── AnimGraphNode_SkeletalControlBase.h # 骨骼控制节点基类
│ ├── AnimGraphNode_AssetPlayerBase.h # 动画资产播放节点基类
│ ├── AnimGraphNode_StateMachineBase.h # 状态机节点基类
│ ├── AnimationGraphSchema.h # 动画图 Schema
│ ├── AnimBlueprintExtension.h # 动画蓝图扩展系统
│ ├── IAnimBlueprintCompilationContext.h # 编译上下文接口
│ ├── AnimGraphCommands.h # 编辑器命令
│ ├── AnimNodeEditModes.h # 节点编辑模式 ID
│ ├── AnimGraphSettings.h # 模块设置
│ ├── AnimGraphAttributes.h # 图属性系统
│ ├── AnimGraphNode_*.h # 各类动画蓝图节点(约 80+ 个)
│ ├── AnimationGraph.h # 动画图
│ ├── AnimationStateMachineGraph.h # 状态机图
│ ├── AnimationStateGraph.h # 状态图
│ ├── AnimationTransitionGraph.h # 过渡图
│ └── ...
├── Private/
│ ├── AnimGraphModule.cpp # 模块入口
│ ├── AnimationGraphSchema.cpp # Schema 实现
│ ├── AnimBlueprintCompiler.cpp/.h # 动画蓝图编译器
│ ├── AnimBlueprintExtension_*.cpp/.h # 各类扩展实现(约 10 个)
│ ├── AnimGraphNode_*.cpp # 各类节点实现
│ ├── EditModes/ # 编辑器模式(IK、PoseDriver 等)
│ ├── AnimGraphDetails.cpp # 图自定义
│ ├── PoseDriverDetails.cpp # PoseDriver 细节面板
│ └── ...
└── Internal/
├── AnimBlueprintExtension_Base.h
└── AnimGraphNodeBinding.h
1.3 模块依赖
AnimGraph 模块的依赖关系在 [AnimGraph.Build.cs](file:///f:/UE/UE_5.5/Engine/Source/Editor/AnimGraph/AnimGraph.Build.cs) 中定义:
公共依赖(PublicDependencyModuleNames):
| 模块 | 用途 |
|---|---|
Core / CoreUObject / Engine | UE 核心基础设施 |
Slate | UI 框架 |
AnimGraphRuntime | 动画蓝图运行时节点(FAnimNode_Base 等) |
BlueprintGraph | 蓝图图形编辑基础设施(UK2Node、UEdGraphSchema_K2 等) |
私有依赖(PrivateDependencyModuleNames):
| 模块 | 用途 |
|---|---|
UnrealEd | 编辑器核心 |
GraphEditor | 通用图形编辑器框架 |
PropertyEditor | 属性面板(细节面板) |
KismetCompiler / Kismet | 蓝图编译基础设施 |
ToolMenus | 右键菜单 |
EditorWidgets / ToolWidgets | 编辑器 UI 组件 |
AnimationEditMode | 动画编辑模式 |
DeveloperSettings | 开发者设置 |
ContentBrowser | 内容浏览器集成 |
2 必须知道的问题
2.1 为什么要做 AnimGraph 模块?如果没有它会怎么样?
核心定位
AnimGraph 的定位可以这样理解:它是动画蓝图编辑器的"编辑器层"。
在 UE 中,动画系统分为两大块:
| 层级 | 模块 | 职责 |
|---|---|---|
| 运行时 | AnimGraphRuntime(Engine 的一部分) | 定义 FAnimNode_Base 和各种运行时动画节点,负责实际的动画计算 |
| 编辑器 | AnimGraph(Editor 的一部分) | 为每个运行时节点提供编辑器可视化表现、属性编辑、编译、预览 |
AnimGraph 模块做的事情,本质上就是把每一个运行时 FAnimNode_Base 子类,映射到一个对应的 UAnimGraphNode_Base 编辑器子类,然后在这个编辑器子类上实现:
- 图上的可视化连线(Pose Link)
- 属性引脚暴露与绑定(Property Binding)
- 编译时把编辑器数据翻译成运行时数据结构
- 编辑器中的骨骼操作预览(Edit Mode)
- 右键菜单、拖拽、快捷键等编辑器交互
如果没有这个模块会怎么样?
那就没有动画蓝图编辑器了。具体来说:
- 无法在编辑器中可视化地编辑动画蓝图:现在大家打开动画蓝图,看到的那张节点图——状态机、BlendSpace、IK、各种混合节点——全部是 AnimGraph 模块提供的。没有它,就只能靠 C++ 代码手写动画逻辑。
- 无法编译动画蓝图:
FAnimBlueprintCompilerContext负责把图上的节点翻译成UAnimBlueprintGeneratedClass中的运行时数据结构。没有编译器,蓝图就只是一张图,无法真正运行。 - 无法在编辑器中预览动画:编辑模式(Edit Mode)系统允许在视口中拖拽 IK 目标、调整骨骼位置,这是 AnimGraph 模块通过
FAnimNodeEditMode体系实现的。 - 属性绑定(Property Binding)不可用:动画节点上那些"从蓝图变量取值"的绑定功能,是 AnimGraph 在编译阶段处理的。
AnimGraph 帮我们省掉了动画系统中最"可视化密集"的那部分工作,让大家能像搭积木一样搭建动画逻辑。
2.2 这个模块和 AnimGraphRuntime 是什么关系?
这是一个非常关键的问题。两者的关系可以概括为:一一映射 + 编译器桥接。
编辑器层 (AnimGraph) 运行时层 (AnimGraphRuntime)
───────────────── ─────────────────────────
UAnimGraphNode_SequencePlayer ←──→ FAnimNode_SequencePlayer
UAnimGraphNode_StateMachine ←──→ FAnimNode_StateMachine
UAnimGraphNode_TwoBoneIK ←──→ FAnimNode_TwoBoneIK
UAnimGraphNode_BlendSpacePlayer ←──→ FAnimNode_BlendSpacePlayer
... ...
每个运行时 FAnimNode_Base 子类都有一个对应的 UAnimGraphNode_Base 子类。编辑器节点内部持有一个对应运行时节点的实例,编译时把编辑器的数据拷贝到运行时结构中。
关键设计点:
- 编辑器节点继承自
UK2Node,所以它能参与蓝图图的编辑流程(引脚、连线、菜单等) - 运行时节点继承自
FAnimNode_Base,它是一个纯 C++ 结构体,不包含任何编辑器逻辑 - 编译器(
FAnimBlueprintCompilerContext) 负责把编辑器节点中的数据"烘焙"到运行时结构里
2.3 核心工作机制:从"图上连线"到"运行时执行"
动画蓝图编辑器的工作流程大致如下:
1. 在动画蓝图中创建动画图(AnimationGraph)
↓
2. 在图上拖入动画节点(如 SequencePlayer、BlendSpace、TwoBoneIK 等)
↓
3. 通过 Pose Link 连线定义数据流向
↓
4. 通过属性绑定将节点属性连接到蓝图变量
↓
5. 点击"编译"(Compile)
↓
6. FAnimBlueprintCompilerContext 处理所有节点:
a. 分配节点索引(AllocateNodeIndexCounter)
b. 分析 Pose Link 连接关系
c. 折叠常量属性(Constant Folding)
d. 创建运行时数据结构(AnimBlueprintGeneratedClass)
e. 处理扩展系统(Extension System)
↓
7. 生成 UAnimBlueprintGeneratedClass,可在运行时执行
3 模块整体架构解析
3.1 架构图
┌──────────────────────────────────────────────────────────────────────────────┐
│ 动画蓝图编辑器 (Persona) │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ 动画蓝图 (UAnimBlueprint) │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ AnimationGraph│ │ StateMachine │ │ Transition │ ... │ │
│ │ │ (动画图) │ │ Graph │ │ Graph │ │ │
│ │ │ │ │ (状态机图) │ │ (过渡图) │ │ │
│ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │
│ └─────────┼──────────────────┼──────────────────┼─────────────────────────┘ │
└────────────┼──────────────────┼──────────────────┼────────────────────────────┘
│ │ │
└──────────────────┴──────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ AnimGraph 模块 (Editor层) │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ UAnimationGraphSchema (图 Schema) │ │
│ │ - 管理连线规则、引脚类型、拖放行为、右键菜单 │ │
│ │ - 定义 Pose Link 引脚(本地空间 / 组件空间) │ │
│ │ - 继承自 UEdGraphSchema_K2 │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────────────────┼────────────────────────────────────┐ │
│ │ UAnimGraphNode_Base (节点基类) │ │
│ │ ┌──────────────────────────────────────────────────────────────────┐ │ │
│ │ │ 核心功能: │ │ │
│ │ │ - 引脚管理 (CreateOutputPins, AllocateDefaultPins) │ │ │
│ │ │ - 属性绑定 (PropertyBinding) │ │ │
│ │ │ - 编译钩子 (OnProcessDuringCompilation) │ │ │
│ │ │ - 编辑模式 (GetEditorMode, OnNodeSelected) │ │ │
│ │ │ - 预览调试 (Draw, GetOnScreenDebugInfo, FindDebugAnimNode) │ │ │
│ │ │ - 函数绑定 (InitialUpdateFunction, BecomeRelevantFunction, │ │ │
│ │ │ UpdateFunction) │ │ │
│ │ └──────────────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────────┼───────────────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌───────────────────┐ ┌───────────────────────┐ ┌───────────────────┐ │
│ │ AssetPlayerBase │ │ SkeletalControlBase │ │ StateMachineBase │ │
│ │ (资产播放节点) │ │ (骨骼控制节点) │ │ (状态机节点) │ │
│ ├───────────────────┤ ├───────────────────────┤ ├───────────────────┤ │
│ │ SequencePlayer │ │ TwoBoneIK │ │ AnimStateNode │ │
│ │ BlendSpacePlayer │ │ CCDIK │ │ StateMachineNode │ │
│ │ SequenceEvaluator │ │ Fabrik │ │ TransitionNode │ │
│ │ RandomPlayer │ │ LookAt │ │ ConduitNode │ │
│ │ PoseDriver │ │ ModifyBone │ │ StateAliasNode │ │
│ │ ... │ │ ... │ │ ... │ │
│ └───────────────────┘ └───────────────────────┘ └───────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ UAnimBlueprintExtension (扩展系统) │ │
│ │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐ │ │
│ │ │ Extension_Base │ │ Extension_State │ │ Extension_PropertyAccess │ │ │
│ │ │ (基础扩展) │ │ Machine │ │ (属性访问扩展) │ │ │
│ │ │ │ │ (状态机扩展) │ │ │ │ │
│ │ └──────────────────┘ └──────────────────┘ └──────────────────────────┘ │ │
│ │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐ │ │
│ │ │ Extension_ │ │ Extension_ │ │ Extension_LinkedAnim │ │ │
│ │ │ CachedPose │ │ CallFunction │ │ Graph │ │ │
│ │ │ (缓存姿势扩展) │ │ (函数调用扩展) │ │ (链接动画图扩展) │ │ │
│ │ └──────────────────┘ └──────────────────┘ └──────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ FAnimBlueprintCompilerContext (编译器) │ │
│ │ - 继承自 FKismetCompilerContext │ │
│ │ - ProcessAllAnimationNodes() → 处理所有节点 │ │
│ │ - 常量折叠 (Constant Folding) │ │
│ │ - 生成 AnimBlueprintGeneratedClass │ │
│ │ - 协调 Extension 系统参与编译 │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ EditModes (编辑器模式) │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ TwoBoneIK │ │ CCDIK │ │ Fabrik │ │ PoseDriver │ │ │
│ │ │ EditMode │ │ EditMode │ │ EditMode │ │ EditMode │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ ModifyBone │ │ ObserveBone │ │ LookAt │ │ SplineIK │ │ │
│ │ │ EditMode │ │ EditMode │ │ EditMode │ │ EditMode │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ AnimGraphRuntime (运行时层) │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ FAnimNode_Base → FAnimNode_AssetPlayerBase → FAnimNode_SkeletalControl │ │
│ │ → FAnimNode_StateMachine → ... │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ UAnimBlueprintGeneratedClass (编译产物) │ │
│ │ - 节点数组 AnimNodeData[] │ │
│ │ - 属性绑定数据 │ │
│ │ - 扩展数据 │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────┘
3.2 模块划分
1. 节点层(Node Layer)
职责:
- 为每个运行时
FAnimNode_Base子类提供编辑器可视化表现 - 管理引脚(Pin)的创建、连接、默认值
- 提供属性绑定接口
- 处理编译钩子
核心类:
UAnimGraphNode_Base— 所有动画蓝图编辑器节点的基类UAnimGraphNode_SkeletalControlBase— 骨骼控制节点基类(IK、LookAt 等)UAnimGraphNode_AssetPlayerBase— 动画资产播放节点基类(SequencePlayer、BlendSpacePlayer 等)UAnimGraphNode_StateMachineBase— 状态机节点基类UAnimGraphNode_BlendListBase— 混合列表节点基类
节点分类:
| 分类 | 基类 | 典型节点 |
|---|---|---|
| 资产播放 | UAnimGraphNode_AssetPlayerBase | SequencePlayer, BlendSpacePlayer, SequenceEvaluator, RandomPlayer, PoseDriver |
| 骨骼控制 | UAnimGraphNode_SkeletalControlBase | TwoBoneIK, CCDIK, Fabrik, LookAt, ModifyBone, SplineIK, SpringBone, Trail |
| 状态机 | UAnimGraphNode_StateMachineBase | AnimStateNode, StateMachineNode, TransitionNode, ConduitNode |
| 混合 | UAnimGraphNode_BlendListBase | BlendListByBool, BlendListByEnum, BlendListByInt, LayeredBoneBlend |
| 空间转换 | UAnimGraphNode_Base | LocalToComponentSpace, ComponentToLocalSpace |
| 缓存 | UAnimGraphNode_Base | SaveCachedPose, UseCachedPose |
| 姿势引用 | UAnimGraphNode_Base | MeshRefPose, IdentityPose, LocalRefPose |
| 其他 | UAnimGraphNode_Base | Root, Slot, Sync, Mirror, CopyPoseFromMesh 等 |
2. 图 Schema 层(Schema Layer)
职责:
- 定义动画图中的连线规则
- 管理 Pose Link 引脚类型(本地空间 / 组件空间)
- 处理拖放资产到图上的行为
- 提供右键菜单
核心类:
UAnimationGraphSchema— 动画蓝图主图的 SchemaUAnimationStateMachineSchema— 状态机图的 SchemaUAnimationStateGraphSchema— 单个状态内部图的 SchemaUAnimationTransitionSchema— 过渡图(Transition Rule)的 SchemaUAnimationConduitGraphSchema— 管道(Conduit)图的 Schema
3. 编译层(Compiler Layer)
职责:
- 将编辑器节点图翻译成运行时数据结构
- 处理 Pose Link 映射
- 常量折叠优化
- 生成
UAnimBlueprintGeneratedClass
核心类:
FAnimBlueprintCompilerContext— 动画蓝图编译器主体IAnimBlueprintCompilationContext— 编译上下文接口IAnimBlueprintCompilerCreationContext— 编译器创建上下文接口IAnimBlueprintGeneratedClassCompiledData— 编译产物数据接口
4. 扩展系统层(Extension Layer)
职责:
- 允许为动画蓝图添加"子系统"级别的数据和处理逻辑
- 在编译过程的各个阶段执行自定义逻辑
- 管理扩展专属的实例数据和类数据
核心类:
UAnimBlueprintExtension— 扩展基类UAnimBlueprintExtension_Base— 基础扩展(处理节点处理)UAnimBlueprintExtension_StateMachine— 状态机扩展UAnimBlueprintExtension_CachedPose— 缓存姿势扩展UAnimBlueprintExtension_CallFunction— 函数调用扩展UAnimBlueprintExtension_PropertyAccess— 属性访问扩展UAnimBlueprintExtension_LinkedAnimGraph— 链接动画图扩展UAnimBlueprintExtension_LinkedInputPose— 链接输入姿势扩展UAnimBlueprintExtension_Tag— 标签扩展UAnimBlueprintExtension_BlendSpaceGraph— BlendSpace 图扩展UAnimBlueprintExtension_Attributes— 属性扩展
5. 编辑模式层(Edit Mode Layer)
职责:
- 在视口中提供骨骼级别的交互式编辑
- 允许拖拽 IK 目标、调整骨骼位置
- 提供节点专属的视口可视化
核心类:
FAnimNodeEditMode— 编辑模式基类FTwoBoneIKEditMode— 双骨骼 IK 编辑模式FCCDIKEditMode— CCD IK 编辑模式FFabrikEditMode— FABRIK 编辑模式FLookAtEditMode— LookAt 编辑模式FModifyBoneEditMode— 修改骨骼编辑模式FObserveBoneEditMode— 观察骨骼编辑模式FPoseDriverEditMode— PoseDriver 编辑模式FSplineIKEditMode— 样条 IK 编辑模式FAnimDynamicsEditMode— 动画动力学编辑模式
6. 基础设施层(Infrastructure Layer)
职责:
- 模块加载/卸载
- 注册编辑器命令、编辑模式、细节面板自定义
- 管理图属性(Graph Attributes)
- 提供模块设置
核心类:
FAnimGraphModule— 模块入口FAnimGraphCommands— 编辑器命令UAnimGraphSettings— 模块设置UAnimGraphAttributes— 图属性系统
3.3 模块间依赖关系
FAnimGraphModule (模块入口)
│
├─ 注册 → FAnimGraphCommands (编辑器命令)
├─ 注册 → FAnimBlueprintCompilerContext (编译器)
├─ 注册 → FAnimNodeEditMode 系列 (编辑器模式)
├─ 注册 → 细节面板自定义 (FAnimBlueprintDetails, FPoseDriverDetails)
├─ 注册 → FAnimGraphDetails (图自定义)
└─ 注册 → UAnimGraphAttributes (图属性)
│
▼
UAnimationGraphSchema (图 Schema)
│
▼
UAnimGraphNode_Base (节点基类)
│
┌───────┼───────────┬──────────────┐
▼ ▼ ▼ ▼
Asset Skeletal StateMachine BlendList
Player Control Base Base
│
▼
FAnimBlueprintCompilerContext (编译器)
│
├─ 使用 → UAnimBlueprintExtension (扩展系统)
│ ├─ Extension_Base
│ ├─ Extension_StateMachine
│ ├─ Extension_PropertyAccess
│ └─ ...
│
└─ 生成 → UAnimBlueprintGeneratedClass (编译产物)
3.4 数据流走向
编译流程:
1. 用户点击 Compile
↓
2. FAnimBlueprintCompilerContext 被创建
↓
3. CreateClassVariablesFromBlueprint() — 创建类变量
↓
4. SpawnNewClass() — 创建新的 UAnimBlueprintGeneratedClass
↓
5. ProcessAllAnimationNodes() — 处理所有动画节点
├─ 遍历所有 AnimationGraph 和子图
├─ 对每个 UAnimGraphNode_Base:
│ ├─ 分配节点索引 (AllocateNodeIndexCounter)
│ ├─ 创建 FProperty 到 AnimBlueprintGeneratedClass
│ ├─ 分析 PoseLink 连接关系
│ ├─ 收集常量折叠记录
│ └─ 调用 OnProcessDuringCompilation()
├─ PruneIsolatedAnimationNodes() — 裁剪孤立节点
└─ ProcessExtensions() — 处理扩展系统
↓
6. ProcessFoldedPropertyRecords() — 常量折叠
↓
7. CopyTermDefaultsToDefaultObject() — 复制默认值到 CDO
↓
8. FinishCompilingClass() — 完成编译
↓
9. 生成 UAnimBlueprintGeneratedClass,可在运行时使用
属性绑定流程:
1. 在编辑器中,右键点击节点属性 → "Bind" → 选择蓝图变量
↓
2. 创建 FAnimGraphNodePropertyBinding 记录
├─ PropertyName: 绑定的属性名
├─ PropertyPath: 属性路径
├─ Type: Property / Function
└─ bIsBound: true
↓
3. 编译时,FAnimBlueprintCompilerContext 处理绑定
├─ 对于 Property 绑定: 生成属性访问代码
├─ 对于 Function 绑定: 生成函数调用代码
└─ 处理 Promotion (如 bool → int 转换)
↓
4. 运行时,通过 PropertyAccess 系统高效读写属性
编辑模式交互流程:
1. 用户在动画蓝图中选中一个骨骼控制节点(如 TwoBoneIK)
↓
2. UAnimGraphNode_Base::OnNodeSelected() 被调用
↓
3. 节点返回 GetEditorMode() → AnimNodeEditModes::TwoBoneIK
↓
4. FTwoBoneIKEditMode 被激活
↓
5. 在视口中显示 IK 目标的 3D 操作手柄
↓
6. 用户拖拽手柄 → DoTranslation/DoRotation/DoScale
↓
7. 节点的运行时数据被更新 → 预览骨骼实时响应
3.5 核心技术栈
| 技术/类 | 用途 |
|---|---|
UK2Node | 蓝图图节点基类,AnimGraph 节点继承自它 |
UEdGraphSchema_K2 | 蓝图图 Schema 基类 |
FAnimNode_Base | 运行时动画节点基类 |
FKismetCompilerContext | 蓝图编译器基类 |
UBlueprintExtension | 蓝图扩展系统基类 |
FEditorModeTools | 编辑器模式管理 |
FPrimitiveDrawInterface | 3D 视口绘制接口 |
FStreamableManager | 异步资源加载(用于 PoseWatch 等) |
FGameplayTag | 游戏标签(用于 UI 层) |
FMemberReference | 函数引用(用于节点函数绑定) |
IPropertyAccessEditor | 属性访问编辑器接口 |
FStructProperty | 结构体属性反射 |
FPropertyEditorModule | 属性编辑器模块 |
3.6 架构设计优势
- 编辑器-运行时分离:
UAnimGraphNode_Base(编辑器)与FAnimNode_Base(运行时)完全分离,运行时不会携带任何编辑器开销 - 可扩展的节点体系:添加新节点只需继承
UAnimGraphNode_Base并实现相应虚函数,Schema 自动处理连线 - 扩展系统:
UAnimBlueprintExtension提供了类似"ECS 子系统"的机制,让不同功能模块独立参与编译 - 常量折叠优化:编译时自动识别并折叠常量属性,减少运行时开销
- 编辑模式可插拔:通过
FEditorModeID注册,每个节点可以独立指定自己的视口交互模式 - 属性绑定系统:统一的属性绑定机制,支持 Property 和 Function 两种绑定类型
- 图属性系统:
UAnimGraphAttributes提供了统一的属性流可视化(Pose、Curves、Attributes、Sync、RootMotion 等)
4 核心类代码详解
4.1 UAnimGraphNode_Base 类
[UAnimGraphNode_Base](file:///f:/UE/UE_5.5/Engine/Source/Editor/AnimGraph/Public/AnimGraphNode_Base.h) 是整个 AnimGraph 模块的基石。所有动画蓝图编辑器节点都继承自它,而它本身继承自 UK2Node。
4.1.1 概述
核心设计理念:
- 每个
UAnimGraphNode_Base子类内部持有一个对应运行时FAnimNode_Base子类的实例 - 编译时,编译器通过
GetFNodeType()和GetFNodeProperty()获取运行时类型信息 - 节点通过
FPoseLinkMappingRecord记录姿势链接关系
4.1.2 关键成员
// 显示的可选引脚
UPROPERTY(EditAnywhere, Category=PinOptions, EditFixedSize)
TArray<FOptionalPinFromProperty> ShowPinForProperties;
// 属性绑定(新系统使用 UAnimGraphNodeBinding)
UPROPERTY(EditAnywhere, Instanced, Category=Bindings)
TObjectPtr<UAnimGraphNodeBinding> Binding;
// 总是动态的属性(不会被常量折叠)
UPROPERTY()
TSet<FName> AlwaysDynamicProperties;
// 节点标签(可用于动画节点引用)
UPROPERTY(EditAnywhere, Category = Tag)
FName Tag;
// 函数绑定:OnInitialUpdate / OnBecomeRelevant / OnUpdate
UPROPERTY(EditAnywhere, Category = Functions)
FMemberReference InitialUpdateFunction;
UPROPERTY(EditAnywhere, Category = Functions)
FMemberReference BecomeRelevantFunction;
UPROPERTY(EditAnywhere, Category = Functions)
FMemberReference UpdateFunction;
4.1.3 核心方法
引脚管理:
AllocateDefaultPins()— 创建默认引脚(包括 Pose Link 引脚)CreateOutputPins()— 创建输出姿势引脚ReallocatePinsDuringReconstruction()— 重建时重新分配引脚GetPinAssociatedProperty()— 获取引脚关联的运行时属性
属性绑定:
IsPinBindable()— 判断引脚是否可绑定HasBinding()— 检查是否有绑定MakePropertyBindingWidget()— 创建属性绑定 UI 控件GetPinBindingInfo()— 获取引脚绑定信息
编译钩子:
OnProcessDuringCompilation()— 编译时处理节点数据OnCopyTermDefaultsToDefaultObject()— 复制默认值到 CDOOnOverrideAssets()— 子蓝图覆盖资产ValidateAnimNodeDuringCompilation()— 编译时验证ValidateAnimNodePostCompile()— 编译后验证BakeDataDuringCompilation()— 烘焙数据
编辑模式:
OnNodeSelected()— 节点选中/取消选中时调用GetEditorMode()— 返回节点对应的编辑模式 IDDraw()— 视口绘制(用于骨骼可视化)GetOnScreenDebugInfo()— 获取屏幕调试信息
调试:
FindDebugAnimNode()— 查找运行时节点实例GetDebuggedAnimNode<T>()— 获取调试中的运行时节点GetActiveInstanceNode<T>()— 获取活动实例中的运行时节点
4.1.4 FPoseLinkMappingRecord
FPoseLinkMappingRecord 是连接编辑器节点和运行时 Pose Link 的关键数据结构:
struct FPoseLinkMappingRecord
{
UAnimGraphNode_Base* LinkedNode; // 被连接的节点
UAnimGraphNode_Base* LinkingNode; // 发起连接的节点
FProperty* ChildProperty; // PoseLink 属性
int32 ChildPropertyIndex; // 数组索引(如果是数组属性)
};
编译时,编译器遍历所有 Pose Link 连接,生成 FPoseLinkMappingRecord 列表,然后在运行时通过 PatchLinkIndex() 把连接关系"写入"到运行时节点数据中。
4.2 UAnimationGraphSchema 类
[UAnimationGraphSchema](file:///f:/UE/UE_5.5/Engine/Source/Editor/AnimGraph/Public/AnimationGraphSchema.h) 继承自 UEdGraphSchema_K2,定义了动画蓝图主图的连线规则和行为。
核心功能
Pose 引脚类型管理:
IsPosePin()— 判断是否为姿势引脚IsLocalSpacePosePin()— 判断是否为本地空间姿势引脚IsComponentSpacePosePin()— 判断是否为组件空间姿势引脚MakeLocalSpacePosePin()/MakeComponentSpacePosePin()— 创建姿势引脚类型
连线规则:
DetermineConnectionResponseOfCompatibleTypedPins()— 判断两个引脚是否可以连接TryCreateConnection()— 尝试创建连接ArePinsCompatible()— 检查引脚兼容性- 自动插入空间转换节点(
LocalToComponentSpace/ComponentToLocalSpace)
拖放行为:
DroppedAssetsOnGraph()— 处理拖放动画资产到图上DroppedAssetsOnNode()— 处理拖放资产到节点上SpawnNodeFromAsset()— 从资产生成对应节点UpdateNodeWithAsset()— 更新节点的资产引用
右键菜单:
GetContextMenuActions()— 构建右键菜单
4.3 FAnimBlueprintCompilerContext 类
[FAnimBlueprintCompilerContext](file:///f:/UE/UE_5.5/Engine/Source/Editor/AnimGraph/Private/AnimBlueprintCompiler.h) 是整个动画蓝图编译的核心。它继承自 FKismetCompilerContext,负责把编辑器中的节点图翻译成可执行的 UAnimBlueprintGeneratedClass。
核心成员
// 常量数据结构和可变数据结构
UScriptStruct* NewAnimBlueprintConstants;
UScriptStruct* NewAnimBlueprintMutables;
// 节点分配映射
TMap<UAnimGraphNode_Base*, FProperty*> AllocatedAnimNodes;
TMap<UAnimGraphNode_Base*, int32> AllocatedAnimNodeIndices;
// Pose Link 映射记录
TArray<FPoseLinkMappingRecord> ValidPoseLinkList;
// 常量折叠记录
TArray<TSharedRef<FFoldedPropertyRecord>> ConstantPropertyRecords;
TArray<TSharedRef<FFoldedPropertyRecord>> MutablePropertyRecords;
// 扩展系统映射
TMap<UAnimBlueprintExtension*, FStructProperty*> ExtensionToInstancePropertyMap;
TMap<UAnimBlueprintExtension*, FStructProperty*> ExtensionToClassPropertyMap;
编译流程
ProcessAllAnimationNodes()— 遍历所有图形,找到所有UAnimGraphNode_Base节点PruneIsolatedAnimationNodes()— 从 Root 节点开始 BFS,裁剪掉没有连接到输出的孤立节点ProcessAnimationNode()— 处理单个节点:- 分配节点索引
- 创建对应的
FProperty - 分析 Pose Link 连接
- 收集常量折叠记录
- 调用
OnProcessDuringCompilation()
ProcessFoldedPropertyRecords()— 将可折叠的常量属性放入常量数据结构CopyTermDefaultsToDefaultObject()— 把编译好的数据拷贝到 CDO
常量折叠(Constant Folding)
常量折叠是编译器的关键优化。如果一个节点属性:
- 没有被引脚暴露(
bExposedOnPin == false) - 没有被引脚连接(
bPinConnected == false) - 没有被标记为总是动态(
!bAlwaysDynamic)
那么它就会被折叠到类的常量数据中,而不是放在每个实例的可变数据里。这大大减少了运行时的内存访问开销。
4.4 UAnimBlueprintExtension 扩展系统
[UAnimBlueprintExtension](file:///f:/UE/UE_5.5/Engine/Source/Editor/AnimGraph/Public/AnimBlueprintExtension.h) 是 UE5 中引入的扩展系统,它允许不同功能模块以"子系统"的方式参与动画蓝图的编译过程。
设计理念
传统方式下,所有动画蓝图逻辑都塞在 FAnimBlueprintCompilerContext 里,导致编译器越来越臃肿。扩展系统把这些逻辑拆分成独立的 UAnimBlueprintExtension 子类,每个子类负责一个特定领域。
扩展的生命周期钩子
HandleBeginCompilation() → 编译器创建时
↓
HandleStartCompilingClass() → 类开始编译
↓
HandlePreProcessAnimationNodes() → 处理节点之前
↓
HandlePostProcessAnimationNodes() → 处理节点之后
↓
HandlePostExpansionStep() → 图展开后
↓
HandleFinishCompilingClass() → 类编译完成
↓
HandleCopyTermDefaultsToDefaultObject() → 复制到 CDO
↓
HandleEndCompilation() → 编译器销毁
扩展类型一览
| 扩展类 | 用途 |
|---|---|
UAnimBlueprintExtension_Base | 基础扩展,处理节点处理的核心逻辑 |
UAnimBlueprintExtension_StateMachine | 状态机相关逻辑 |
UAnimBlueprintExtension_CachedPose | SaveCachedPose / UseCachedPose 逻辑 |
UAnimBlueprintExtension_CallFunction | 动画函数调用逻辑 |
UAnimBlueprintExtension_PropertyAccess | 属性访问(Property Access)逻辑 |
UAnimBlueprintExtension_LinkedAnimGraph | 链接动画图层逻辑 |
UAnimBlueprintExtension_LinkedInputPose | 链接输入姿势逻辑 |
UAnimBlueprintExtension_Tag | 节点标签逻辑 |
UAnimBlueprintExtension_BlendSpaceGraph | BlendSpace 图逻辑 |
UAnimBlueprintExtension_Attributes | 属性逻辑 |
数据管理
每个扩展可以声明自己的实例数据(GetInstanceData<T>())和类数据(GetClassData<T>()):
- 实例数据:每个
UAnimInstance实例一份 - 类数据:每个
UAnimBlueprintGeneratedClass一份(放在 SparseClassData 中)
4.5 FAnimGraphModule 模块入口
[FAnimGraphModule](file:///f:/UE/UE_5.5/Engine/Source/Editor/AnimGraph/Private/AnimGraphModule.cpp) 在 StartupModule() 中完成了大量初始化工作:
- 注册编辑器命令:
FAnimGraphCommands::Register() - 注册编译器:将
FAnimBlueprintCompilerContext注册为UAnimBlueprint的编译器 - 注册 PropertyAccess 绑定:将
FPropertyAccessAnimBlueprintBinding注册为模块化特性 - 注册编辑器模式:注册 10 种
FAnimNodeEditMode子类 - 注册细节面板自定义:
FAnimBlueprintDetails、FPoseDriverDetails、FAnimBlueprintFunctionPinInfoDetails - 注册图属性:注册 Pose、Curves、Attributes、Sync、RootMotion、InertialBlending 等内置图属性
- 注册图自定义:
FAnimGraphDetails
5 功能使用示例编写
示例1:自定义一个简单的动画蓝图节点
假设要创建一个新的动画蓝图节点 UAnimGraphNode_MyCustomNode,对应运行时节点 FAnimNode_MyCustomNode。
// MyCustomAnimGraphNode.h
#pragma once
#include "CoreMinimal.h"
#include "AnimGraphNode_Base.h"
#include "AnimNode_MyCustomNode.h" // 运行时节点
#include "MyCustomAnimGraphNode.generated.h"
UCLASS()
class MYGAMEEDITOR_API UMyCustomAnimGraphNode : public UAnimGraphNode_Base
{
GENERATED_BODY()
public:
// 运行时节点实例(编译器通过它获取类型信息)
UPROPERTY(EditAnywhere, Category = Settings)
FAnimNode_MyCustomNode Node;
// UAnimGraphNode_Base 接口
virtual FLinearColor GetNodeTitleColor() const override
{
return FLinearColor(0.2f, 0.8f, 0.2f);
}
virtual FText GetTooltipText() const override
{
return NSLOCTEXT("MyGame", "MyCustomNode_Tooltip", "我的自定义动画节点");
}
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override
{
return NSLOCTEXT("MyGame", "MyCustomNode_Title", "My Custom Node");
}
virtual FText GetMenuCategory() const override
{
return NSLOCTEXT("MyGame", "MyCustomNode_Category", "My Game");
}
// 创建输出姿势引脚
virtual void CreateOutputPins() override
{
CreatePin(EGPD_Output, UAnimationGraphSchema::MakeLocalSpacePosePin(), TEXT("Pose"));
}
// 编译时处理
virtual void OnProcessDuringCompilation(
IAnimBlueprintCompilationContext& InCompilationContext,
IAnimBlueprintGeneratedClassCompiledData& OutCompiledData) override
{
// 处理输入姿势链接
// ...
}
};
示例2:为骨骼控制节点添加自定义编辑模式
// MyCustomEditMode.h
#pragma once
#include "CoreMinimal.h"
#include "AnimNodeEditMode.h"
class FMyCustomEditMode : public FAnimNodeEditMode
{
public:
// 返回编辑模式 ID
virtual FEditorModeID GetEditorModeID() const override
{
return TEXT("MyGame.MyCustomEditMode");
}
// 视口渲染
virtual void Render(const FSceneView* View, FViewport* Viewport,
FPrimitiveDrawInterface* PDI) override
{
// 绘制骨骼位置、IK 目标等
FAnimNodeEditMode::Render(View, Viewport, PDI);
}
// 处理用户输入
virtual bool HandleClick(FEditorViewportClient* InViewportClient,
HHitProxy* HitProxy, const FViewportClick& Click) override
{
// 处理骨骼点击
return FAnimNodeEditMode::HandleClick(InViewportClient, HitProxy, Click);
}
};
然后在模块 StartupModule() 中注册:
FEditorModeRegistry::Get().RegisterMode<FMyCustomEditMode>(
TEXT("MyGame.MyCustomEditMode"),
NSLOCTEXT("MyGame", "MyCustomEditMode", "My Custom Edit Mode"),
FSlateIcon(),
false
);
示例3:创建自定义动画蓝图扩展
// MyAnimBlueprintExtension.h
#pragma once
#include "CoreMinimal.h"
#include "AnimBlueprintExtension.h"
#include "MyAnimBlueprintExtension.generated.h"
UCLASS()
class UMyAnimBlueprintExtension : public UAnimBlueprintExtension
{
GENERATED_BODY()
public:
// 在实例数据中添加自定义数据
UPROPERTY()
float MyCustomData = 0.0f;
protected:
// 编译开始时初始化
virtual void HandleBeginCompilation(
IAnimBlueprintCompilerCreationContext& InCreationContext) override
{
Super::HandleBeginCompilation(InCreationContext);
MyCustomData = 0.0f;
}
// 在所有节点处理完成后执行
virtual void HandlePostProcessAnimationNodes(
TArrayView<UAnimGraphNode_Base*> InAnimNodes,
IAnimBlueprintCompilationContext& InCompilationContext,
IAnimBlueprintGeneratedClassCompiledData& OutCompiledData) override
{
// 遍历所有节点,收集自定义数据
for (UAnimGraphNode_Base* Node : InAnimNodes)
{
// 处理逻辑...
}
}
};
示例4:注册自定义图属性
// 在模块的 StartupModule() 中
void FMyGameModule::StartupModule()
{
UAnimGraphAttributes* AnimGraphAttributes = GetMutableDefault<UAnimGraphAttributes>();
AnimGraphAttributes->LoadConfig();
AnimGraphAttributes->Register(
FAnimGraphAttributeDesc(
TEXT("MyGame.MyCustomAttribute"),
EAnimGraphAttributeBlend::Blendable,
FSlateBrush(),
NSLOCTEXT("MyGame", "MyAttribute", "My Attribute"),
NSLOCTEXT("MyGame", "MyAttributeTip", "My custom graph attribute"),
FLinearColor::Blue,
EAnimGraphAttributesDisplayMode::Automatic)
);
}
示例5:通过 Tag 引用动画节点
// 在动画蓝图中,给某个节点设置 Tag(如 "MyPoseNode")
// 然后在另一个节点中通过 AnimNodeReference 引用它:
void UMyAnimInstance::SomeFunction()
{
// 通过 AnimNodeReference 获取标记的节点
FAnimNodeReference NodeRef = GetAnimNodeReference(TEXT("MyPoseNode"));
// 使用 NodeRef...
}
6 总结与最佳实践
核心要点
-
AnimGraph 是动画蓝图的"编辑器层"
- 它不参与运行时计算,只在编辑器中工作
- 每个编辑器节点(
UAnimGraphNode_Base)对应一个运行时节点(FAnimNode_Base) - 编译器(
FAnimBlueprintCompilerContext)是两者之间的桥梁
-
扩展系统是架构的核心创新
UAnimBlueprintExtension让不同功能模块独立参与编译- 每个扩展有自己的实例数据和类数据
- 新功能应该优先考虑通过扩展系统实现,而不是直接修改编译器
-
常量折叠是重要的性能优化
- 编译时自动识别常量属性
- 放在类级别数据中,避免每个实例都存一份
AlwaysDynamicProperties可以阻止不需要的折叠
-
属性绑定是节点与蓝图通信的关键
- 支持 Property 绑定和 Function 绑定
- 通过
PropertyAccess系统高效实现 - 绑定信息在编译时处理,运行时零开销
-
编辑模式让视口交互成为可能
- 每种骨骼控制节点可以有自己的编辑模式
- 通过
FEditorModeRegistry注册 - 实现了 IK 拖拽、骨骼选择等可视化操作
最佳实践
-
创建自定义节点
- 继承
UAnimGraphNode_Base(或UAnimGraphNode_SkeletalControlBase/UAnimGraphNode_AssetPlayerBase) - 重写
GetNodeTitleColor()、GetTooltipText()、GetNodeTitle()提供视觉信息 - 重写
CreateOutputPins()创建输出姿势引脚 - 重写
OnProcessDuringCompilation()处理编译逻辑 - 重写
GetMenuCategory()归入正确的菜单分类
- 继承
-
使用扩展系统
- 对于新的子系统功能,创建
UAnimBlueprintExtension子类 - 利用生命周期钩子在正确的时机执行逻辑
- 通过
GetInstanceData<T>()/GetClassData<T>()管理数据
- 对于新的子系统功能,创建
-
属性绑定
- 优先使用
PropertyAccess绑定而非老式的PropertyBindings系统 - 对于需要频繁更新的属性,标记为
AlwaysDynamicProperties - 使用
IsPinExposedAndLinked()检查引脚状态
- 优先使用
-
编辑模式
- 继承
FAnimNodeEditMode创建自定义编辑模式 - 通过
FEditorModeRegistry::Get().RegisterMode()注册 - 在节点中重写
GetEditorMode()返回对应的模式 ID
- 继承
-
图属性
- 使用
UAnimGraphAttributes注册自定义属性类型 - 指定
EAnimGraphAttributeBlend和EAnimGraphAttributesDisplayMode - 在节点中重写
GetInputLinkAttributes()/GetOutputLinkAttributes()声明属性流
- 使用
-
调试
- 使用
GetDebuggedAnimNode<T>()在编辑器中获取运行时节点 - 重写
GetOnScreenDebugInfo()输出调试信息 - 使用
FindDebugAnimNode()在预览骨骼网格上查找节点
- 使用
常见问题
问题1:节点在编译后没有出现在生成的类中
- 检查节点是否被
PruneIsolatedAnimationNodes()裁剪 - 确保节点通过 Pose Link 连接到了 Root 节点
- 检查
IsSinkNode()的返回值
问题2:属性绑定不生效
- 检查
IsPinBindable()是否返回 true - 确认绑定信息在编译时被正确处理
- 检查
GetPinBindingInfo()的返回值
问题3:自定义编辑模式无法激活
- 确认在
StartupModule()中正确注册了编辑模式 - 检查节点的
GetEditorMode()返回值与注册的 ID 一致 - 确认
OnNodeSelected()被正确调用
问题4:常量折叠导致属性无法运行时修改
- 将该属性添加到
AlwaysDynamicProperties - 或者将该属性暴露为引脚并连线
附录:相关资源
AnimGraphRuntime模块 — 运行时动画节点定义AnimationBlueprintEditor模块 — 动画蓝图编辑器主体BlueprintGraph模块 — 蓝图图编辑基础设施KismetCompiler模块 — 蓝图编译器基础设施- UE5 官方文档:Animation Blueprint

434

被折叠的 条评论
为什么被折叠?



