Search Unity

Question about IK targets

Discussion in 'Animation' started by Zomby138, Oct 14, 2014.

  1. Zomby138

    Zomby138

    Joined:
    Nov 3, 2009
    Posts:
    659
    Hi.

    I'm using the mecanim IK targets to help position my character's hands and feet. It works pretty well overall.

    However, a lot of the time it would be very useful to know where the position of the foot/hand would be if there was no IK correction. So I can see how far the foot/hand has been pulled from it's unadulterated animation position.

    Does anyone know of any way to do this? I've tried setting the IK weight to zero, checking the position, then setting it back to 1, but it doesn't work, the actually positions seem to wait till the end of the cycle to actually change.

    Anyone got any ideas for a work around?
     
  2. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
  3. Zomby138

    Zomby138

    Joined:
    Nov 3, 2009
    Posts:
    659
    Thank you for the reply!

    Unfortunately you seen to have misunderstood me. GetIKPosition() gives me the TARGET position that I have set myself. I want to know where the hand or foot WOULD be if it had no IK applied to it at all.

    Believe me, I've tried that function :p

    Right now I'm using a totally ridicules work around. I have a separate, invisible version of my character, doing exactly the same animations and following my character's transform. The only difference is this one does have any IK at all, so I can actually get the original positions of the hands and feet from it.

    >_<

    My work around is working perfectly. But it's totally stupid!
     
  4. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    Call the function before setting the target and you will get the current position of this effector without IK. Mecanim update these target just before it call your OnAnimatorIK()
     
  5. Zomby138

    Zomby138

    Joined:
    Nov 3, 2009
    Posts:
    659
    Are you telling me that will work even if I set the target in the previous update cycle? It really doesn't work man... The hand is already in an IK'ed position from the previous frame, that's the position GetIKPosition() gives me.

    Can you show me some sample code that sets the target every frame, and also get's the original position every frame?
     
  6. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    Yes, this is how mecanim work. Just before calling your OnAnimatorIK callback, mecanim will compute the current IK position based on the FK animation evaluated by the animation engine. It never look at the previous frame transform.
    It's even used by the animator Gizmo to show the current position of the effectors (this is a 5.0 feature).

    On the following image you can see 3 game object call: FirstPass(Cube near leg), SecondPass(Cube right on left hand) and Effector(Sphere right on left hand)
    IKPosition.png
    And this is the code that goes with this, you can clearly see that I'm retrieving the current position of the IK goal before setting the new IK goal.
    In this case I did add a second layer just to show you the new position of the IK goal after the frist IK pass for the first layer.

    Code (CSharp):
    1. public class IKExample : MonoBehaviour {
    2.     private GameObject leftHandEffector;
    3.     private GameObject leftHandFirstPass;
    4.     private GameObject leftHandSecondPass;
    5.     private Animator animator;
    6.  
    7.     // Use this for initialization
    8.     void Start () {
    9.         leftHandEffector = GameObject.CreatePrimitive(PrimitiveType.Sphere);
    10.         leftHandEffector.transform.position = new Vector3(-0.4f, 1.0f, 0.00f);
    11.         leftHandEffector.transform.localScale = new Vector3(0.07f, 0.07f, 0.07f);
    12.         leftHandEffector.name = "Effector";
    13.  
    14.         leftHandFirstPass = GameObject.CreatePrimitive(PrimitiveType.Cube) as GameObject;
    15.         leftHandFirstPass.name = "FirstPass";
    16.         leftHandFirstPass.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);
    17.         leftHandSecondPass = GameObject.CreatePrimitive(PrimitiveType.Cube) as GameObject;
    18.         leftHandSecondPass.name = "SecondPass";
    19.         leftHandSecondPass.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);
    20.         animator = GetComponent<Animator>();
    21.     }
    22.    
    23.     // Update is called once per frame
    24.     void Update () {
    25.    
    26.     }
    27.  
    28.     void OnAnimatorIK(int layerIndex)
    29.     {
    30.         if (layerIndex == 0)
    31.         {
    32.             leftHandFirstPass.transform.position = animator.GetIKPosition(AvatarIKGoal.LeftHand);
    33.  
    34.             animator.SetIKPosition(AvatarIKGoal.LeftHand, leftHandEffector.transform.position);
    35.             animator.SetIKPositionWeight(AvatarIKGoal.LeftHand, 1.0F);
    36.         }
    37.         else if (layerIndex == 1)
    38.         {
    39.             leftHandSecondPass.transform.position = animator.GetIKPosition(AvatarIKGoal.LeftHand);
    40.         }
    41.     }
    42. }
     
  7. Zomby138

    Zomby138

    Joined:
    Nov 3, 2009
    Posts:
    659
    Interesting! Hold on to your horses, please tell me what this layerIndex is all about? I've never used that before. I've just been putting all my IK code in the OnAnimatorIK function without checking that value... which, looking at your code, makes me think it's doing all the calls twice per frame.. or maybe even more than twice.

    Is any of this documented somewhere?

    I just followed this:
    http://docs.unity3d.com/ScriptReference/MonoBehaviour.OnAnimatorIK.html

    They don't look at the layerIndex... is that page missing some information?
     
  8. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    It depend on how many layer you have and if they have IK pass checked. For each layer with IK pass checked you will get a OnAnimatorIK callback with the layer index. This is pretty useful if you have a complex IK setup between two character that are wrestling by example, you need a few pass to resolve the final Ik solution in those case

    If you only have one layer then you can safely ignore layerIndex. The reason why i'm using it in this sample was to show you the different position of the IK effector: before setting the IK goal, after setting the IK goal and after mecanim did they IK solving.
     
    theANMATOR2b likes this.
  9. Zomby138

    Zomby138

    Joined:
    Nov 3, 2009
    Posts:
    659
    Ok I think I get it. So am I right in thinking that if you have only one layer (the base layer, with IK Pass ticked) then the function will be called twice?

    Once with layerIndex = 0, the prepass.
    Then once with layerIndex = 1, the pass for the base layer.

    If that's so, surely in the sample code in the script reference page, those functions are all called twice per frame?

    By the way, it does work now. But I'm a bit miffed that there was no explanation for this anywhere on the internet. I wasted so much time on this!
     
  10. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    You only get one call for each layer with IK pass ticked.
    the pseudo code look like this:
    Code (CSharp):
    1.  
    2. foreach layers with IK pass ticked
    3. {
    4.     Prepare IK goal
    5.     Call OnAnimatorIK
    6.     Solve IK with goal.
    7. }
     
    IbrahimRashwan likes this.
  11. theANMATOR2b

    theANMATOR2b

    Joined:
    Jul 12, 2014
    Posts:
    7,790
    Just for my curiosity Zomby / Mecanim.Dev, can you explain the reason you want this information in the editor? It seems to me if you get the IK working, placing the hand where you want it to on the surface of whatever, why do you need the FK information for?
    What benefit does receiving this FK information give you?

    Thanks for clarifying, it could be helpful in the future.
     
  12. Zomby138

    Zomby138

    Joined:
    Nov 3, 2009
    Posts:
    659
    @theANMATOR2b Good question! Right now I'm using it for two things.

    Firstly. I use IK to help keep my character's feet still while they are in contact with the ground while she's moving around. I also do the same for her hands when she's climbing along a ledge. In normal usage I can rely on my animation events to tell me when these IKs need to be locked and unlocked, but, sometimes when changing between animation states one of these IKs ends up being left on lock for too long. I basically check the IK target position against the FK position to see if she's trying to move her limb away from where it's been locked too, and if she is, I fade down the IK weight. If I don't do this I can end up in some really ugly situations where a leg or arm is stretched out.

    The second thing I'm using it for is to give her arms some bounce while she's not using them to hold a ledge or block etc. I basically use a bit of maths to simulate a loose spring that pulls the IK target towards the FK position all the time. It really helps make animation transitions look more fluid. For instance if she turns quickly on the spot then stops, her arms will swing around a bit then come to rest at her sides :)

    Does that answer your question? I may well end up with more uses for it in the future.
     
    Last edited: Oct 24, 2014
    IbrahimRashwan and theANMATOR2b like this.
  13. theANMATOR2b

    theANMATOR2b

    Joined:
    Jul 12, 2014
    Posts:
    7,790

    Thanks for the reply Zomby.

    Doe this IK locking occur when the IK state is interrupted half way through the state and quickly transitions into a FK state with her hands? I could see that causing issues.

    Yeah and thanks for the extra information. I can see how your 2nd example IK/FK implementation could add very subtle animation variances which is always nice to give a little bit more life to the characters we create.

    It's probably obvious; I use the Indie (free) version. :)
     
  14. Zomby138

    Zomby138

    Joined:
    Nov 3, 2009
    Posts:
    659
    @theANMATOR2b Yes that's right. It's particularly bad if the thing that interrupts the animation also moves her relatively quickly in any direction.
     
  15. vladk

    vladk

    Joined:
    Jul 10, 2008
    Posts:
    167
    I'm really sorry about the necro-posting, but it's just the thing I'm struggling with right now. When I use this script above both "first pass" and "second pass" objects are in the same place. Looks like the GetIKPosition returns the already changed position by the SetIKPosition in the previous frame.
     
  16. vladk

    vladk

    Joined:
    Jul 10, 2008
    Posts:
    167
    Guys, seriously. After multiple test I can confirm that the GetIKPosition returns wrong values. IT DOESN'T WORK! I need help fixing it!

    And it definitely not related to the latest fix in 2017.1.1p3 - the behavior didn't change when I updated to this version.
     
    Last edited: Sep 25, 2017
  17. Zomby138

    Zomby138

    Joined:
    Nov 3, 2009
    Posts:
    659
    I feel for you vladk, believe me. I remember how much trouble I had getting it working.

    My code is still working in my project, and it's very similar to your code. Although I am still using 5.6.3p1

    Are you sure that both your animator layers have the IK checkbox ticked?

    If it turns out that they've broken it in 2017 then I will not be happy. I don't have a reason to upgrade right now, but I might in the future.
     
  18. 00christian00

    00christian00

    Joined:
    Jul 22, 2012
    Posts:
    1,035
    @Mecanim-Dev
    Is it possible to also know the original hand position before humanoid retargeting, as it is in the original animation?