1
\$\begingroup\$

I have a FPS character game object, which consists of just the arms. I made this IK script off of a tutorial.

{
public int chainLength = 2;

public Transform target;
public Transform pole;

public int iterations = 10;

public float marginOfError = 0.001f;

[Range(0, 1)]
public float snapBackStrength = 1f;

protected float[] bonesLength;
protected float completeLength;
protected Transform[] bones;
protected Vector3[] positions;
protected Vector3[] startDirection;
protected Quaternion[] startRotationBone;
protected Quaternion startRotationTarget;
protected Quaternion startRotationRoot;

public bool ikActive = false;

// Start is called before the first frame update
void Start()
{
    if (ikActive)
    {
        Init();
    }
}

// Update is called once per frame
void Update()
{
    
}

void LateUpdate()
{
    if (ikActive)
    {
        ResolveIK();
    }
}

void Init()
{
    bones = new Transform[chainLength + 1];
    positions = new Vector3[chainLength + 1];
    bonesLength = new float[chainLength];
    startDirection = new Vector3[chainLength + 1];
    startRotationBone = new Quaternion[chainLength + 1];

    startRotationTarget = target.rotation;
    completeLength = 0;

    var current = transform;
    for(var i = bones.Length - 1; i>= 0; i--)
    {
        bones[i] = current;
        startRotationBone[i] = current.rotation;

        if (i == bones.Length - 1)
        {
            startDirection[i] = target.position - current.position;
        }

        else
        {
            startDirection[i] = bones[i + 1].position - current.position;
            bonesLength[i] = (bones[i + 1].position - current.position).magnitude;
            completeLength += bonesLength[1];
        }

        current = current.parent;
    }
}

void ResolveIK()
{
    if (target == null)
    {
        return;
    }

    if (bonesLength.Length != chainLength)
    {
        Init();
    }

    for (int i = 0; i < bones.Length; i++)
    {
        positions[i] = bones[i].position;
    }

    var rootRotation = (bones[0].parent != null) ? bones[0].parent.rotation : Quaternion.identity;
    var rootRotationDifference = rootRotation * Quaternion.Inverse(startRotationRoot);
   
    if ((target.position - bones[0].position).magnitude >= completeLength)
    {
        var direction = (target.position - bones[0].position).normalized;

        for (int i = 1; i < positions.Length; i++)
        {
            positions[i] = positions[i - 1] + direction * bonesLength[i - 1];
        }
    }
      
    else
    {
        for (int i = 0; i < positions.Length - 1; i++)
        {
            positions[i + 1] = Vector3.Lerp(positions[i + 1], positions[i] + rootRotationDifference * startDirection[i], snapBackStrength);
        }

        for (int iteration = 0; iteration < iterations; iteration++)
        {
            for (int i = positions.Length - 1; i > 0; i--)
            {
                if (i == positions.Length - 1)
                {
                    positions[i] = target.position;
                }

                else
                {
                    positions[i] = positions[i + 1] + (positions[i] - positions[i + 1]).normalized * bonesLength[i];
                }
            }

            for (int i = 1; i < positions.Length; i++)
            {
                positions[i] = positions[i - 1] + (positions[i] - positions[i - 1]).normalized * bonesLength[i - 1];
            }

            if ((positions[positions.Length - 1]).magnitude < marginOfError)
            {
                break;
            }
        }
    }

    if (pole != null)
    {
        for(int i = 1; i < positions.Length - 1; i++)
        {
            var plane = new Plane(positions[i + 1] - positions[i - 1], positions[i - 1]);
            var projectedPole = plane.ClosestPointOnPlane(pole.position);
            var projectedBone = plane.ClosestPointOnPlane(positions[i]);
            var angle = Vector3.SignedAngle(projectedBone - positions[i - 1], projectedPole - positions[i -1], plane.normal);
            positions[i] = Quaternion.AngleAxis(angle, plane.normal) * (positions[i] - positions[i - 1] + positions[i - 1]);
        }
    }

    for (int i = 0; i < positions.Length; i++)
    {
        if (i == positions.Length - 1)
        {
            bones[i].rotation = target.rotation * Quaternion.Inverse(startRotationTarget) * startRotationBone[i];
        }

        else
        {
            bones[i].rotation = Quaternion.FromToRotation(startDirection[i], positions[i + 1] - positions[i]) * startRotationBone[i];
        }

        bones[i].position = positions[i];
    }
}

}

The hierarchy looks like this:

FPS Person

(the arm mesh)

FPS armature

   camera bone


      shoulder bone.L


         upper arm bone.L


            lower arm bone.L


               hand.L


      shoulder bone.R


         upper arm bone.R


            lower arm bone.R


               hand.R
    

                  weapon holder (empty object)


                     assault rifle


                        (assault rifle mesh)


                        assault rifle armature (gun bone and magazine bone)


                        left hand position    (empty game object)




                        right hand position (empty game object)

I have the IK script attached to each of the hand bones with a chain length of 2, with no pole set. I have the target for the left hand as the left hand position, and I have the target for the right hand as the right hand position. The right hand position is in the exact same location as the hand bone.

The left hand IK works fine. For some reason, when I rotate the right hand position, the hand moves, but it moves really fast and in a jagged way, so that it looks glitchy. It also moves up and down even if I'm not rotating the gun.

Why is this happening, and how can I fix this?

I'd preferably no rotate the weapon holder itself since I plan to have multiple weapons as its child objects for weapon switching and I don't want to mess the rotation up.

\$\endgroup\$

0

You must log in to answer this question.