Set Up IK In Editor

How do you set it up…what scripting is needed to get IK control in the Editor. This would be highly useful for setting script values. Right now I have to hit Play and record the positions by hand. In Edit Mode I could set the IK and Hints and click a button to record them to the appropriate script vars.

Edit…To put it in perspective you have to hit Play…move the IK Goal and rotate it till yer happy…record it to the script as the Inspector window will wipe any changes when heading back to edt mode…then in edit mode you have to either rerecord those into arrays…which I am using…or hit Reset on the script component and relink everything tossed out by the Reset… which while work, is a hellofa lot less than recording 25 Vector3’s by hand in the Inspector to match the script values you recorded. Then i you want Hints you have to move those in edit mode, hit Play and hope…more of a PIA than IK Goals as once you have hit the sweet pole vector spot you may still have to readjust the IK goals and record those.

I don’t know, does OnAnimatorIk even make a pass in the editmode?

Also, doesn’t Skele (AS) do this?

I am not a big editor know-it-all Fox:) I spent four or five hours this morning trying/googling and reading to find this out to no avail. I tried a few things like ExecuteInEditorMode and nada… And not being able to move elbow and knee hints in the Scene View when in PlayMode is a big hindrance to finding just where the sweet spot of an arm or leg position is. If you just can’t currently then Mecanim devs need a kick in the pants to give it to us since we cannot see what is in their black box.

What is Skele(AS)?

Skele

I hear you, and now you will get what you deserve :wink:

Component are only ticked in play mode.
To do what you want, you will need to tick by your self your animator component with animator.Update(); in a Monobehaviour with attribute ExecuteInEditorMode. And you also need to create a controller and set the base layer IK pass On.

You can import this package in 5.1 and open up the scene test. You will see the complete solution.

here the script as a reference

using UnityEngine;
using System.Collections;

[ExecuteInEditMode]
public class EditorIK : MonoBehaviour {

    Animator animator;

    public bool enabledIK = false;

    public GameObject leftFootIKGoal = null;
    public GameObject rightFootIKGoal = null;
    public GameObject leftHandIKGoal = null;
    public GameObject rightHandIKGoal = null;

    public float leftFootPositionWeight = 0;
    public float rightFootPositionWeight = 0;
    public float leftHandPositionWeight = 0;
    public float rightHandPositionWeight = 0;

    public float leftFootRotationWeight = 0;
    public float rightFootRotationWeight = 0;
    public float leftHandRotationWeight = 0;
    public float rightHandRotationWeight = 0;


    protected GameObject CreateIKGoal(string name)
    {
        GameObject obj =  GameObject.CreatePrimitive(PrimitiveType.Sphere);
        obj.name = name;
        obj.transform.localScale = new Vector3(0.2f, 0.2f, 0.2f);
        return obj;
    }

    // Use this for initialization
    void Start () {
        animator = GetComponent<Animator>();

        if (leftFootIKGoal == null)  leftFootIKGoal = CreateIKGoal("LeftFootGoal");
        if (rightFootIKGoal == null) rightFootIKGoal = CreateIKGoal("RightFootGoal");
        if (leftHandIKGoal == null) leftHandIKGoal = CreateIKGoal("LeftHandGoal");
        if (rightHandIKGoal == null) rightHandIKGoal = CreateIKGoal("RightHandGoal");
    }
   
    // Update is called once per frame
    void Update () {
        Debug.Log("EditorIK.Update()");
        animator.Update(0);
    }

    void OnAnimatorIK(int layerIndex)
    {
        Debug.Log("EditorIK.OnAnimatorIK()");
        animator.SetIKPositionWeight(AvatarIKGoal.LeftFoot, leftFootPositionWeight);
        animator.SetIKPositionWeight(AvatarIKGoal.RightFoot, rightFootPositionWeight);
        animator.SetIKPositionWeight(AvatarIKGoal.LeftHand, leftHandPositionWeight);
        animator.SetIKPositionWeight(AvatarIKGoal.RightHand, rightHandPositionWeight);

        animator.SetIKRotationWeight(AvatarIKGoal.LeftFoot, leftFootRotationWeight);
        animator.SetIKRotationWeight(AvatarIKGoal.RightFoot, rightFootRotationWeight);
        animator.SetIKRotationWeight(AvatarIKGoal.LeftHand, leftHandRotationWeight);
        animator.SetIKRotationWeight(AvatarIKGoal.RightHand, rightHandRotationWeight);

        if (enabledIK)
        {
            animator.SetIKPosition(AvatarIKGoal.LeftFoot, leftFootIKGoal.transform.position);
            animator.SetIKRotation(AvatarIKGoal.LeftFoot, leftFootIKGoal.transform.rotation);

            animator.SetIKPosition(AvatarIKGoal.RightFoot, rightFootIKGoal.transform.position);
            animator.SetIKRotation(AvatarIKGoal.RightFoot, rightFootIKGoal.transform.rotation);

            animator.SetIKPosition(AvatarIKGoal.LeftHand, leftHandIKGoal.transform.position);
            animator.SetIKRotation(AvatarIKGoal.LeftHand, leftHandIKGoal.transform.rotation);

            animator.SetIKPosition(AvatarIKGoal.RightHand, rightHandIKGoal.transform.position);
            animator.SetIKRotation(AvatarIKGoal.RightHand, rightHandIKGoal.transform.rotation);
        }
        else
        {
            leftFootIKGoal.transform.position = animator.GetIKPosition(AvatarIKGoal.LeftFoot);
            leftFootIKGoal.transform.rotation = animator.GetIKRotation(AvatarIKGoal.LeftFoot);

            rightFootIKGoal.transform.position = animator.GetIKPosition(AvatarIKGoal.RightFoot);
            rightFootIKGoal.transform.rotation = animator.GetIKRotation(AvatarIKGoal.RightFoot);

            leftHandIKGoal.transform.position = animator.GetIKPosition(AvatarIKGoal.LeftHand);
            leftHandIKGoal.transform.rotation = animator.GetIKRotation(AvatarIKGoal.LeftHand);

            rightHandIKGoal.transform.position = animator.GetIKPosition(AvatarIKGoal.RightHand);
            rightHandIKGoal.transform.rotation = animator.GetIKRotation(AvatarIKGoal.RightHand);
        }
    }
}

2155040–142319–IKinEditor.unitypackage (1.57 MB)

8 Likes

Thanks Pal. You rock:) I am sure many will have a use for this

Very cool! Thanks for the tip =)

I will be up at 6am messing with this… As a clarification when you say “tick” my assumption is using the checkmark that toggles components off and on? And that I tick this on whilst in Edit Mode and Odd when i am in Play Mode? Or something else because I perceive a few ways to discern what methodology you are referring to in “ticking”

By Component are only ticked in play mode I mean: Unity will call Component::Update only when you are in play mode.

That why you need to add a Monobehaviour with ExecuteInEditmode and manually call Animator.Update to see the result in the editor.

1 Like

@Mecanim-Dev I have the IK working In Editor now using the IK goals created. I simply dropped it on my Avatar root where the Animator is. (should I be doing something different?) I substituted my avatar IK goals and they did not work. Do I just get rid of their creation in the Start() function? Also… Great you replied just minutes ago because just minutes ago i added the elbow and knee hints to the script and indeed they do react to them…meaning my internal avatar pole vectors per limb. However now I cannot adjust their position in Editor…This one last deal or understanding of it will make this super sweet for my and I am sure many others purposes. Here is my script…with sliders for adjusting weights.

edit : Just found out that even when I shut the script off and adjust the elbow hints that they snap back to the previous position whether the enableIK bool is set to true or not. What is the trick here?

using UnityEngine;
using System.Collections;

[ExecuteInEditMode]
public class EditorIK : MonoBehaviour {

    Animator animator;

    public bool enabledIK = false;

    //IK Goals
    public GameObject leftFootIKGoal = null;
    public GameObject rightFootIKGoal = null;
    public GameObject leftHandIKGoal = null;
    public GameObject rightHandIKGoal = null;

    //Pole Vectors
    public GameObject leftKneeHint;
    public GameObject rightKneeHint;
    public GameObject leftElbowHint;
    public GameObject rightElbowHint;

    [Range(0.0f, 1.0f)]
    public float leftFootPositionWeight = 0;
    [Range(0.0f, 1.0f)]
    public float rightFootPositionWeight = 0;
    [Range(0.0f, 1.0f)]
    public float leftHandPositionWeight = 0;
    [Range(0.0f, 1.0f)]
    public float rightHandPositionWeight = 0;
    [Range(0.0f, 1.0f)]
    public float rightElbowHintWeight = 0;
    [Range(0.0f, 1.0f)]
    public float leftElbowHintWeight = 0;

    [Range(0.0f, 1.0f)]
    public float leftFootRotationWeight = 0;
    [Range(0.0f, 1.0f)]
    public float rightFootRotationWeight = 0;
    [Range(0.0f, 1.0f)]
    public float leftHandRotationWeight = 0;
    [Range(0.0f, 1.0f)]
    public float rightHandRotationWeight = 0;
    [Range(0.0f, 1.0f)]
    public float rightKneeHintWeight = 0;
    [Range(0.0f, 1.0f)]
    public float leftKneeHintWeight = 0;


    protected GameObject CreateIKGoal(string name)
    {
        GameObject obj =  GameObject.CreatePrimitive(PrimitiveType.Sphere);
        obj.name = name;
        obj.transform.localScale = new Vector3(0.2f, 0.2f, 0.2f);
        return obj;
    }

    // Use this for initialization
    void Start () {
        animator = GetComponent<Animator>();

        if (leftFootIKGoal == null)  leftFootIKGoal = CreateIKGoal("LeftFootGoal");
        if (rightFootIKGoal == null) rightFootIKGoal = CreateIKGoal("RightFootGoal");
        if (leftHandIKGoal == null) leftHandIKGoal = CreateIKGoal("LeftHandGoal");
        if (rightHandIKGoal == null) rightHandIKGoal = CreateIKGoal("RightHandGoal");
    }

    // Update is called once per frame
    void Update () {
        Debug.Log("EditorIK.Update()");
        animator.Update(0);
    }

    void OnAnimatorIK(int layerIndex)
    {
        Debug.Log("EditorIK.OnAnimatorIK()");
        animator.SetIKPositionWeight(AvatarIKGoal.LeftFoot, leftFootPositionWeight);
        animator.SetIKHintPositionWeight (AvatarIKHint.LeftKnee, leftKneeHintWeight);
        animator.SetIKPositionWeight(AvatarIKGoal.RightFoot, rightFootPositionWeight);
        animator.SetIKHintPositionWeight (AvatarIKHint.RightKnee, rightKneeHintWeight);
        animator.SetIKPositionWeight(AvatarIKGoal.LeftHand, leftHandPositionWeight);
        animator.SetIKHintPositionWeight (AvatarIKHint.LeftElbow, leftElbowHintWeight);
        animator.SetIKPositionWeight(AvatarIKGoal.RightHand, rightHandPositionWeight);
        animator.SetIKHintPositionWeight (AvatarIKHint.RightElbow, rightElbowHintWeight);

        animator.SetIKRotationWeight(AvatarIKGoal.LeftFoot, leftFootRotationWeight);
        animator.SetIKRotationWeight(AvatarIKGoal.RightFoot, rightFootRotationWeight);
        animator.SetIKRotationWeight(AvatarIKGoal.LeftHand, leftHandRotationWeight);
        animator.SetIKRotationWeight(AvatarIKGoal.RightHand, rightHandRotationWeight);

        if (enabledIK)
        {
            animator.SetIKPosition(AvatarIKGoal.LeftFoot, leftFootIKGoal.transform.position);
            animator.SetIKRotation(AvatarIKGoal.LeftFoot, leftFootIKGoal.transform.rotation);

            animator.SetIKPosition(AvatarIKGoal.RightFoot, rightFootIKGoal.transform.position);
            animator.SetIKRotation(AvatarIKGoal.RightFoot, rightFootIKGoal.transform.rotation);

            animator.SetIKPosition(AvatarIKGoal.LeftHand, leftHandIKGoal.transform.position);
            animator.SetIKRotation(AvatarIKGoal.LeftHand, leftHandIKGoal.transform.rotation);

            animator.SetIKPosition(AvatarIKGoal.RightHand, rightHandIKGoal.transform.position);
            animator.SetIKRotation(AvatarIKGoal.RightHand, rightHandIKGoal.transform.rotation);
        }
        else
        {
            leftFootIKGoal.transform.position = animator.GetIKPosition(AvatarIKGoal.LeftFoot);
            leftFootIKGoal.transform.rotation = animator.GetIKRotation(AvatarIKGoal.LeftFoot);

            rightFootIKGoal.transform.position = animator.GetIKPosition(AvatarIKGoal.RightFoot);
            rightFootIKGoal.transform.rotation = animator.GetIKRotation(AvatarIKGoal.RightFoot);

            leftHandIKGoal.transform.position = animator.GetIKPosition(AvatarIKGoal.LeftHand);
            leftHandIKGoal.transform.rotation = animator.GetIKRotation(AvatarIKGoal.LeftHand);

            rightHandIKGoal.transform.position = animator.GetIKPosition(AvatarIKGoal.RightHand);
            rightHandIKGoal.transform.rotation = animator.GetIKRotation(AvatarIKGoal.RightHand);
        }
    }
}

Thank you very much for any clarification you can offer. Mecanim rocks my face off. I can finally put together the procedural IK kata rig training system I developed over a few years in C4D in Unity and will be releasing it to the Asset Store soon.

Yes, this is the only step needed to make it work.

For IK hint you also need to send their position/rotation like other IK goal when IK is active
http://docs.unity3d.com/ScriptReference/Animator.SetIKHintPosition.html

And read their position from the animator when ik is not active. if you need some some feedback
http://docs.unity3d.com/ScriptReference/Animator.GetIKHintPosition.html

If you push the concept a little bit more you could even have a controller with some animation clip playing and with the live feeback when IK is not active you will see the current position of IK goal and Hint as computed by the animator for the currently playing clip.

As for the creation of the IK goal and IK hint, you can do as you want, if you want to use other game object simply remove the create function and setup your gameobject in the inspector like you would do to setup reference for your game script

1 Like

I have revised the script to reflect the addition of IK Hints and got the foot IK goals in my rigs hierarchy to work. The KneeHints move the knees now when I rotate the foot IK goals of which they are children as per my setup of the past 13 years creating IK rigs.

However I cannot move the elbowHint positions using the Position Handles in scene view though they do move to the positions I set in script in game mode when playing. Same with As well…I made special proxies so I did not have to set scaling which originally was at 0.1 (using default spheres). Now when I turn their scale to 1 when the EditorIK script is off they immediately revert back to 0.1 when the script is turned back on… What the heck… Makes no sense. I have even reset the script. Is it holding onto this scaling somewhere? The relevant code is as follows …

void OnAnimatorIK(int layerIndex)
    {
        Debug.Log("EditorIK.OnAnimatorIK()");
        animator.SetIKPositionWeight(AvatarIKGoal.LeftFoot, leftFootPositionWeight);
        animator.SetIKHintPositionWeight (AvatarIKHint.LeftKnee, leftKneeHintWeight);
        animator.SetIKPositionWeight(AvatarIKGoal.RightFoot, rightFootPositionWeight);
        animator.SetIKHintPositionWeight (AvatarIKHint.RightKnee, rightKneeHintWeight);
        animator.SetIKPositionWeight(AvatarIKGoal.LeftHand, leftHandPositionWeight);
        animator.SetIKHintPositionWeight (AvatarIKHint.LeftElbow, leftElbowHintWeight);
        animator.SetIKPositionWeight(AvatarIKGoal.RightHand, rightHandPositionWeight);
        animator.SetIKHintPositionWeight (AvatarIKHint.RightElbow, rightElbowHintWeight);

        animator.SetIKRotationWeight(AvatarIKGoal.LeftFoot, leftFootRotationWeight);
        animator.SetIKRotationWeight(AvatarIKGoal.RightFoot, rightFootRotationWeight);
        animator.SetIKRotationWeight(AvatarIKGoal.LeftHand, leftHandRotationWeight);
        animator.SetIKRotationWeight(AvatarIKGoal.RightHand, rightHandRotationWeight);

        if (enabledIK)
        { 
         
            animator.SetIKHintPosition(AvatarIKHint.LeftKnee, leftKneeHint.transform.position);
            animator.SetIKPosition(AvatarIKGoal.LeftFoot, leftFootIKGoal.transform.position);
            animator.SetIKRotation(AvatarIKGoal.LeftFoot, leftFootIKGoal.transform.rotation);
         
            animator.SetIKHintPosition(AvatarIKHint.RightKnee, rightKneeHint.transform.position);
            animator.SetIKPosition(AvatarIKGoal.RightFoot, rightFootIKGoal.transform.position);
            animator.SetIKRotation(AvatarIKGoal.RightFoot, rightFootIKGoal.transform.rotation);

            animator.SetIKHintPosition(AvatarIKHint.LeftElbow, leftElbowHint.transform.position);
            animator.SetIKPosition(AvatarIKGoal.LeftHand, leftHandIKGoal.transform.position);
            animator.SetIKRotation(AvatarIKGoal.LeftHand, leftHandIKGoal.transform.rotation);

            animator.SetIKHintPosition(AvatarIKHint.RightElbow, rightElbowHint.transform.position);
            animator.SetIKPosition(AvatarIKGoal.RightHand, rightHandIKGoal.transform.position);
            animator.SetIKRotation(AvatarIKGoal.RightHand, rightHandIKGoal.transform.rotation);
        }
        else
        {
            leftKneeHint.transform.position = animator.GetIKHintPosition(AvatarIKHint.LeftKnee);
            leftFootIKGoal.transform.position = animator.GetIKPosition(AvatarIKGoal.LeftFoot);
            leftFootIKGoal.transform.rotation = animator.GetIKRotation(AvatarIKGoal.LeftFoot);

            rightKneeHint.transform.position = animator.GetIKHintPosition(AvatarIKHint.RightKnee);
            rightFootIKGoal.transform.position = animator.GetIKPosition(AvatarIKGoal.RightFoot);
            rightFootIKGoal.transform.rotation = animator.GetIKRotation(AvatarIKGoal.RightFoot);

            leftElbowHint.transform.position = animator.GetIKHintPosition(AvatarIKHint.LeftElbow);
            leftHandIKGoal.transform.position = animator.GetIKPosition(AvatarIKGoal.LeftHand);
            leftHandIKGoal.transform.rotation = animator.GetIKRotation(AvatarIKGoal.LeftHand);

            rightElbowHint.transform.position = animator.GetIKHintPosition(AvatarIKHint.RightElbow);
            rightHandIKGoal.transform.position = animator.GetIKPosition(AvatarIKGoal.RightHand);
            rightHandIKGoal.transform.rotation = animator.GetIKRotation(AvatarIKGoal.RightHand);
        }
    }
}

Just did a test and IK hint seem to behave normally in 5.1, here the code just in case.
I’m not sure I understand exactly what is not working. You cannot manipulate elbow IK hint at all? that doesn’t make any sense if nobody is actually writing values into thoses transform.

using UnityEngine;
using System.Collections;

[ExecuteInEditMode]
public class EditorIK : MonoBehaviour {

    Animator animator;

    public bool enabledIK = false;

    public GameObject leftFootIKGoal = null;
    public GameObject rightFootIKGoal = null;
    public GameObject leftHandIKGoal = null;
    public GameObject rightHandIKGoal = null;

    public GameObject leftKneeIKHint = null;
    public GameObject rightKneeIKHint = null;
    public GameObject leftElbowIKHint = null;
    public GameObject rightElbowIKHint = null;

    public float leftFootPositionWeight = 0;
    public float rightFootPositionWeight = 0;
    public float leftHandPositionWeight = 0;
    public float rightHandPositionWeight = 0;

    public float leftKneePositionWeight = 0;
    public float rightKneePositionWeight = 0;
    public float leftElbowPositionWeight = 0;
    public float rightElbowPositionWeight = 0;

    public float leftFootRotationWeight = 0;
    public float rightFootRotationWeight = 0;
    public float leftHandRotationWeight = 0;
    public float rightHandRotationWeight = 0;


    protected GameObject CreateIKGoal(string name)
    {
        GameObject obj =  GameObject.CreatePrimitive(PrimitiveType.Sphere);
        obj.name = name;
        obj.transform.localScale = new Vector3(0.2f, 0.2f, 0.2f);
        return obj;
    }

    // Use this for initialization
    void Start () {
        animator = GetComponent<Animator>();

        if (leftFootIKGoal == null)  leftFootIKGoal = CreateIKGoal("LeftFootGoal");
        if (rightFootIKGoal == null) rightFootIKGoal = CreateIKGoal("RightFootGoal");
        if (leftHandIKGoal == null) leftHandIKGoal = CreateIKGoal("LeftHandGoal");
        if (rightHandIKGoal == null) rightHandIKGoal = CreateIKGoal("RightHandGoal");

        if (leftKneeIKHint == null) leftKneeIKHint = CreateIKGoal("LeftKneeIKHint");
        if (rightKneeIKHint == null) rightKneeIKHint = CreateIKGoal("RightKneeIKHint");
        if (leftElbowIKHint == null) leftElbowIKHint = CreateIKGoal("LeftElbowIKHint");
        if (rightElbowIKHint == null) rightElbowIKHint = CreateIKGoal("RightElbowIKHint");
    }
   
    // Update is called once per frame
    void Update () {
        animator.Update(0);
    }

    void OnAnimatorIK(int layerIndex)
    {
        animator.SetIKPositionWeight(AvatarIKGoal.LeftFoot, leftFootPositionWeight);
        animator.SetIKPositionWeight(AvatarIKGoal.RightFoot, rightFootPositionWeight);
        animator.SetIKPositionWeight(AvatarIKGoal.LeftHand, leftHandPositionWeight);
        animator.SetIKPositionWeight(AvatarIKGoal.RightHand, rightHandPositionWeight);

        animator.SetIKHintPositionWeight(AvatarIKHint.LeftKnee, leftKneePositionWeight);
        animator.SetIKHintPositionWeight(AvatarIKHint.RightKnee, rightKneePositionWeight);
        animator.SetIKHintPositionWeight(AvatarIKHint.LeftElbow, leftElbowPositionWeight);
        animator.SetIKHintPositionWeight(AvatarIKHint.RightElbow, rightElbowPositionWeight);

        animator.SetIKRotationWeight(AvatarIKGoal.LeftFoot, leftFootRotationWeight);
        animator.SetIKRotationWeight(AvatarIKGoal.RightFoot, rightFootRotationWeight);
        animator.SetIKRotationWeight(AvatarIKGoal.LeftHand, leftHandRotationWeight);
        animator.SetIKRotationWeight(AvatarIKGoal.RightHand, rightHandRotationWeight);

        if (enabledIK)
        {
            animator.SetIKPosition(AvatarIKGoal.LeftFoot, leftFootIKGoal.transform.position);
            animator.SetIKHintPosition(AvatarIKHint.LeftKnee, leftKneeIKHint.transform.position);
            animator.SetIKRotation(AvatarIKGoal.LeftFoot, leftFootIKGoal.transform.rotation);

            animator.SetIKPosition(AvatarIKGoal.RightFoot, rightFootIKGoal.transform.position);
            animator.SetIKHintPosition(AvatarIKHint.RightKnee, rightKneeIKHint.transform.position);
            animator.SetIKRotation(AvatarIKGoal.RightFoot, rightFootIKGoal.transform.rotation);

            animator.SetIKPosition(AvatarIKGoal.LeftHand, leftHandIKGoal.transform.position);
            animator.SetIKHintPosition(AvatarIKHint.LeftElbow, leftElbowIKHint.transform.position);
            animator.SetIKRotation(AvatarIKGoal.LeftHand, leftHandIKGoal.transform.rotation);

            animator.SetIKPosition(AvatarIKGoal.RightHand, rightHandIKGoal.transform.position);
            animator.SetIKHintPosition(AvatarIKHint.RightElbow, rightElbowIKHint.transform.position);
            animator.SetIKRotation(AvatarIKGoal.RightHand, rightHandIKGoal.transform.rotation);
        }
        else
        {
            leftFootIKGoal.transform.position = animator.GetIKPosition(AvatarIKGoal.LeftFoot);
            leftKneeIKHint.transform.position = animator.GetIKHintPosition(AvatarIKHint.LeftKnee);
            leftFootIKGoal.transform.rotation = animator.GetIKRotation(AvatarIKGoal.LeftFoot);

            rightFootIKGoal.transform.position = animator.GetIKPosition(AvatarIKGoal.RightFoot);
            rightKneeIKHint.transform.position = animator.GetIKHintPosition(AvatarIKHint.RightKnee);
            rightFootIKGoal.transform.rotation = animator.GetIKRotation(AvatarIKGoal.RightFoot);

            leftHandIKGoal.transform.position = animator.GetIKPosition(AvatarIKGoal.LeftHand);
            leftElbowIKHint.transform.position = animator.GetIKHintPosition(AvatarIKHint.LeftElbow);
            leftHandIKGoal.transform.rotation = animator.GetIKRotation(AvatarIKGoal.LeftHand);

            rightHandIKGoal.transform.position = animator.GetIKPosition(AvatarIKGoal.RightHand);
            rightElbowIKHint.transform.position = animator.GetIKHintPosition(AvatarIKHint.RightElbow);
            rightHandIKGoal.transform.rotation = animator.GetIKRotation(AvatarIKGoal.RightHand);
        }
    }
}
3 Likes

@Mecanim-Dev Based on your sample codes i’m building a solution for making small corrections to any Mocap animation applied to a Humanoid right in Editor. I create a sort of “shadow rig” (actually i create 4 “shadow limbs”) that moves with the actual mocap data and allows you to move IKgoals and IKhints (which are children of these shadow limbs) in a sort of local limb space.

The goal of this is to fix hands compenetrations with the body during standing idles, for example, or incorrectly placed feet on the ground… In short terms, i compensate erroneous animations with an animation-delta (which can also be animated during clips) using the Mecanim IK solver.

I’m already at a very good point, but i just stumbled against a (i hope not major) issue: since my code works almost entirely in the OnAnimatorIK callback i have problems to see it working when I move the timeline in the Editor to preview just one animation from the animator (which can be a very complex state machine). Since I’m calling animator.Update(0) basically at any Update function of a ExecuteInEditMode script, it completely breaks the preview of the animation, resulting in just the 1st frame being displayed (and making my script almost useless in edit mode). I also tried removing the forced animator update, hoping that, when you move the timeline, an actual animator.Update call is raised from the timeline, but with no luck: there is no OnAnimatorIK callbacks during animation preview in editor (I’m still not sure why).

At the end i can only use my code in runtime, pausing the game when I need to making corrections, moving goals or hints without actually seeing the result live (I’m still in pause…), resuming (hoping that the correction was enough and not too much), and then, when it’s everything looking good, copying the values of the component and re-pasting them when i stop the game…

It may be sound too preposterous asking for any advice but, any advice? :slight_smile:

Yes this is the expected behaviour when you move the animation window timeline. The animation window use a Sampling function which create a custom controller with only one clip to avoid having weird behaviour when you key frame a clip, it would not make any sense in the animation window context to have a transition triggered while playing one animation clip.
http://docs.unity3d.com/ScriptReference/AnimationMode.SampleAnimationClip.html

In this case I think you will need to create your own keyframing tools which should sample your complete controller with animator.Play() to set the current time and animator.Update(0) to force the update

1 Like

Thank you for your explanation. Now I totally understand the behaviour of the Animation window. It just create an overriding controller that play a single clip “as is”, basically taking values from the clip and only assigning them without any consideration for layers or other things that can be stacked in runtime.

It seems really obvious now, the only way to go is to create a sort of “runtime timeline” in editor to see the full stack of animations and IK layers evaluated.

Thank you again!

Waiting for Unity3d’s Animation Job system.

I ran into a small issue with that technique. The IK script works fine in the Editor now but when I do a change and I save the scene, it stops triggering the OnAnimatorIK() function for some reason. The Update function still triggers like usual but it looks like the “animator.Update(0)” stops to work… Any clue on why is that and any fix?

@danUnity_1 this worked for me:

        animator.Update( 0 );
        script.transform.position = script.transform.position + Vector3.one;
        script.transform.position = script.transform.position - Vector3.one;

@Vladerx not sure what you are doing there. You are moving up and down to the exact same location? How can you even know it works or not?