Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Head bone won't rotate via script.

Discussion in 'Animation' started by voltage, Nov 22, 2016.

  1. voltage

    voltage

    Joined:
    Nov 11, 2011
    Posts:
    515
    So I've got a head bone that I want to rotate when changing the Mouse Y axis. I've achieved the code with this:
    Code (CSharp):
    1. head.transform.Rotate(new Vector3(-90f, 0, 0) * Time.deltaTime, Space.Self);
    And it worked perfectly. However, after adding additional animations to my player - the game will no longer allow me to rotate the head. I rotate the Right Shoulder via script as well and that still rotates. What gives? It's super confusing. I've checked all my code left and right - and there's quite literally NOTHING via script hindering the free motion. I decided to delete the head bone's keyframe from the idle animation just to see if it can rotate then - no luck. Finally, I've also played with the head bone in editor pause mode and it won't turn.

    This model was created in Blender. I'm at a loss right now. No understanding on what could've possibly gone wrong.

    EDIT: Disabling the Animator completely will allow the head to rotate through editor pause mode. Yet rotating Right shoulder regardless of animator allows rotation. Keyframes are active for right shoulder. Strange.
     
    Last edited: Nov 22, 2016
  2. theANMATOR2b

    theANMATOR2b

    Joined:
    Jul 12, 2014
    Posts:
    7,790
    I'm not familiar with manipulating bones with code but just thought mentioning this might help.

    This sounds like something related to IK, since arms and legs use IK commonly, but the head/neck are usually FK, maybe there is something that needs to be added to the head/neck chain (related to IK) to allow the code to work properly.
    afaik - IK is calculated after the animation pass.
     
  3. voltage

    voltage

    Joined:
    Nov 11, 2011
    Posts:
    515
    Thank you so much for replying Animator. I thought about your theory, but I'm not sure it can hold water in this specific scenario.
    You're correct about the arms being IK's, but the shoulders themselves cannot deform. In this model they serve as a rotation point or socket for the entire limb. And while the right shoulder rotates through code, the left won't! I have absolutely no idea why. I'm very confused. lol
     

    Attached Files:

  4. dibdab

    dibdab

    Joined:
    Jul 5, 2011
    Posts:
    976
    try it in LateUpdate.

    animations go in Update and IK in LateUpdate, so it you want to rotate transform with animations, it has to be lateUpdate.
     
    cRaZy92 likes this.
  5. voltage

    voltage

    Joined:
    Nov 11, 2011
    Posts:
    515
    Thank you for that info. I'll be sure to edit this post with an update once I find out what happens.

    EDIT: I added script rotation for leftShoulder in LateUpdate() and the results are all the same. It's not possible to rotate this shoulder through code. I have no idea why, but that's how it seems to go. Same as the head, only when the animator is disabled entirely will they rotate. Strange as I'm not modifying the leftShoulder's axis whatsoever besides transform.Rotate x Mouse Y.

    I believe it has to do with something in my keyframing, but god knows what. How could this all happen, yet right shoulder is fine? To be clear, I have keyframes for the right shoulder as well, but it rotates through code all the same.

    I deleted the keyframes for Head, rightShoulder and left inside my idle animation - but everything remains the same. Only the rightShoulder rotates.

    I can say now with absolute certainty that it's not the scripting - but the animation keyframes themselves. I fiddled around in blender with my idle animation too much and I've lost control of the right shoulder. All the updating is still in LateUpdate().
     
    Last edited: Nov 28, 2016
  6. voltage

    voltage

    Joined:
    Nov 11, 2011
    Posts:
    515
    I tried both of these mask methods on separate occasions too and it did nothing. Running out of ideas.

    EDIT: So masks don't work, LateUpdate() doesn't work, removing keyframes doesn't work, OnAnimatorMove() didn't work.

    In Input.GetAxis("Mouse Y") I added an empty and rotate that instead of head. I then set head to match the empty's rotation every LateUpdate() frame. Still nothing.
    Code (CSharp):
    1. headTracking.transform.Rotate(new Vector3(-90f, 0, 0) * Time.deltaTime, Space.Self);
    2.                 head.transform.localEulerAngles = headTracking.transform.localEulerAngles;
    EDIT 2: Yes! I think I'm figuring it out. I added the keyframes back to my idle animation, applied a mask to the head and now I'm updating headTracking's rotation in Update() and then I apply
    Code (csharp):
    1. head.transform.localEulerAngles = headTracking.transform.localEulerAngles;
    in LateUpdate().

    My key takeaway from this experience thus far is that an animation clip will override your bone rotation/location automatically every frame even if they don't have a keyframe applied. So you might as well keyframe your whole skeleton and apply the mask. After which, you need to LateUpdate() your bone's rotation every single frame in-order to prevent Mecanim from overriding it. But you can't simply rotate gradually in LateUpdate() but rather copy and paste your transition. Therefore lerp your transition in Update() with an empty and grab it's data for later. No information on the subject I've found over the net has been this specific. I wish it were, but I hope this helps someone out in the future.

    I shutter at the thought of applying these rotations in RPC's. I'll probably be back if I'm struggling.
     

    Attached Files:

    Last edited: Nov 28, 2016
  7. NeatWolf

    NeatWolf

    Joined:
    Sep 27, 2013
    Posts:
    924
    I believe you should select the affected bones and mark as green the one you're going to use, not the other way around.
    Also make sure you're tweaking the appropriate animation layer by checking the appropriate parameter in the IK callback function, and remember to set the override layer with weight =1, with that layer set to Override, down in the Animator layer queue.

    Were you trying to achieve this by using the Base Layer only?
     
  8. mijagourlay

    mijagourlay

    Joined:
    Apr 18, 2017
    Posts:
    4
    FWIW I am having a similar issue:

    I have a character with an Animator, a track in the timeline associated with that Animator, and a clip that plays in that track. This Animator / track / clip animates the entire character.

    I want a separate track (or script) to control jaw flaps. So I have another Animator on just the jaw joint.

    What I tried:
    • Adding a mask to each animation clip to mask OUT the jaw bone (i.e. all joints are checked except the jaw joint).
    • Adding an Avatar Mask with the same mask as I tried on the clip.
    So far the jaw joint animator+track+clip has NOT worked reliably. (See below for weird caveat.)

    Also, the script does NOT work if the update happens in Update. But it DOES work if it runs in LateUpdate.

    I confirmed that if I disable the Animator on the whole character then the jaw bone animation clip works as expected.
    I confirmed that if I disable ALL Animators then the script that modifies the jaw bone transform works.

    My conclusion is that since it works in isolation the jaw animator+track+clip isn't completely wrong.

    Here's the weird part:

    SOMETIMES when I use the timeline preview, the jaw joint track works as expected; the full-body animation plays on the character, and the jaw joint animates according to the jaw joint animation animator / track / clip.

    But only SOMETIMES. For example, just after I initially created the jaw track, it worked. And sometimes if I move that track around in the timeline editor, then it works a single time. But Unity always goes back to ignoring that track.

    Out of desperation I also tried disabling Write Defaults on the animation clip but that had no lasting effect. (Actually toggling that setting is one of the weird times when the timeline preview temporarily showed the jaw bone animating, but alas as before that stopped working after I pressed "Play" in the editor.)

    Definitely seems like a bug with animation clip masks and avatar masks.

    Anyway, as I wrote, if I update the jaw bone joint in LateUpdate then the script does indeed animate the jaw bone.

    I only want jaw flap to happen when the character is talking, though, so rather than animating the jaw via a timeline Animation track+clip, I need to use an Activation track to control when the script runs. But since Activation tracks operate on GameObjects (not Components) I need to put that script on its own GameObject just to control its activation.

    *sigh* what a pain.
     
  9. SpaceManDan

    SpaceManDan

    Joined:
    Aug 3, 2013
    Posts:
    15

    I can not thank you enough. I've spent HOURS trying to get this to work using every method I can find on the internet but nothing helped.

    I can confirm,

    1. Keyframes don't matter.

    2. I did NOT need a mask in the end.

    3. Must use an empty that you lerp in UPDATE, then set the bone to match it in LateUpdate (pNeck.transform.rotation = neckOverride.transform.rotation;) This was actually the only trick. I was lerping in lateUpdate and that wont work BECAUSE if you follow the logic...

    Frame 1
    -Animator moves bone to animations location/rotation.
    -LateUpdate lerps bone toward desired location/rotation.

    Frame 2
    -Animator moves bone to animations location/rotation (or in other words, back to where it was prior to the lerp.)
    -LateUpdate lerps bone to location/rotation.

    Following this logic you can see that the bone will never lerp but move back and forth just a fraction each frame. This will give it the appearance of not working even though if you measured it you'd be 1 frame into your lerp every update cycle.

    So again, Create an empty and change your lerps target to that new empty gameObject. Then move the lerp into Update. Then, in lateUpdate set the desired bone to the empty that you just lerped. Either that or in LateUpdate you need to keep a reference to where you were last lerp frame and reference it in the current lerp frame.

    That being said, you are a freakin hero my friend. Thanks for taking time to let us all know how you solved this. It's saving my projects life. I hope my experience builds upon what you've already shared and that it helps somebody else out there like you did for me.
     
    Last edited: Nov 5, 2019
    jellevda likes this.
  10. Vice69

    Vice69

    Joined:
    Mar 28, 2023
    Posts:
    8
    For anyone still wondering how to fix this simply:
    Make an empty gameObject and put the bone into the gameObject. Now rotate the gameobject instead of the bone :) !