逆向运动学(Inverse Kinematics)简称IK是相对于 “正向运动学” 的。 “正向运动学” 即从骨骼的上级到下级进行旋转来达到自己想要的姿势,这是一个正向的思维。与此相对的,“逆向运动学” 是已知最后想要达成的姿势,然后反求出骨骼们的旋转。
之前的博客《使用UE4动画蒙太奇实现分层动画》里,我的工程中的人物已经能够挥拳,然而它的拳头是“穿墙”的: 为此,需要使用IK功能,思路是使用射线来判断和碰撞物的交点,以此点作为拳头的目标,使用IK计算骨骼期望的旋转量。
在学习过程中,我主要参考了官方内容示例 中的: 除此之外,也参考了《IK设置 | Unreal Engine Documentation》和《[UE4蓝图]虚幻4中完整实现脚部IK(一) - 知乎》
首先,打开骨架资源 upperarm_r-lowerarm_r-hand_r是要启用IK的骨骼链。 在lowerarm_r中添加插槽RightLowerArmSocket,在hand_r中添加插槽RightHandSocket 需要注意的是::
RightLowerArmSocket的相对坐标是0,0,0: 而RightHandSocket的相对坐标则要向前一些 其原因和没有这么做的后果可见附录。在这里,IK的主要是由双骨骼IK节点实现的。先创建一个: 其中IKBone是骨骼链末端的骨骼,是hand_r。 而Effector Location选择世界空间,因为一会儿射线操作将在世界空间里做:
双骨骼IK节点有一个参数是需要事先测试得出的,即JointTargetLocation。此值是必要的,因为三维空间中使双骨骼的末端到达一点的旋转值不是唯一的,为此需要一个三维点来确定空间中双骨骼的平面。
想要确定此处什么值是合适的,需要先构造一个环境来测试: 将挥拳的动作拖进来,直接输出: 编译之后,应该可以在预览窗口中看到挥拳的动作了。为了方便调试,需要将动作暂停 拖动播放进度到一个挥拳最远的帧,对于19_Frank_Punch_Right_Straight_Inplace这个挥拳动画是第7帧: 随后,将刚才的双骨骼IK节点连入: 编译后,应该会在预览窗口看到一个错误的动作,这是因为Effector Location和JointTargetLocation都是默认值0,0,0。
下面,选择双骨骼IK节点,这两个坐标就都可以在预览窗口中移动预览了: 最后调到一个满意的值:
将双骨骼IK节点的Effector Location提升为变量并命名为RightHandEffectorLocation,将Alpha提升为变量,并命名为IKRightHandAlpha。这两个值将由事件图表来赋值。
概括讲:
它获得RightLowerArmSocket和RightHandSocket的位置然后做射线,如果成功,就将结果设置为RightHandEffectorLocation,并将IKRightHandAlpha设置为1.0,否则IKRightHandAlpha就是0.0。另外要注意的是,射线检测用的是SphereTraceByChannel,即“球”形状,整体看来是个胶囊体。因为“拳头”被看做是一个“球”更真实。先看后果,如果RightHandSocket的相对位置是0,0,0,会出现抖动情况: 直接原因是IKRightHandAlpha被频繁在1和0之间跳变。 为了证明,我将动作设置为一个固定的姿势,并在IKRightHandAlpha被设置成1和0时输出调试信息,随后可以看到: Yes和No调试信息交替出现,由于IKRightHandAlpha一直突变,因此动作也在突变。 我推测的情况是这样:
首先,射线检测到了碰撞点,因此IKRightHandAlpha变为1,然后手部被放到了碰撞点。此时手部正好在碰撞点,所以下一次碰撞检测变得不稳定,可能是0或者1。此时的位置就是0,因为IK不再启用,手部回归正常动画位置,也就是第1步的情况。如此,进入循环。而将插槽设置在了靠前的位置,则会有效避免此情况:
首先,射线检测到了碰撞点,因此IKRightHandAlpha变为1,然后手部被放到了碰撞点。此时手部正好在碰撞点,但是插槽位置更靠前,因此射线检测一定会检测到碰撞,而且碰撞点很可能不变。如此便固定下来。