ADDING NATURAL IK TO YOUR ANIMATIONS IN UNITY
Using Unity Version 2019.4
Inverse Kinematics (IK) refers to the use of kinematic equations to determine movements required of joints along a chain to drive an end effector to a desired position.
The best way to visualize IK is to imagine a robotic arm, with proper joints. When we animate with Forward Kinematics, we move our end effector into position by manipulating from the parent joints (shoulder, then elbow, then wrist, for example). With Inverse Kinematics, we move our end effector into position and kinematic equations figure out where all of the arm bones need to be in order to move it there.
So how can you use it in Unity, and what if you want it to play nicely with an animation?
Your character needs to pick something up. You have the animation and prop all set up, but the character’s hand doesn’t properly reach the object no matter how precisely you try to set up the scene.
Use Inverse Kinematics!
If you want to be extremely precise about it, get Final IK. It is, without a doubt, the top IK asset package on the Unity Asset Store.
That being said, it is pricey and has many features that you may not need. If that’s the case, Unity has it’s own IK solution that you can easily tap into. Whichever way you go, you can use the principles in this tutorial to make IK play nicely with your animations.
What you need:
A character with a humanoid rig. I’ll be using my casual version of Dahlia Hart.
An animation to add the IK to. I’ve downloaded a “pick up” clip from Mixamo.
A prop to interact with. I’ve created a ball from a sphere primitive.
First, let’s set up the scene. Your character will need an animator controller with your pickup animation included. This tutorial is not going to cover navigation, so my animations transition from walk, to idle, to my pickup animation with no conditions. Dahlia will walk for one loop, transition to idle, and then reach out to pick up the ball on the table.
Let’s run the scene and see what happens.
There are a couple of obvious issues. First, Dahlia doesn’t actually pick up the ball, and second, her hand doesn’t connect with the ball in a natural way. Fortunately, we can take care of both of these things with fairly simple code.
We'll start with picking up the object. The principle here is to pinpoint the time in the animation when she would likely pick up the ball and add an animation event which will call a script that parents the prop to the correct hand at that moment.
NOTE: Animation events can not be added to Read-Only animations. If you’ve imported the clip, make sure you duplicate the animation and use the copy on your animator controller.
Scrub the timeline on the animation until you find the point where you want your character to interact with the item. Then click on the little flag to add an event.
If you click on the event marker, you’ll see a dropdown menu in the inspector. Right now it doesn’t have any functions to call, but any public functions we add to the character being animated will end up here.
The first piece of code we’re going to create should be called something like “PickUpObject”. It will contain two functions:
A function to parent the object to the right hand, and
A pause function for fine-tuning prop locations and IK transforms.
The pause function is purely to aid setup. By adding it, we can pause the character at the exact point we are going to parent the object. This is extremely helpful because our characters’ position at the end of our animation chain won’t be available in edit mode, and IK transforms can’t be adjusted when the scene is paused.
Let’s make the pause function first:
Easy, right? Now let’s add the PickUpObject script to our character and call the Pause function from our animation event. If you enter playmode now, you should see the animation pause at the point of contact. While still in play mode, adjust the position of your prop to be as close as possible to the character’s hand. Copy the new transforms for the prop and exit playmode so you can copy them to the prop in edit mode.
Now we need to create a function in our PickUpObject script that parents the object to the character’s hand. We’ll need to reference a new position and rotation for the prop once it is picked up because we want it to be held correctly in the character’s hand. To demonstrate, I’ll create an empty gameobject called “NewTransform” to save the transform data, but you could do this with a database (using the position and rotation data) or a list.
In our code, create public parameters for the object to be picked up:
pickupObject: the gameObject that we want to pick up,
parentTransform: the transform we want to parent it to (in this case, the right hand), and
newObjectPosRot: the new transform for the object once it is held.
And here's the new function we need to pick up the prop:
Back in your scene, populate the fields with the prop, the parent transform and the NewTransform gameobject. Go back to the animation event and select “PickUp” from the dropdown menu. Now run the animation again but this time, pause the scene right after the character grabs the prop.
In paused playmode, adjust the prop until you are happy with how it is positioned. You will need to copy this transform data after it is parented because we are using local transforms; the values will change when it’s parented or unparented (and we want to capture the parented values). Again, exit playmode and copy these values to our “NewTransform” gameobject. When you run the scene again, the object should be placed correctly in the character’s hand.
Unfortunately, the ball appears to jump into Dahlia's hand.
We’ll set up our IK to take care of this problem.
Let’s start by creating two new parameters in our controller: IKPositionWeight and IKRotationWeight. We can add these as properties to our animation by selecting “Add Property” and clicking the dropdown arrow next to Animator. Click on the plus sign beside each value to add them to the clip. Now switch from Dopesheet view to Curves. Here, you’ll want to add a key frame with a value of 1 at the point of contact, where our animation event fired off, and adjust the curves so they lerp nicely. (The curves should look the same.)
With our curves set up, we now need our character to pay attention to them. Make sure IK Pass in enabled on the layer containing the animations.
We could add the code for IK straight to our pick up code, but that would require an Update function, which I like to stay away from. Instead, if we go to the animation clip, you’ll see an option to add a Behaviour. Click on the button to create a new behaviour and name it “PickUpIK”. This will create a new C# script with several options, all commented out.
The two we’ll be using are OnStateEnter, which is called when a transition starts, and OnStateIK, which is called right after Animator.OnAnimatorIK. In OnStateEnter, we’ll cast our reach target, and in OnStateIK, we’ll have the IK target follow the position and rotation weights of the new float curves we just created. Here’s what the final PickUpIK script looks like:
To make this work, we need to go back to our PickUpObject script and set up a transform reference for the character to reach to. We can’t use the prop itself, because it would mess with our IK when the prop becomes parented to the characters hand. I’m going to create another empty gameobject and call it “Reach Target”. (Again, you might want to do this in an organized way, but this is just for demonstration purposes.) Let’s add that parameter to our PickUp code.
You may wonder here why we’re adding it to the PickUpObject script instead of the behaviour. Behaviours are efficient in that they will call every frame only during that state. That’s why we’re using it to nicely lerp the IK. However, they do not allow public fields which means we need another way to reference the transform we want to reach to.
After you drag the reach transform to the new field in the inspector, go back to the animation event and switch it back to call the pause function. This time, we’re going to run the animation and adjust where we want the hand effector to reach to.
Once the animation stops, and without pausing the scene, adjust the position and rotation of the reach target transform until the hand position is exactly where you want it. Again, copy the transform values, exit playmode and copy them to the reach target reference gameobject.
Now let’s switch the event back to Pick Up and run our scene again. You should see the character perfectly picking up the prop.
Here's the full PickUpObject code:
NOTE: If you want the character to also look at the target while you're running the animation, you can add a parameter for the LookAt weight.
Used in this article: