CCDIK(循环坐标下降逆动态学)
策略思路 1.骨骼间的结构为子父层次链接(父节点的变换会影响子节点的) 2.每个骨骼都以【自身轴点到尾叶子节点的方向】旋转到【自身轴点到目标点方向】,开始趋近 3.最终效果与迭代次数成正比 更多细节查看 IK
Arrow类
[Serializable]
public class Arrow
{
public float len;
public float angle;
public Vector2 angleLimt = new Vector2(-180f,180f);
public Color color;
public Vector2 a { get; private set; }
public Vector2 b { get; private set; }
public Vector2 forward { get { return (b - a).normalized; } }
public void Follow(Vector2 target, Vector2 end, bool limt)
{
angle = (angle + Vector2.SignedAngle(end - a, target - a)) % 360f;
if (limt && (angleLimt.x != -180 || angleLimt.y != 180))
{
angle = Mathf.Clamp(angle, angleLimt.x, angleLimt.y);
}
}
public void Calculate_AB(Vector2 origin, Vector2 right)
{
a = origin;
b = a + Rotate(right, angle) * len;
}
private Vector2 Rotate(Vector2 v, float a)
{
a = a * Mathf.Deg2Rad + Mathf.Atan2(v.y, v.x);
return new Vector2(Mathf.Cos(a), Mathf.Sin(a));
}
}
IKSolverCCD 类
[Serializable]
public class IKSolverCCD {
public Arrow[] arrows = new Arrow[] { };
public Vector2 target;
public bool useLimt = false;
public int iterations = 4;
public void FollowTarget()
{
for (int n = 0; n < iterations; n++)
{
for (int i = arrows.Length - 1; i >= 0; i--)
{
arrows[i].Follow(target, arrows[arrows.Length - 1].b, useLimt);
UpdatePosition(i == 0 ? Vector2.right : arrows[i - 1].forward, i);
}
}
}
public void UpdatePosition(Vector2 right, int n = 0)
{
Vector2 origin = arrows[n].a;
for (; n < arrows.Length; n++)
{
arrows[n].Calculate_AB(origin, right);
origin = arrows[n].b;
right = arrows[n].forward;
}
}
}
CCDTest类
public class CCDTest : MonoBehaviour
{
public IKSolverCCD iKSolverCCD;
public Transform targetP;
public Transform anchorP;
public bool useLimt = true;
public bool update = false;
[Range(1,10)]
public int iterations = 1;
[Button("UpdateAnchor")]
void UpdateAnchor()
{
iKSolverCCD.useLimt = useLimt;
iKSolverCCD.iterations = iterations;
iKSolverCCD.arrows[0].Calculate_AB(anchorP.position, Vector2.right);
iKSolverCCD.UpdatePosition(Vector2.right);
iKSolverCCD.FollowTarget();
}
[Button("Calculate")]
void Calculate()
{
iKSolverCCD.useLimt = useLimt;
iKSolverCCD.iterations = iterations;
iKSolverCCD.target = targetP.position;
iKSolverCCD.FollowTarget();
}
private void OnDrawGizmos()
{
if (update) {
Calculate();
}
Arrow last = null;
foreach (var item in iKSolverCCD.arrows)
{
Gizmos.color = item.color;
Mov.GizmeDrawArrow(item.a, item.b);
if (useLimt)
{
Mov.GizmeDrawCircleLimt(item.a, item.forward, item.angleLimt, 0, item.len / 4f, last == null ? (item.a-Vector2.right): last.a);
}
last = item;
}
}
}
|