Animating Inverse Kinematics

I am trying to write an animation application. I know, it’s a tall order. What I am testing now is how to do Inverse Kinematics on the legs of a character by dragging a goal object around. I have downloaded and explored the Locomotion assets, but I can’t see how to integrate that into what I am doing. Locomotion is designed to modify animation that is already going on.

I found a script on this forum, but it doesn’t work correctly as the author admits.

Does anyone know how to accomplish this? I am not a code master so, please, go easy on me!

I also tried a “pseudo IK” by using LookAt to point the joints at target objects, but the joints rotate not from the tip but the center. Is there a way to change the pivot point of the objects in the scene editor? I’ve looked but couldn’t find anything.

Also, is there a better way to get something to track the position of another object than LookAt?

Anyone have any thoughts on this? I’m not a math wiz so I’m having a hard time creating my own IK script.

If anyone knows how to integrate the Locomotion framework into a program, please let me know.

Just keeping this thread alive.

Anyone have ANY ideas?

One possibility might be to use the physics system. If you set up joints corresponding to the character’s joints and have a rigidbody on each bone then you could drag a bone and have the others follow along with it. Alternatively, you might want to check out the CCD algorithm for IK (see here for example).

to fix the bones rotating weird, just make a empty gameobject at it’s position and then make the empty gameobject the parent of the bone you want to move, then rotate the gameobject and it should work fine.

Here’s a 2D version of the above CCD algo

/*
File: IKCCD2D.cs
  Code adapted from: www.gdmag.com nov 1998 article by Jeff Lander
*/
using UnityEngine;
using System.Collections;

public class IKCCD2D : IKSolver {

	public bool Damping = false; //Allow some delay in the motion?
	public float Damping_Max = 0.5f; //Maximum radians per iteration if damping is on
	public Vector3 RotationAxis = new Vector3(0,0,-1); //the axis on the bones that we'll be using to rotate around
	
	private float IK_POS_THRESH = 0.125f; //How close we need to be to skip the calculations.
	private float ANGLE_THRESH = 0.999f; //a little trick to avoid the NaN return from the Dot(180)
	private int MAXLOOPSPERFRAME = 10; //max loops to try before we let it slip to the next frame
	private float STEPSIZE = 3.0f; //multiplier to use to set the max movement per frame (Speed of following)
		
	public override void Solve(Transform[] bones, Vector3 target) {

		Transform endEffector = bones[bones.Length-1];
		Vector3 curEnd = Vector3.zero;	
		Vector3 rootPos = Vector3.zero;
		Vector3 crossResult = Vector3.zero;	
		Vector3 targetDirection = Vector3.zero;	
		Vector3 currentDirection = Vector3.zero;	

		float theDot=0;
		float turnRadians=0;
		
		int link=0; //which bone are we moving right now.
		int tries=1; //number of tries to match it up this frame.
		
		//start from the end effector.
		curEnd = endEffector.position;
		
		//we only want the Y rotations for now
		curEnd.y = 0; //0 out the end_bodypart height
		target.y = 0; //0 out the target height
		
		while (tries < MAXLOOPSPERFRAME  (curEnd-target).sqrMagnitude > IK_POS_THRESH)
		{
			//If we're at the top of the array, start over from the bottom
			if (link < 0) 
				{ link = bones.Length-1; }
			
			//Get the current link to rotate from the array, and its position
			//This is the main changing value in the set
			rootPos=bones[link].position;
			
			//Get the current positions of the target and end effector every loop
			curEnd=endEffector.position;
			
			//Only dealing with Y rotations for now.. more to come!		
			curEnd.y = 0; //0 out the end_bodypart height
			//target.y = 0; //0 out the target height
			rootPos.y = 0; //0 out the current links height Because we're doing 2D
			
			//The direction we are pointing, is the direction FROM the current links loop
			//TO the End_Bodypart.. 
			//the Direction we WANT to point, is 
			currentDirection =  curEnd - rootPos;
			targetDirection =  target - rootPos;
			
			
			//Dot product needs normalized vectors! (Slowest part of the process)
			currentDirection.Normalize();
			targetDirection.Normalize();
			theDot = Vector3.Dot(currentDirection,targetDirection);
		
			//If the DotProduct is below our angle threshold, we're already very nearly pointing the
			//linkage straight at the target.. so skip the rotations and send us out for a new frame
			//It also gives us the distance we need to turn to match the currentDirection TO the
			//targetDirection..
			
			//If the angle is exactly 180 degrees, skip this iteration..
			if (theDot > -ANGLE_THRESH  theDot < ANGLE_THRESH)
			{
				turnRadians = Mathf.Acos(theDot);
				
				//The sign of the y value of the CrossProduct tells us which 
				//direction we need to turn the current linkage!
				crossResult = Vector3.Cross(currentDirection,targetDirection);

				if (crossResult.y > 0.0f)
				{	
					if (Damping)
					{
							if (turnRadians > Damping_Max)
								turnRadians = Damping_Max;
					}
					bones[link].Rotate(RotationAxis,-turnRadians*STEPSIZE);
				}
				if (crossResult.y < 0.0f)
				{
					if (Damping)
					{
							if (turnRadians > Damping_Max)
							turnRadians = Damping_Max;
					}
					bones[link].Rotate(RotationAxis,turnRadians*STEPSIZE);
				}
			}
			tries++;
			link--; //move to the next link up the chain
		}//end while not-done
	}
}
/*
File: IKSolver.cs
Copyright (c) 2008, Rune Skovbo Johansen  Unity Technologies ApS

See the document "TERMS OF USE" included in the project folder for licencing details.
*/
using UnityEngine;
using System.Collections;

public abstract class IKSolver {
	
	public float positionAccuracy = 0.001f;
	
	public abstract void Solve(Transform[] bones, Vector3 target);
	
}

in some script:

...random control class...
	public IKSolver solver;
	public Transform[] IKbones;
	public Vector3 IKtargetVector;

	void Start () {
		solver = new IKCCD2D();
}

somewhere onclick or on drag or whatever..:

	solver.Solve(IKbones,IKtargetVector);

Somewhat limited as it is there, as i only wanted to deal with rotations in the Y axis, so i flatten all the vectors before doing the calcs, but you can adjust that as you want.

its more of a generic solution than the iksimple from the locomotion, in that i can feed it any number of bones at runtime, and there aren’t any constraints as it stands… (IE: Keeping that knee facing the right direction was a bit of a waste for my mechanical linkage IK)