3D逆向运动学: 动物腿部IK实现

Number of views 17

3D逆向运动学: 三角函数求解与Fabrik中已经讲解了如何实现双关节与多关节的IK功能。

本篇讲解下如何实现动物的腿部IK。效果如下:

ezgif2df04ce38d27d1.gif

我们在身体节点下挂载了4条关节链,每条关节链都附带了IKSystem脚本,用于各个关节链的逻辑计算。关于IKSystem可查看源码或3D逆向运动学: 工程源码MultiLimbIK插件使用

image1745914868119.png在Body节点下放置了4个脚部节点,主要用途在于身体移动时,当目标位置与脚部节点位置产生一定距离时,目标位置就做位置插值运算。这样产生的结果就是目标位置移动带动了相应关节链的整体联动。使得动物的4条腿部IK产生走动的效果。

(4个脚部在身体(Body)节点内部)

image1745915407400.png

同时4条关节链对应的4个目标节点,在最开始时与4个脚部节点重合。

image1745915665159.png

运行后可以看到4条蜘蛛腿的末端效应器处在了预先设置的脚部位置。

image1745916374826.png

当身体 Body节点移动时,可看到如下的过程,其中红框内的节点就是脚部的节点,身体节点移动后,由于脚部节点在身体节点内部,导致了脚部节点也跟着移动。因为脚部节点移动了,那么目标节点 黄色部分如果跟脚部节点的距离大于阈值,就会触发目标节点的插值运动,目标节点运动就会促使对应关节链的联动。这就是腿部IK的逻辑原理。

image1745916692403.png

以下是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();
}

相关原理或代码如下:

2D逆向运动学:三角函数求解

3D逆向运动学: 三角函数求解与Fabrik

3D逆向运动学: 工程源码MultiLimbIK插件使用

CocosCreator3.x多关节铰链与动物关节IK源码

0 Answers