很多做游戏动画和角色表现的技术同学,近几年应该都有同感:传统 AnimGraph 状态机的维护成本越来越高。角色技能一多、动作层一复杂,动画蓝图里密密麻麻的连线、同步状态、各类转换条件,很容易变成只有原作者才敢碰的“意大利面”。UE 5.4 引入的 AnimNext 与动画神经网络控制器,正是官方针对这套痛点给出的下一代动画系统方案。本文会从状态机的历史局限讲起,梳理 AnimNext 的核心设计理念,再通过一个可落地的实战案例演示如何创建分层动画图、接入神经网络控制器,并整理迁移过程中的常见问题与工程化建议。无论你是刚接触虚幻引擎 5 的动画新手,还是在存量项目里被动画蓝图折磨过的技术美术,都能从中找到一套清晰的切入路径。
1. AnimNext 是什么:一次动画系统的重构尝试
1.1 传统状态机的光亮与阴影
状态机本身是一个非常经典的计算模型,它由“状态、事件、迁移”三要素组成。在 C 语言开发里,我们经常用枚举加 switch 实现三段式状态机;在 MCU 裸机程序里,用状态机处理按键扫描和通信协议;在 Simulink 里,Stateflow 也是用状态机描述控制逻辑。可以说,状态机是嵌入式、游戏逻辑、协议解析等大量领域的基础编程范式。
传统动画系统也采用了状态机:基础状态是 Idle、Walk、Run、Jump、Attack,事件是输入条件、动画通知、物理查询,转换条件是速度、朝向、技能 ID 等。这套模型在小规模角色上非常直观,但一旦项目变大,问题就暴露出来:
- 状态数量增长很快,状态迁移矩阵呈指数级复杂。
- 全局状态之间互相影响,比如“是否持有武器”“是否处于受伤状态”“当前运动模式”,全部挤在一个平面里。
- 美术和程序同时修改动画蓝图时,冲突几乎不可避免。
- 可视化调试能力有限,运行中很难一眼看出当前处于哪些状态、下一步可能跳到哪里。
这不是状态机的错,而是动画系统的问题域本身就带有多层级、多条件、并行混合的特点。用一个平铺的状态集合去表达所有行为,等价于用一张巨大的表去穷举所有可能性,自然难以维护。
1.2 AnimNext 的定位:动画系统而不是单个节点
AnimNext 的完整名字很长,常见称呼是“AnimNext 动画神经网络控制器”。它并不是 AnimGraph 里新加的一个节点,而是一整套面向下一代动画生产流程的框架。
它试图用“分层状态机 + 数据驱动 + 并行求值 + 神经网络推理”来替代传统“单一全局状态机 + 动画蓝图变量 + 混合空间”的组合。在 AnimNext 的体系里,动画图本身是一种可复用的资产,而不是只能挂在角色蓝图里的“接线板”。状态不再是你必须一次性罗列完的所有动作,而是可以被拆分成多个相互独立的层:移动层只管移动,上半身层只管武器和交互,反应层只管受击和表情,最后由混合机制把各层结果合成最终姿势。
这么做的一个直接好处是:你不再需要在一个巨大的状态机里协调所有逻辑。移动层改变速度条件时,不会影响上半身层的出招逻辑;上半身层增加一个翻滚动作,也不会导致移动状态机发生连锁迁移。每个层可以被独立测试、独立复用,也可以作为独立资产交给不同的人维护。
1.3 动画神经网络控制器在其中的角色
动画神经网络控制器并不是“让 AI 帮你做动画”,而是一套把神经网络推理集成到动画管线中的方案。核心思路是:用一个训练好的神经网络模型,替代一部分传统动画选择逻辑或姿势生成逻辑。
以角色移动为例,传统做法是:计算当前速度,在 BlendSpace 中按速度方向在地面、斜坡、台阶之间插值,再通过转向状态切换确定旋转速度。换成神经网络控制器后,可以将输入特征(速度、朝向、移动方向、地形高度、角色朝向)送入网络,直接输出下一帧的姿势偏移或骨骼修正。视觉表现上会更自然,转弯、变速、上下坡的过渡更连续,不需要手写大量 BlendSpace 和插值规则。
当然,神经网络并不能凭空生成所有细节,它通常仍然依赖一组基础动画序列作为参考姿势或样本,网络负责在推理时选择、混合和修正。这也解释了为什么官方将 AnimNext 重点放在“控制器”上:它控制的是动画决策过程,而不是模型资产的自动生产。
2. 环境准备与版本说明
2.1 引擎版本选择
AnimNext 最早在 UE 5.4 版本中作为实验性插件引入,后续版本中持续迭代。由于它目前仍然带有实验属性,接口、菜单路径和资产格式都可能发生变化,因此不建议在生产项目里直接全量切到 AnimNext,更适合先在测试项目或新玩法原型里验证。
本文示例以 UE 5.4 及以上版本为例,推荐使用 5.5 或更新版本以获得更稳定的编辑体验。如果你使用的是更新版本,部分菜单名称和节点名可能会调整,重点理解设计思路而不是死记菜单路径。
2.2 启用相关插件
打开虚幻引擎的项目,依次进入“编辑”->“插件”,在搜索栏输入 AnimNext,启用对应插件。如果项目中需要使用神经网络推理能力,还需要确认以下插件处于启用状态:
| 插件名称 | 用途 |
|---|---|
| AnimNext | 提供 AnimNext 动画图、分层状态机等核心资产 |
| Animation Neural Network Inference | 提供神经网络运行时推理与模型导入能力 |
| Neural Network Model Asset | 管理训练好的网络模型资产,供动画图推理 |
启用插件后需要重启编辑器,之后才能在内容浏览器中看到 AnimNext 相关资产创建入口。
2.3 项目结构规划
建议从第三人称模板或第一人称模板起步,因为这些模板自带一个带骨骼网格体的角色蓝图,便于快速替换动画逻辑。项目目录可以这样规划:
Content/ |-- Characters/ | |-- Hero/ | | |-- AnimNext/ | | | |-- ABP_Hero_AnimNext.uasset | | | |-- StateMachine_Locomotion.uasset | | | |-- Model_HeroLocomotion.nni | | |-- Meshes/ | | |-- Blueprints/其中 AnimNext 文件夹专门存放动画图、分层状态机、神经网络模型资产,让动画相关文件与角色逻辑、网格体分开,后续做资产迁移和查找会更高效。
3. 核心原理拆解:从状态机到分层动画图
3.1 传统状态机的三段式结构与动画的冲突
在程序里写三段式状态机时,结构通常是:
enum class EHeroState { Idle, Walk, Run, Jump, Attack }; EHeroState CurrentState = EHeroState::Idle; void UpdateHero(float DeltaTime) { switch (CurrentState) { case EHeroState::Idle: // 检测输入,决定是否切换到 Walk / Run / Jump break; case EHeroState::Walk: // 根据速度切换 Run / Idle break; case EHeroState::Run: // 检测跳跃、攻击、转向 break; } }这种结构清晰、执行效率高,在 MCU、嵌入式场景里非常合适,因为状态数量可控、事件来源确定。但动画系统的问题在于“状态”并不互斥:角色可以一边跑一边攻击,也可以一边处于受伤状态一边转向。把它们强行压成一个平面状态机,就会出现大量组合状态,比如:
AttackWhileRunning AttackWhileJumping HitWhileRunning HitWhileAttacking HitWhileRunningAndAttacking组合一旦多起来,状态迁移条件和进入退出逻辑就会交织在一起,维护成本急剧上升。
3.2 AnimNext 的分层状态机思想
AnimNext 把状态机拆成多个层次,每一层只关心一个维度的问题。
假设一个角色有移动层、行为层、反应层:
- 移动层:负责 Idle、Walk、Run、Crouch 等基础运动状态,输入是速度、加速度、朝向。
- 行为层:负责攻击、跳跃、翻滚、使用物品等动作状态,输入是技能请求、动作标记。
- 反应层:负责受击、倒地和表情反馈状态,输入是伤害事件、血量变化。
每个层独立运行,最终通过权重混合把所有层的结果合成一个输出姿势。这样,组合状态不再需要显式地出现在状态图里:移动层在 Run,行为层在 Attack,反应层在 Hit,呈现出来的效果就是“跑动中攻击并被打到”,但三个层各自的状态图并没有因此产生额外的迁移分支。
分层状态机的关键收益是可组合性。新增一个“毒区挣扎”层,完全不需要修改移动层和攻击层的任何状态,只需要定义好该层的输入条件与混合权重即可。
3.3 继承状态机与复用
AnimNext 进一步引入了继承状态机的概念,让基础状态逻辑能够被多个子状态机复用。可以把“继承状态机”理解成一个带默认实现的状态机模板。父状态机定义了通用状态和转换逻辑,子状态机在其基础上追加状态或覆盖行为,而不会破坏父状态机的稳定性。
比如一个通用的移动层状态机可以被人类敌人、队友角色、BOSS 共用。人类敌人子状态机增加“巡逻”“警戒”状态,BOSS 子状态机增加“狂暴”“召唤”状态,三者仍然共享底层的 Idle、Walk、Run 实现。这种复用方式在传统 AnimGraph 中很难做到,因为传统动画蓝图通常是单份资产直接写死逻辑,复用只能靠复制粘贴和参数重连。
3.4 动画神经网络控制器的推理链路
动画神经网络控制器的工作链路可以简化成三部分:输入特征提取、神经网络推理、姿势输出适配。
输入特征通常包括骨骼位置、速度、朝向、动画曲线值、角色胶囊体运动状态;神经网络输出可以是骨骼的旋转修正量、混合权重、甚至直接是一组目标骨骼姿势。引擎在拿到输出后,会把它们应用到基础骨骼姿势上,再通过动画图后续节点做物理修正、IK、叠加动画。
在实际工程中,模型通常不在 UE 编辑器里训练,而是在 Python 生态中完成数据采集、预处理、训练和导出。引擎侧通过 Animation Neural Network Inference 插件加载模型,在 AnimNext 动画图中作为一个控制节点或推理节点运行。如果你的项目还没有现成模型,可以先在传统状态机中跑通流程,将 AnimNext 作为替换层逐步验证。
4. 完整实战案例:搭建一套 AnimNext 动画图
这一节我们用一个第三人称角色示例,走通 AnimNext 的核心流程:创建动画图资产、配置分层状态机、接入外部数据接口、加载神经网络控制器、运行验证。整个过程以编辑器操作为主,少量代码用于打通数据链路。
4.1 创建 AnimNext 动画蓝图资产
在内容浏览器中,切换到 AnimNext 目录,右键选择 AnimNext 动画蓝图。命名建议用 ABP_Hero_AnimNext,方便与旧版动画蓝图区分。
创建完成后,双击打开 AnimNext 工作空间。你会看到类似动画蓝图的编辑区域,但顶部组织逻辑不同。AnimNext 工作空间左侧一般是层列表和状态列表,中间是逻辑图表,右侧是细节面板。这个布局对应的是“层优先”的设计理念:先划分层,再为每个层编写状态逻辑。
先建立最基础的三个层:
- LocomotionLayer:角色移动层,负责 Idle、Walk、Run。
- ActionLayer:行为层,负责攻击、翻滚等动作。
- ReactionLayer:反应层,负责受击状态。
创建方式是在层管理区域点击添加层,并为层指定类型。每个层最终会输出一个姿势,它们按权重混合。
4.2 配置移动层状态逻辑
在 LocomotionLayer 内部建立一个状态机图。和传统 AnimGraph 的 State Machine 类似,创建三个状态:Idle、Walk、Run,并为每个状态指定对应的动画序列或混合空间。
注意,状态转换条件不再使用传统动画蓝图里的“转换规则”连线,而是可以在层状态机中添加条件规则。条件规则可以读取动画图变量、外部数据提供者提供的数值、以及事件驱动标记。
比如:
- Idle 到 Walk:当 Speed > 10 时触发。
- Walk 到 Run:当 Speed > 300 时触发。
- Run 到 Walk:当 Speed <= 300 时触发。
- Walk 到 Idle:当 Speed <= 10 时触发。
为了让这些条件起作用,我们还需要一个数据源来提供 Speed 值。这个数据源可以是角色蓝图中的变量,也可以是下一小节中的接口提供者。
4.3 设计外部数据接口与代码接线
AnimNext 的一个重要改进是把动画决策与外部逻辑解耦。为了让动画图能够获取角色速度、是否在地面等信息,可以定义一个动画图接口。
下面是一个 C++ 接口示例,用于为动画图提供外部输入数据:
// 文件路径:Source/MyProject/Animation/AnimNextInputProvider.h UINTERFACE(BlueprintType, Blueprintable) class UAnimNextInputProvider : public UInterface { GENERATED_BODY() }; class IAnimNextInputProvider { GENERATED_BODY() public: // 获取当前移动速度 UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "AnimNext") float GetMoveSpeed() const; // 是否在地面上 UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "AnimNext") bool IsOnGround() const; // 是否处于受击状态 UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "AnimNext") bool IsInReaction() const; };在角色蓝图或组件中实现这个接口,并返回角色当前的运动状态值。然后在 AnimNext 动画图中,通过接口节点或属性访问器来读取这些值,并赋值给状态转换条件使用的变量。
如果项目没有走到 C++ 阶段,也可以用 Pure Blueprint 接口实现同样的效果。思路一样:把“数据是怎么来的”和“数据如何使用”分离开,这样动画图资产本身不依赖具体角色实现,换一套角色数据源也不会改动画图内部逻辑。
4.4 接入动画神经网络控制器
现在进入控制器的接入阶段。这里分两种情况。
情况一:你手上已经有一个训练好的神经网络模型,用于移动姿势控制。这种情况需要在内容浏览器中导入模型资产文件,格式通常是引擎支持的 NN NNI 或 ONNX。导入后,在 AnimNext 动画图中创建一个神经网络控制器节点,指定模型资产,并将输入特征链接到角色数据接口输出上。
情况二:项目还没有训练好的模型。我们可以先留出控制器节点位置,用传统混合空间作为过渡,后续再引入模型。这种渐进式切法在生产上更稳妥,降低实验性功能带来的风险。
下面是一个训练侧的伪代码示例,帮助你理解神经网络模型是如何产出的:
# 训练阶段示意(Python) # 特征:当前速度、目标速度、朝向夹角、是否跳跃 # 标签:下一帧骨骼偏移或动画权重 import numpy as np features = np.load("locomotion_features.npy") labels = np.load("locomotion_labels.npy") model = build_pose_network(input_dim=8, output_dim=64) model.compile(optimizer="adam", loss="mse") model.fit(features, labels, epochs=200, batch_size=256) # 导出为引擎可加载格式(具体格式取决于引擎版本与NNI插件) export_model(model, "hero_locomotion")需要说明的是:模型导出格式、特征维度、网络结构都需要根据你自己的数据和目标进行设计,上述代码只是流程示意,不能直接用于正式训练。引擎端神经网络推理引擎负责加载模型并逐帧运行,因此推理性能、模型大小、运行帧率都需要在真机上进行测量和调优。
4.5 在角色蓝图上指定 AnimNext 动画图
回到角色蓝图,找到动画相关设置。传统项目里这里通常指定一个动画蓝图类(AnimBP),AnimNext 项目则改为指定 AnimNext 动画图资产。
将之前创建的 ABP_Hero_AnimNext 指定给角色,然后编译保存。之后运行项目,角色应该会使用新的动画图来播放移动和攻击动画。
如果运行后角色没有任何动画,优先检查以下几点:
- AnimNext 动画图资产是否已经正确指定。
- 数据接口返回的速度值是否为预期范围。
- 各状态是否分配了有效的骨骼动画序列。
- 层的混合权重是否为零或过低,导致输出姿势被其他层覆盖。
4.6 运行与验证
运行游戏后,可以使用控制台命令“stat animation”观察动画系统开销。此时注意观察:
- 角色移动时,移动层状态是否在 Idle、Walk、Run 之间正常切换。
- 执行翻滚时,行为层是否会覆盖移动层输出并再次恢复。
- 触发受击事件时,反应层是否会即时插入受击动画,并在结束后平滑过渡。
- 如果接入了神经网络控制器,增加一个 AI 控制角色后,观察其移动转弯是否比传统混合空间更平滑。
5. 常见问题与排查思路
AnimNext 仍处于快速迭代期,使用过程中遇到未知报错是常态。下面整理几类经典问题,并给出排查思路。
| 问题现象 | 常见原因 | 解决与排查思路 |
|---|---|---|
| 找不到 AnimNext 启动入口 | 引擎版本过低,插件未启用 | 确认使用 UE 5.4 及以上版本;在插件列表中搜索 AnimNext 并启用后重启编辑器 |
| 动画图编译报错 | 变量类型不匹配、接口节点未绑定 | 查看输出日志中报错的节点名,检查接口返回值类型,统一为浮点型、布尔型或枚举型 |
| 角色处于 T-Pose 或完全不播放动画 | AnimNext 图未指定、模型没有正确骨骼绑定 | 在角色蓝图中查看动画资产引用;检查状态是否分配了动画序列;检查输出逻辑链中是否有断开节点 |
| 状态切换不稳定、来回抖动 | 转换条件阈值设置不合理 | 增加滞回区间,例如进入 Run 需要 Speed>300,退出 Run 需要 Speed<260,形成迟滞 |
| 神经网络推理帧率过低 | 模型结构过大、输入特征过多、每个角色单独跑一次推理 | 压缩模型大小,减少输入维度;考虑批处理推理;对大场景中的少数角色使用简化控制器 |
| 资产迁移后引用丢失 | 目标环境缺少同名插件或模型资产 | 先迁移模型资产和依赖资产,再迁移 AnimNext 动画图;使用资产重定向工具修复引用 |
5.1 状态来回抖动的处理建议
状态切换抖动在传统状态机里也很常见。AnimNext 分层结构虽然拆分了逻辑,但临界条件仍可能造成抖动。推荐在条件规则中引入“进入阈值”和“退出阈值”。例如 Speed 在 10 到 300 之间都可以是 Walk,但进入 Walk 需要达到 10,退出 Walk 需要低于 8。这样即使数值在边界小幅波动,也不会频繁触发状态迁移。
5.2 神经网络控制器结果异常
如果神经网络推理输出的姿势明显不对,可以先做一个“空网络对照实验”:将控制器节点替换为常量输出,或使用参考姿势节点,确认动画图其余部分正常。随后逐步放开网络输入特征,观察哪个特征导致了异常。常见问题是特征未归一化,导致网络输入分布与训练时不一致,输出剧烈漂移。训练时记录特征的均值和标准差,在引擎侧做相同的归一化变换。
6. 最佳实践与工程建议
AnimNext 提供了很灵活的框架,但如果组织不当,实验性的自由也会带来新的混乱。下面几条建议来自真实项目迁移的常见经验,值得提前考虑。
6.1 层的划分应该以“可独立维护”为标准
不是所有动画逻辑都需要拆层。拆分层的标准是:该层是否可以独立修改而不影响其他层。移动层与行为层之间往往只通过混合权重和少量标记交互,适合拆层;如果两个状态集合之间存在复杂的因果联系,拆到同一层反而更直观。
建议每层状态数控制在 5 到 8 个以内。层内状态过多,说明这个层承担的职责过重,应该继续拆分子层。
6.2 数据驱动优先,事件驱动兜底
AnimNext 的架构里,状态切换最好由数据条件驱动,例如速度、朝向、Bool 标记。事件驱动的优点是响应及时,缺点是难以调试和预测。在动画系统中,尽量用“当前状态 + 连续输入”推导下一状态,而不是到处抛出瞬时事件。时刻跟踪动画层的输入数据,往往比追踪一个事件链更容易定位问题。
6.3 保留传统动画蓝图的回滚通道
AnimNext 目前还比较年轻,生产环境切到新系统前,建议保留一份旧版动画蓝图资产作为回滚通道。可以将旧蓝图放在 Content/Characters/Hero/Legacy 目录下。如果新系统遇到无法短期解决的性能或稳定性问题,可以快速将角色的动画类切回传统动画蓝图,不至于阻塞版本发布。
6.4 关注性能与内存
AnimNext 的目标之一是支持大规模角色并行处理,但并行求值并不意味着可以无视性能开销。在项目里同时激活大量 AnimNext 动画图角色时,需要用动画统计工具观察 CPU 耗时。不同层、不同状态是否缓存、神经网络推理是否能够合批,都会影响最终帧率。
如果你的游戏里有大量 AI 角色同时可见,建议为角色设置 LOD 级别的动画策略:近距离使用完整分层 AnimNext 图,远距离切换到采样率更低的简单动画循环。
6.5 命名与规范
资产命名的核心是让所有人不需要打开就能知道这是什么。建议格式:
ABP_角色_用途 AnimNext 动画蓝图资产 SM_角色_层_功能 分层状态机资产 NN_角色_用途 神经网络模型资产层内状态也用统一前缀,比如 Locomotion_Idle、Locomotion_Walk、Action_Attack,方便在数据统计和日志中快速筛选。
6.6 版本兼容与团队协作
AnimNext 插件仍处于实验性阶段,团队中使用时要约定统一引擎版本和插件版本。多人同时编辑同一张 AnimNext 动画图时,建议以层作为最小编辑单元,每人负责一个层文件,减少合流冲突。动画图之间通过接口引用数据,同一层内部尽量不直接引用另一个层的内部状态,保持层间松耦合。
7. 总结与学习路线
从传统状态机到 AnimNext,改变的并不只是 UI 布局,而是动画系统的设计理念:从“平铺状态”转向“分层解耦”,从“手写转换规则”转向“数据驱动 + 神经网络推理”。传统状态机的三段式结构在嵌入式、MCU、Simulink 等领域依然是非常成熟有效的范式,但在游戏动画的高维度组合场景下,AnimNext 的分层状态机和神经网络控制器给出了更贴合问题本质的解法。
本文梳理了 AnimNext 的核心概念、环境准备、分层动画图构建、外部数据接口设计以及神经网络控制器的接入思路,最后提供了一套可用于实战的排查清单和工程规范。如果你正准备在新项目里尝试 AnimNext,建议先做一个小规模的测试角色,跑通移动层和神经网络控制器的链路,再逐步扩展到行为层与反应层。遇到编译报错或资产问题不要慌,优先检查插件版本、数据接口返回值和控制器的模型输入格式,大部分问题都能在这些环节找到原因。
入手之后,可以进一步深入官方动画文档中关于继承状态机和动画图资产复用的章节,并尝试把项目中复杂度最高的角色动画迁移过来做压力测试。只有真正把一名角色的动画逻辑完整迁到 AnimNext 之后,你才能感受到分层状态机和数据驱动设计带来的维护性提升。这个迁移过程可能会踩一些实验性功能的坑,但方向是对的:动画系统的复杂度,不应该靠更庞大的状态图来硬扛,而应该靠更合理的架构来化解。