在3D逆向运动学: 三角函数求解与Fabrik中已经讲解了如何实现双关节与多关节的IK功能。
本篇讲解下如何实现动物的腿部IK。效果如下:
我们在身体节点下挂载了4条关节链,每条关节链都附带了IKSystem脚本,用于各个关节链的逻辑计算。关于IKSystem可查看源码或3D逆向运动学: 工程源码MultiLimbIK插件使用。
在Body节点下放置了4个脚部节点,主要用途在于身体移动时,当目标位置与脚部节点位置产生一定距离时,目标位置就做位置插值运算。这样产生的结果就是目标位置移动带动了相应关节链的整体联动。使得动物的4条腿部IK产生走动的效果。
(4个脚部在身体(Body)节点内部)
同时4条关节链对应的4个目标节点,在最开始时与4个脚部节点重合。
运行后可以看到4条蜘蛛腿的末端效应器处在了预先设置的脚部位置。
当身体 Body
节点移动时,可看到如下的过程,其中红框内的节点就是脚部的节点,身体节点移动后,由于脚部节点在身体节点内部,导致了脚部节点也跟着移动。因为脚部节点移动了,那么目标节点 黄色部分
如果跟脚部节点的距离大于阈值,就会触发目标节点的插值运动,目标节点运动就会促使对应关节链的联动。这就是腿部IK的逻辑原理。
以下是LegIK的关键代码:
updateLegs(deltaTime: number) {
if(!this.legState) {
this.legState = { isMoving: false, targetPos: this.node.worldPosition };
}
// 如果已经在移动就不运算
if(!this.legState.isMoving) {
// 计算身体的移动偏移,也就是IK的目标节点与脚部节点的距离
const movementLength = Vec3.distance(this.node.worldPosition, this.footTarget.worldPosition);
// 如果腿需要移动(当距离大于阈值了,腿部需要跟着联动)
if (movementLength > this.legMaxStep) {
// 标记IK的目标节点可以移动了
this.legState.isMoving = true;
// 设置目标位置为对应脚部的位置
this.legState.targetPos = this.footTarget.worldPosition;
}
}
// 更新腿的位置
if (this.legState.isMoving) {
// 移动速度,用于插值运算
const moveSpeed = this.legMoveSpeed * deltaTime;
// IK目标节点到脚部节点位置进行插值
this.node.worldPosition = Vec3.lerp(new Vec3(), this.node.worldPosition, this.legState.targetPos, moveSpeed);
// 当目标节点与脚部节点位置的距离在误差范围内,则视为已到达对应位置,此时moving状态结束
if (Vec3.distance(this.node.worldPosition, this.legState.targetPos) < this.legMoveThreshold) {
this.node.worldPosition = this.legState.targetPos;
this.legState.isMoving = false;
}
}
}
update(deltaTime: number) {
this.updateLegs(deltaTime);
}
除了上述代码外,还需要注意的一点是,关节链在不干预的情况下,关节链的第一个关节 基部关节
是固定不动的。但是因为腿部IK是跟随动物身体移动的,所以基部关节就不能固定不动,基于此需要修改下上篇中IKSystem脚本的初始化部分的代码,放到update中。
update(deltaTime: number) {
// 需要强制更新整条关节链,包括首个关节
if(this.forceUpdateChain) {
// 重新计算所有相关初始值
this.computeStartPos();
}
this.updateJoints();
}
相关原理或代码如下: