一、AnimBlueprint 的两层结构
AnimBlueprint 不是一个大的蓝图文件——它有两个完全独立的图:
AnimBlueprint├── EventGraph ← 更新动画参数(每帧 Tick)│ └── BlueprintUpdateAnimation → 从 Character 读速度、方向、是否在空中│ → 写入 AnimInstance 的变量└── AnimGraph ← 根据参数混合动画(输出每帧 Pose) ├── State Machine → 决定播 Idle / Walk / Run / Jump ├── BlendSpace → 根据参数连续混合动画 ├── IK / Bone Transform → 修正骨骼位置 └── Output Pose → 送到渲染管线关键:EventGraph 每帧跑在 Game Thread 上。它从 APawn 读数据(速度、是否按下跳跃键),写到 UAnimInstance 的成员变量。AnimGraph 在 Worker Thread 上消费这些变量做动画混合。两者完全解耦。
二、State Machine:动画状态的”交通灯”
1 状态定义
每个 State 绑定一个动画(或 BlendSpace):
[Idle/Rifle/Hip] → 播放持枪待机动画[Walk/Rifle] → 播放持枪走动画[Run/Rifle] → 播放持枪跑动画[Jump/Start] → 起跳动画[Jump/Loop] → 空中循环[Jump/Land] → 落地动画2 Transition 规则
Idle → Walk: Speed > 0.1(开始移动)Walk → Idle: Speed < 0.05(停下)Walk → Run: Speed > 400(加速到跑)Run → Walk: Speed < 300(减速到走)
条件由 C++ 侧每帧更新到 AnimInstance 的变量驱动: Speed, Direction, bIsInAir, bIsCrouching...Transition 不是瞬间切换——每个 Transition 有 Blend Duration(如 0.15 秒)。0.15 秒内 Weight 从 0 到 1,两个动画平滑过渡。
3 BlendSpace:连续参数空间
不是简单的 if Speed > 400——用 BlendSpace 把走和跑之间的速度映射为连续的混合比例:
BlendSpace_Locomotion: 横轴 = Speed (0 → 600) 纵轴 = Direction (-180° → 180°,向前/向后/向左/向右)
Speed=300, Dir=0 → WalkForward(60%) + JogForward(40%) Speed=300, Dir=90 → WalkForward(30%) + WalkRight(70%) Speed=600, Dir=0 → SprintForward(100%)策划可以在 BlendSpace 编辑器里拖动采样点观察混合效果——不需要程序员改代码。
三、AnimNotify / AnimNotifyState
动画不只是”播放”——需要在特定帧触发事件。比如:
Attack Animation(1 秒,30 帧): Frame 10 → AnimNotify: EnableWeaponCollision (开始检测武器碰撞) Frame 20 → AnimNotify: DisableWeaponCollision (关闭碰撞检测) Frame 15 → AnimNotify: SpawnSlashEffect (生成刀光特效)Notify 的 C++ 侧
// AnimNotify 在动画资源里配置,触发时调用这个函数void UAnimNotify_EnableWeaponCollision::Notify( USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) {
ACharacter* Owner = Cast<ACharacter>(MeshComp->GetOwner()); if (Owner && Owner->HasAuthority()) { // 只在服务端处理碰撞 // 给角色的武器 Component 开启碰撞 UWeaponComponent* Weapon = Owner->FindComponentByClass<UWeaponComponent>(); Weapon->EnableCollision(); }}Notify 和网络:伤害检测通常在服务端——Notify 触发时要检查 HasAuthority()。客户端的 Notify 只播放 VFX/SFX(特效音效),不管碰撞。
AnimNotifyState:有持续时间的 Notify
// 不是一瞬间——有 Begin / Tick / Endclass UAnimNotifyState_WeaponTrail : public UAnimNotifyState { void NotifyBegin(...) { StartTrailEffect(); } void NotifyTick(...) { UpdateTrailPosition(); } void NotifyEnd(...) { StopTrailEffect(); }};四、Root Motion——用动画驱动移动
1 问题
角色播放攻击动画时,如果玩家按了 W 键,角色该不该往前走?
传统方案:移动 和 动画 完全分离——CharacterMovement 控制位置,动画只是播放视觉效果。缺点是”脚底打滑”——播放 Walk 动画但角色移动速度不匹配。
2 Root Motion 的做法
让动画本身告诉引擎”这一帧我移动了多少”:
动画里,Root 骨骼有位移数据(制作时自然就有——Walk 动画里 Root 骨自然会前移) ↓UE 在 AnimBlueprint 里勾选 EnableRootMotion ↓每帧,AnimInstance 计算 Root 骨的 ΔTransform ↓传给 CharacterMovement → 驱动角色实际移动 ↓结果:动画和移动天然同步——走的动画 = 走的速度,脚底不会打滑3 什么时候用 Root Motion
| 适用 | 不适用 |
|---|---|
| 近战攻击连招(动画驱动位移) | 玩家自由移动(WASD,不能用 Root Motion 锁定) |
| 翻滚/闪避(固定距离的位移) | 平台跳跃(需要精确控制距离) |
| 处决动画(完全接管角色控制) | 竞速游戏 |
Root Motion 的问题是”玩家失去了控制权”——动画期间不能移动、不能转向。这对动作游戏是可以接受的(出招时本来就不该移动),但对于开放世界跑图是灾难。
五、Layered Blend——上半身和下半身分离
角色移动时可以做攻击——上半身播攻击动画,下半身继续 Walk/Idle:
AnimGraph: Base Pose = BlendSpace_Locomotion(Speed, Direction) ← 全身移动 Upper Body = Animation_FireWeapon ← 上半身攻击 Final Pose = LayeredBlendPerBone(BasePose, UpperBody, Spine_01, // 从这根骨头往上,全用 UpperBody 1.0) // 混合权重Layered Blend Per Bone 的核心思想:不同的骨头跑不同的动画。下半身走移动 BlendSpace,上半身播攻击动画。中间用 Spine 骨头做平滑过渡(不然腰那里会有断痕)。
六、IK(Inverse Kinematics)——让脚贴地
IK 解决的问题:角色站在楼梯/斜坡上时,“Walk 动画假设脚在平地上”就不对了——脚会浮空或陷入地面。
AnimGraph 最后一步: → Two Bone IK (脚部) → 目标位置 = 地面检测结果(从 IK Trace 来的) → 修正 Foot 骨骼的 Transform,让 IK 目标贴合地面 → 结果:楼梯上每一步脚都贴在台阶上Control Rig(UE5 新功能): 比 AnimBlueprint 里手搭 IK 节点更灵活——Control Rig 是一个独立的 Rig 资产,可以做更复杂的程序化动画(爬墙、牵手、武器 IK)。它在 AnimBlueprint 的 AnimGraph 最后一步执行。
七、C++ 与 AnimBlueprint 的数据流
// C++ 侧:每帧更新动画参数void UMyAnimInstance::NativeUpdateAnimation(float DeltaSeconds) { Super::NativeUpdateAnimation(DeltaSeconds);
AActor* Owner = GetOwningActor(); if (!Owner) return;
// 从 Character 拿数据 Speed = Owner->GetVelocity().Size2D(); Direction = CalculateDirection(Owner->GetVelocity(), Owner->GetActorRotation()); bIsInAir = Owner->GetCharacterMovement()->IsFalling();}AnimInstance 是 C++ class——不是纯蓝图对象。你可以在 C++ 里覆盖 NativeUpdateAnimation 做复杂的每帧数据准备。AnimBlueprint 只是 UI 展示层。
八、总结
| 概念 | 一句话 |
|---|---|
| EventGraph | 每帧更新动画参数(C++ 或 BP)——速度、方向、是否空中 |
| AnimGraph | 消费参数 → 状态机选动画 → BlendSpace 混合 → IK 修正 → Output Pose |
| Transition | 状态之间平滑过渡——Blend Duration 决定过渡时间 |
| AnimNotify | 动画特定帧触发事件——武器碰撞、音效、特效 |
| Root Motion | 动画里的位移驱动角色实际移动——脚不滑但不灵活 |
| Layered Blend Per Bone | 不同骨头不同动画——腰以上攻击、腰以下走路 |
| IK | 修正脚/手的位置贴合环境——Control Rig 是更强大的 IK 系统 |
动画系统是 UE 里唯一一个”一半在 C++、一半在蓝图、一半在工具里”的模块——搞清楚 EventGraph vs AnimGraph vs C++ NativeUpdateAnimation 的数据流向,就不会迷失在动画 bug 的排查里。