Lyra里跑了挺久,动画基本流程顺着官方文档摸了一遍,但一到实战场就会出现脚不贴地的尴尬情况。标准第三人称角色站在平地上没什么问题,一旦上了斜坡、台阶或者石头,脚要么踩进地面,要么悬在半空,跑起来还能看到脚在地面上滑动。这个问题最常用的解法是脚部IK(Foot IK),而UE5里最顺手的工具链就是ControlRig这一套。这篇我打算把Lyra动画模块中接入ControlRig脚部IK的完整思路和实操过程捋一遍,包括我在接入时踩到的几个坑,给同样在Lyra里做动画模块的同行省点时间。
先说结论:Lyra本身没有开箱即用的ControlRig脚部IK方案,需要自己在动画蓝图里接入,但接入过程比想象中简单,真正的难点在Goal坐标计算和动画层协调上。下面我会从方案选型、地面检测、IK资产搭建、AnimGraph接入、调优避坑这几个角度展开,最后附上我实测下来的参数和心得。
1. 为什么Lyra动画模块里做脚部IK,不能照搬UE4的老办法
1.1 Lyra的动画链路不欢迎散装节点
Lyra的动画模块和传统UE4模板有一个很大的区别:它把动画流程拆成了多个Animation Layer,Layer之间的切换依赖GameplayTag,而不是一堆StateMachine互相Notify。整个动画蓝图更像一个按标签组装出来的插槽系统,你想要在哪个阶段插入什么逻辑需要先看懂这套层叠关系。
在这个框架下,如果我像早期UE4教程那样,把射线追踪逻辑直接在动画蓝图的Event Graph里堆一堆,再把结果传给AnimGraph里的两个骨骼IK节点,虽然也能跑,但会有几个问题:
- 动画蓝图的模块化被破坏,后续想通过GameplayTag控制某个动画层就变得很别扭。
- Lyra经常用于多人联机测试,动画蓝图的事件图和动画线程分离之后,这种散装逻辑很容易出现两线程数据不同步的问题。
- Lyra的AnimGraph使用了一种类似“插槽”的方式层层混合,散装节点插入后不好判断它到底作用于哪个状态。
所以更好的方式是把脚部IK做成一个独立、可复用的模块,而不是塞进去一堆蓝图节点。IK Rig恰好就是这个思路:它把解算过程独立成资产,动画蓝图只负责提供一个入口,Goal数据通过组件在外部更新。
1.2 UE4两骨骼IK方案的三个局限性
老玩家应该都用过动画蓝图里的Two Bone IK节点,它确实能实现基本的脚部贴合,但它有三个明显短板:
第一个短板是它只能解算一条骨骼链。脚部IK需要同时处理大腿、小腿、脚踝三个关节,Two Bone IK一次只能控制一条链,对上半身几乎无感。这意味着如果要让骨盆随两脚高度差产生合理的下沉或倾斜,得再单独加逻辑,做出来效果很机械。
第二个短板是它不处理关节的旋转约束和膝盖朝向。Two Bone IK给你暴露了Effector和Joint Target两个输入,如果你只是把Effector放在脚踝位置,Joint Target给得不好,膝盖就会反折。这个在角色背对坡面或者侧身站的时候出现概率极高。
第三个短板是它完全忽视地面法线。游戏里的地面不全是平的,脚部IK如果只修正高度而不修正脚的Pitch和Roll,脚会像踩在隐形平地上一样,坡面上依然看起来是「悬空平脚」。
UE4老方案能解决“脚的高度对不对”这个最基础的问题,但解决不了“脚贴得自不自然”这个更关键的问题。而UE5的IK Rig在设计上就是冲着解决这些短板去的,所以在Lyra这种新工程里没有必要再走老路。
1.3 为什么这次选择IK Rig而不是Control Rig
这里要澄清一个容易混淆的概念:UE5里有两套ControlRig体系。一套是传统的Control Rig,资产以CR_开头,基于RigVM,更偏向Sequencer过场动画或者编辑器的手动绑定。另一套是IK Rig,资产以IK_开头,专门为运行时程序化动画设计,支持在动画蓝图中通过AnimNode_IKRig接入。
Lyra的动画模块是实时游戏系统,不是过场动画系统,所以首选应该是IK Rig。老Control Rig虽然在AnimGraph里也有AnimNode_ControlRig节点,但你需要额外处理Control Value的传递、控制值插值等问题,流程比IK Rig复杂,而且它没有一个清晰的Goal概念,维护起来很费劲。
IK Rig的另一个优势是可以在资产里直接定义多个Goal和解算器,运行时只需要更新Goal的Transform,解算器内部会自己处理骨骼链的优先级和循环迭代。加上UE5.4版本里IK Rig对FullBodyIK的支持已经很成熟,用在Foot IK这种场景完全够用。
2. 脚部IK的第一公里:地面检测和Goal坐标计算
2.1 用球体追踪代替射线追踪的细节
脚部IK的第一步是检测地面。很多初学者直接用LineTraceByChannel从角色脚踝垂直往下打一条线,这在平地上没问题,但到了稍微崎岖一点的地形就会出问题:地面上一个小石子或者一个尖锐的突起,会让检测点瞬间抬高好几厘米,脚踝立刻跟着跳,视觉上就是高频抖动。
所以我在Lyra里做这块时用的是SphereTraceByChannel,也就是球体追踪。球体追踪相当于一根有半径的短棍往下压,它会把地面上那些细小的凸起过滤掉,让检测结果更稳定。半径建议给2到4厘米,太小没效果,太大又会让脚“浮”在凹陷地形上方。
追踪参数我是这样设的:
| 参数 | 值 | 说明 |
|---|---|---|
| 追踪类型 | SphereTraceByChannel | 球体追踪,稳定过滤小凸起 |
| 球体半径 | 2.5 cm | 过滤细小地面起伏 |
| 起点 | 脚踝骨骼位置 + 向上0.5m | 确保追踪起点始终在地面上方 |
| 终点 | 脚踝骨骼位置 - 向下0.6m | 覆盖大部分斜坡和台阶 |
| 追踪通道 | Visibility | 能命中场景静态网格和可破坏物 |
起点为什么要从脚踝位置往上抬50厘米?因为角色下台阶或者下坡时,脚踝可能在短时间内低于地面高度,如果从脚踝位置垂直往下打,可能根本打不到地面。往上多给一段距离,能保证追踪射线始终覆盖住“从高处突然落到低处”的场景。
2.2 从命中点算脚踝Goal的正确姿势
球体追踪得到的Hit.Location是地面上一个点的世界坐标,这个点并不等于脚踝的Goal位置,因为脚踝骨骼本身在脚底上方还有一段距离。如果不做补偿,直接把IK Goal放在地面命中点上,脚会整个陷进地面里。
补偿方式很简单:在角色蒙皮网格上找到脚底到脚踝的相对偏移,然后在命中点上加上这个偏移。实际操作中,我是先在角色的动画蓝图里取一次foot_l的骨骼世界位置,同时用一个自定义的“脚底参考点”(比如在蓝图里加一个Socket)取它的世界位置,两者相减得到脚踝高度差,然后把这个高度差加到地面命中点上。
用公式表达就是:
FootGoalLocation = HitLocation + (FootAnkleLocation - FootBottomLocation)这里FootAnkleLocation - FootBottomLocation是一个常量,取决于骨骼绑定,通常Lyra默认角色的Mannequin骨骼大概在8到12厘米左右,不需要每帧重新算,初始化时取一次缓存就行。
再说旋转。Hit.Normal是地面法线,我们可以用MakeRotFromZ(Normal)得到一个朝向,这个朝向的Pitch和Roll能让脚掌贴合坡面。但直接全量应用会出问题:在稍微不平的地面上,脚会因为法线变化而疯狂旋转,看起来像在踩棉花。所以我的做法是给Pitch和Roll加一个权重,比如只应用法线旋转量的70%,剩下的保留动画本身的角度,视觉上更自然。
2.3 平滑与预测:消除高频抖动
地面检测做好之后,还有一个很影响体感的问题:目标值的抖动。即使用了球体追踪,地面的微小起伏还是会让Goal位置一两帧内跳几个厘米。如果直接把跳动的Goal传给IK解算器,脚就会抖。
这一步需要插值。我用的是FInterp,每帧把当前Goal向新计算出的Goal靠拢。速度参数我试下来12到15比较合适,太慢的话脚落地会拖沓,太快又没过滤效果。
除了插值,还有一个“预测”技巧:奔跑的时候,脚处于摆动阶段,如果只在脚当前位置做检测,等脚快落地时才突然修正Goal,观感上就会有一种“脚在空中突然被拉向地面”的感觉。解决办法是沿角色移动方向把追踪起点向前偏移一段距离,让IK提前“看到”地面。这个预测距离不能固定,最好和移动速度挂钩,我一般用速度 * 0.02,限制最大不超过20厘米。
预测偏移只适用于移动状态下,在角色静止、转身、切换方向的时候需要把预测距离归零,否则脚会往一个错误的方向探出去。具体做法是判断角色速度向量和脚踝移动方向向量的点积,如果是负数,说明脚在往回摆,预测距离直接清零。
3. 在Lyra角色骨骼上搭建IK Rig资产
3.1 创建IK Rig并绑定骨骼
IK Rig资产的创建路径很简单:Content Browser里右键 -> Animation -> IK Rig,然后选择Lyra角色使用的骨骼资产。Lyra默认的角色骨架是标准的Mannequin骨骼,里面包含了foot_l、foot_r、calf_l、calf_r、thigh_l、thigh_r这些标准骨骼,Foot IK需要的关键骨都在。
创建完之后会进入IK Rig编辑器,左侧是骨骼层级树,右侧是解算器设置面板。这里要注意一件事:创建IK Rig时选择的骨骼网格体最好和角色当前使用的Mesh保持一致,如果角色换过Mesh但骨骼资产还是同一个,问题不大,但如果骨骼层级不同,IK Rig里的Goal就会找不到对应的骨骼。
我见过有人直接拿系统默认的UE5 Mannequin骨骼建IK资产,结果角色用的是一套带Twist骨骼的第三方骨骼,最后IK解算时Twist骨骼被忽略,膝盖扭曲。所以创建之前一定确认Skeleton资产完全匹配。
3.2 定义脚部Goal与骨骼链
在IK Rig编辑器的骨骼树里找到foot_l,右键选择“从骨骼添加Goal”,命名为IK_Foot_L;foot_r同理,命名为IK_Foot_R。这两个Goal就是解算器的目标位置和旋转。
创建Goal之后要检查一件事:Goal的骨骼链必须包含从大腿到脚踝的完整路径。如果骨骼树上没有正确识别这条链路,FullBodyIK解算器就无法沿着骨骼层级逐级求解。通常从骨骼创建Goal时链会自动带上,但有些第三方骨骼因为存在多级Twist骨骼,链会断掉或包含多余骨骼,这时候需要在Rig Hierarchy里手动调整。
另外,如果角色需要做脚趾贴合,还可以从ball_l(脚掌前沿)创建第二个Goal。但Lyra默认的Mannequin骨骼如果没有ball骨骼,就不用纠结这一步,直接用foot骨骼就够了。UE5.4的FullBodyIK对末端的处理已经很细,单脚踝Goal也能基本模拟出脚掌踩实的效果。
3.3 选择FullBody IK解算器并调整关键参数
在IK Rig编辑器右侧点击添加解算器,选择FullBodyIK。这个解算器适合处理多Goal协调的全身IK问题,我们只用它来解算两条腿,就没必要再去单独加一个TwoBoneIK之类的解算器。
添加之后需要设置几个关键参数:
- Root Bone:选择
pelvis,骨盆作为IK链的根,这样当脚部Goal无法满足时,求解器会优先调整骨盆位置。 - End Bones:添加
foot_l和foot_r作为末端骨骼,这两个骨骼就是要对齐到Goal的位置。 - Iterations:迭代次数默认20,通常够用。如果角色在极端姿势下解算结果不理想,可以提高到40。注意迭代次数越高开销越大。
还有一个很关键的设置是Bone Settings。在左侧骨骼树里选中thigh_l和thigh_r,右侧会弹出骨骼设置,里面有旋转约束和权重相关选项。给大腿和小腿加一个合理的旋转约束,能大幅减少膝盖反折的概率。一般做法是限制大腿和小腿在X轴和Y轴上的旋转范围,Z轴通常不限制,保证角色可以正常朝向。
完成这些设置后,可以在IK Rig编辑器里直接拖动Goal验证效果。把IK_Foot_L的位置往下移一点,预览窗口里应该能看到左脚整条链跟着调整,膝盖自然弯曲,骨盆轻微下沉。如果验证时发现膝盖弯曲方向不对,优先检查大腿的旋转约束设置。
4. 把IK节点接进Lyra的AnimGraph
4.1 找到正确的插入点:状态机之后,输出之前
打开Lyra角色使用的动画蓝图,进入AnimGraph。Lyra的AnimGraph结构比普通模板复杂一些,但原理是一样的:最终Output Pose之前会有一个处理完整身体姿势的节点图,状态机输出的是FullBody姿势,经过各种Layer混合之后作为最终姿势输出。
IK节点的插入位置我建议放在状态机输出与最终输出节点之间。这样IK节点能拿到完整姿势,然后对腿部骨骼进行修正。如果AnimGraph里有AnimNode_Slot或者LayeredBoneBlend之类的混合节点,IK节点要放在这些节点的下游,确保作用于所有动画层叠加后的最终姿势。
这里有个反例:有人把IK节点放在状态机内部某个状态下,结果只有在那个状态下脚部IK才生效,角色一切换到其他状态脚就立刻穿地。放在Output之前再配合Alpha控制,才能保证所有状态都能使用IK,又可以在不需要时通过Alpha关掉。
4.2 通过AnimNode_IKRig连接IK绑定资产
在AnimGraph里搜索“IK Rig”节点,添加AnimNode_IKRig,然后把它的Input Pose连接到状态机输出,Output Pose连接到最终输出节点。
在节点详情面板里指定之前创建的IK Rig资产。指定之后节点会自动读取IK Rig资产里的Goal定义,但如果IK资产里的Goal还没绑定具体的输入源,运行时Goal会一直是默认值。
IK Rig节点的Goal数据输入有两种方式。一种是直接在节点上暴露Goal的Transform引脚,在动画蓝图里通过变量或计算节点连接;另一种是通过IKRigComponent组件,在AnimInstance外部更新Goal。我更推荐第二种,因为它天然解决了动画线程和游戏线程的时序问题。
4.3 通过IKRigComponent更新Foot Goal
IKRigComponent是一个可以在运行时更新IK Goal的组件。给角色附加一个IKRigComponent后,在IK Rig资产里每个Goal会有一个“Goal Source”选项,选择IKRigComponent之后,运行时组件会替动画线程维护每个Goal的Transform。
具体操作:在Lyra的角色蓝图里添加一个IKRigComponent组件,然后在角色蓝图的Event Tick里执行以下逻辑:
- 获取左、右脚的
foot_l和foot_r骨骼世界位置。 - 对两个位置做球体追踪,得到左脚地面点
Hit_L和右脚地面点Hit_R。 - 根据
2.2里的公式计算左脚和右脚的Goal位置和旋转。 - 调用
IKRigComponent的SetIKRigGoalTransform,指定Goal名称为IK_Foot_L和IK_Foot_R,传入对应的Transform。
时序上不用担心:IKRigComponent内部会缓存目标变换,动画线程在解算时读取的是最新值,不存在跨线程写竞争导致崩溃的问题。
如果你不想改角色蓝图,也可以在动画蓝图初始化时获取Mesh的Owner并动态添加组件,但我实践下来不太建议这么做。动态添加组件在运行时会让组件注册顺序不稳定,而且在多人游戏里每个客户端和服务器都会走一遍逻辑,容易出不可预期的问题。直接在角色蓝图里加组件是最稳妥的。
4.4 用GameplayTag控制IK的开关与强度
Lyra非常喜欢用GameplayTag管理状态,脚部IK也应该接入这套体系,而不是写死在动画蓝图里。
我定义了这样几个Tag:
State.IK.Enabled:表示当前脚部IK应该生效。State.IK.Disabled:表示当前脚部IK应该关闭。State.IK.Clamp:表示IK强度受限(比如在下蹲、攀爬、受伤状态时)。
在动画蓝图的AnimGraph里,IK Rig节点通常暴露一个Alpha输入,用来控制IK混合强度。我把这个Alpha连到一个自定义计算节点,根据GameplayTag做一个平滑插值:
Alpha = FInterp(当前Alpha, 目标Alpha, DeltaTime * 10) 目标Alpha = Tag_Enabled ? (Tag_Clamp ? 0.3 : 1.0) : 0.0这个设计的好处是,Jump、Roll、死亡等状态下,只要状态机在事件里设置一下Tag,IK就会自动平滑关闭,不需要在动画蓝图里写一大堆状态判断。跳跃时脚部IK快速归零,落地瞬间再快速恢复,视觉效果比一个固定Alpha自然得多。
5. 实测中遇到的那些坑和对应调优
5.1 脚部抖动与平滑参数
第一次把所有逻辑串起来之后,我遇到的第一问题就是脚抖。角色在稍微不平整的地面上走路时,脚踝一直在高频小幅度跳动,整个下半身看着像筛糠。
排查下来原因有两个。一是球体追踪半径太小,我一开始给了1.5厘米,地面稍微有个石子尖角,检测结果就跳。把半径加到2.5厘米后有明显改善,但还不够干净。二是Goal的插值速度太快,我一开始用FInterp速度给了30,结果插值形同虚设。降到12之后,抖动肉眼可见地被过滤掉了。
另外还有一个不起眼但影响很大的点:起点偏移量。如果起点在脚踝上方只偏移了20厘米,角色下台阶时会在一瞬间出现“追踪不到地面”的情况,然后IK失去目标,脚突然弹回原始位置,看起来像抽搐。把起点偏移加到50厘米之后,这类问题基本消失。
5.2 膝盖翻转问题与Joint Target取点
第二个坑是膝盖反折。现象是角色在斜坡上侧身站立时,某条腿的小腿突然向内或向外翻,膝盖形状直接扭曲。
IK Rig的FullBodyIK虽然比TwoBoneIK智能,但在多解情况下同样需要约束信息才能选出正确解。我通过给大腿和小腿的Bone Settings加旋转约束解决了大部分问题。具体来说,大腿骨骼的X轴旋转范围限制在-80到80度,Y轴旋转限制在-30到30度,Z轴不限制;小腿骨骼的X轴限制在-110到10度,Y轴限制在-20到20度,Z轴不限制。
如果你的角色骨骼里包含Twist骨骼,还要把这些Twist骨骼在Excluded Bones里排除掉,否则FullBodyIK会把一部分旋转分配到Twist骨骼上,导致小腿主骨骼的旋转看起来很奇怪。这个坑在第三方骨骼里特别常见,Lyra默认Mannequin骨骼没有Twist,但如果你的项目换了骨骼,就一定会遇到。
5.3 骨盆下沉与斜坡坡度限制
第三个坑出现在陡坡上。下坡时,两只脚为了贴合坡面,一个往上一个往下,FullBodyIK为了同时满足两个Goal,会把骨盆拉得非常低,上半身被“拽”得往下弯,姿态很难看。
解决方法是限制骨盆位移。在FullBodyIK解算器里找到Pelvis相关配置,给最大下沉量设了一个上限,我设的是2厘米。如果两个脚的高度差实在太大,宁可让一只脚稍微悬空,也不能让骨盆过度下沉。
同时加了一个斜坡坡度限制:当地面法线和世界Up方向的夹角大于35度时,FootIK的Alpha强制归零。超过35度基本就是悬崖或者陡坡,脚部IK强行贴合上来反而不自然,关闭IK让原动画动画自己处理视觉反而更正常。
这两个参数建议做成曲线配置,而不是写死。不同地形需求差异很大,Lyra里的平地竞技场和野外山区地形对坡度限制的要求完全不同。
5.4 性能开销与异步执行
最后说说开销。IK Rig里的FullBodyIK迭代20次,加上两只脚的球体追踪,在单角色上跑几乎没有压力。但Lyra是多人射击示范项目,一个场景可能同时出现三四十个角色,如果每个人都每帧做两到四次球体追踪加FullBodyIK,开销会明显上升。
我测下来一个角色全帧完整跑一次FootIK(包括追踪和解算)大概在0.05到0.1毫秒左右。但如果用的是同步追踪,而且每帧都强制等待追踪结果,这个时间可能飙到2毫秒以上,完全不可接受。
优化思路有三个方向:
- 降低Goal更新频率。IK Rig的Goal不需要每帧更新,我把它改成15到30Hz更新,动画线程在两次更新之间用插值保持平滑。视觉上几乎看不出差异。
- 异步追踪。用
SphereTraceByChannelAsync,本帧发起追踪,下一帧拿结果。这样追踪过程不阻塞游戏线程。 - LOD关IK。低LOD角色直接不启用IK Rig,只用原始动画。Lyra本身有动画共享和LOD系统,脚部IK这种细节在远处根本看不清,没必要为低成本角色掏这份性能。
实测在30个角色同时活跃的场景里,做这三项优化后,FootIK的总体开销控制在0.3毫秒以内,属于可以接受的量级。
写在最后的实测体会
把整套东西接到Lyra里前后折腾了大概一个多星期,最后复盘发现大部分问题都不是IK解算器本身的缺陷,而是Goal输入不稳定和角色状态没有接入好。先把射线追踪、Goal计算、IK Rig资产、GameplayTag控制这条链路理顺,踩坑率会大幅下降。
现在Lyra已经更新到UE5.4,IK Rig这套体系已经很成熟了,FootIK这种级别的功能不需要自己写太多底层逻辑。下一步我计划在Lyra里把Pose Warping和脚跟落地检测也加进来,和这套脚部IK配合,解决跑步时脚在地面滑动的问题。这里先分享到这篇,希望对你接入ControlRig脚部IK有帮助。