UE4 简单脚部IK C++实现
如题,这只是很简单的脚步IK实现,动画部分使用的是蓝图。
实际项目可以使用 Power IK 或UE5 基于全身位置的IK。
项目地址
思路
就是让离地远的脚着地,另一只脚抬起相应的距离。
如下图所示,1是IK前的左右脚状态,2是把骨骼网格体整体往下移动至离地远的脚着地时左右脚的状态,3是离地近的脚抬起后的状态,4是将前3个过程结合在一起。
所以最主要的就是求骨骼网格体下移的距离和离地近的脚需要抬起的距离,即图所示的绿线和红线。
我们可以如下图所示从双脚脚底往下发出长度为 IKTraceDistance
的射线,通过 HitDistance
可以得到哪一只脚是离地远的脚。因为离地远的脚不做IK处理,姿势不变,所以所得到的 HitDistance
与骨骼网格体下移的距离(绿线)相等。离地近的脚则需要抬起双脚 HitDistance
差值的绝对值,而不是离地远的脚的 HitDistance
。这点可以从图上看出,双脚 HitDistance
差值的绝对值就是台阶的高度。只是在一脚着地的情况下,也就是该脚 HitDistance
为0,离地远的脚的 HitDistance
刚好等于双脚 HitDistance
差值的绝对值,这点要注意。
实现
射线长度
射线长度为半腿高,也就是胶囊体半高的一半,射线终点就是离地远的脚最多能下到的位置。
1 | //in constructor |
我的想法是让脚最多就只能抬半条腿的高度,高于这个程度的话,看起来就有点不合理了。比如下图:
射线检测
主要是调用UKismetSystemLibrary::LineTraceSingleForObjects
。
准备工作
在骨骼的左右脚部分增加插槽。
也可以,使用现有的foot骨骼的话,到时候设置射线起点时记得加上偏移,或者使用现有的ball骨骼。使用插槽的话,比较直观,方便调整。
检测代码
1 | FVector Start; |
1️⃣ float FootZ = GetActorLocation().Z - CapsuleHalfHeight;
SocketLocation.Z
的值会随着脚的移动发生改变,不单单IK时会被影响,人物网格体下移时也会被影响,甚至人物移动时都会影响到。所以最好找个相对于整个过程中位置不变的脚的位置,也可以理解为IK前的初始位置。
求得IKOffset
1 | if( HitDistanceL < 0 || HitDistanceR < 0 ) |
应用IKOffset
虚幻引擎在动画蓝图中提供了Two Bone IK
节点,具体讲解可以参考:
Two Bone IK
节点需要 Effector Location
,当执行器位置空间选择骨骼空间时,执行器目标本地坐标系朝上方向的大小就是对应先前求得的 IKOffset
。
因为我们求出的 IKOffset
是标量,不带方向,执行器位置空间又选择的是骨骼空间,所以得注意执行器目标的本地坐标系朝向。从下图可以看出右脚对应的 Two Bone IK
的执行器目标 foot_r
本地坐标系 X
的反方向对应世界坐标系的 Z
的正方向,所以处理时需要乘以-1,转换成我们需要的上方向。
而 Joint Target Location
是为了确定3个骨骼所处在的平面,这个自己调的就行了。需要注意所填的值必须得是对应所选的关节目标位置空间以及关节目标。
到这一步,可以运行下看看。
求得MeshOffset
脚部已经有了IK效果,接下来处理骨骼网格体下移的部分。之前分析过,离地远的脚的 HitDistance
与骨骼网格体下移的距离相等,所以只要加上这么一行代码就可以了。
1 | MeshOffsetZ = FMath::Max(HitDistanceL, HitDistanceR); |
应用MeshOffset
虚幻引擎在动画蓝图中提供了 Transform Bone
节点,Transform Bone
节点将处理骨骼的变换——即为平移、旋转或缩放。
一样要注意方向的问题。
阶段效果
效果基本已经实现,但变化过程是瞬间的,上下楼梯很鬼畜,可以使用插值让变化过渡得更自然。
1 | float AIK_PracticeCharacter::FInterp(float CurrentValue, float TargetValue) |
1 | if( HitDistanceL < 0 || HitDistanceR < 0 ) |
调整胶囊体
骨骼网格体下移后,胶囊体就不再贴合了,如下图所示。
我们需要对应调整胶囊体的半高。如下图所示,绿线就是胶囊体要调整的幅度,等于 MeshOffset
。
因为我们只能设置胶囊体的半高,所以调整的幅度得除以2。
1 | float _CapsuleHalfHeight = FInterp(CapsuleHalfHeight, CapsuleHalfHeight - MeshOffsetZMax / 2); |
其实效果并不是很好,因为胶囊体调整高度时是上下两头同时进行,而不是单单上头。目前没找到什么好的解决方案。
脚部贴合地面
TODO
参考
相关
UE4 简单脚部IK C++实现