Search Unity

  1. New Unity Live Help updates. Check them out here!

    Dismiss Notice

debugging animation transitions (especially interruption failures)

Discussion in 'Animation' started by s_guy, Aug 8, 2017.

  1. s_guy

    s_guy

    Joined:
    Feb 27, 2013
    Posts:
    102
    Debugging complex Animators can be quite difficult. I was brought out of the dark ages with the Unity blog post about how the transition settings actually worked. Thank you Team Unity so much for that! Still, it sometimes takes me hours to find a problem, often through trial and error, that could be much easier to debug.

    The Animator view is nice, but it can be hard to see frame-by-frame issues at the moment of transition, or when an expected transition didn't happen. What would be super handy is a way to debug log transition events for an animator or animator state.

    Helpful events to log:
    1. a transition has its parameter conditions met (which is easy to see in the Animator view), but is not allowed to start yet and why it cannot start (which is sometimes very hard to see in the Animator view)
      1. Another animation has an Exit Time blocking
      2. Another animation transition is running and doesn't allow interruptions
      3. Another animation transition was met on the same frame and has higher priority
      4. Other?
    2. a transition has begun and which transition it interrupted, if any
    3. a transition has ended

    This would also help find issues of hidden problems baked into Animator Controllers, such as those caused by previous editor bugs that corrupted data. All known such issues have been fixed, but some have "delete and recreate the state / transition" as a workaround for preexisting assets. What if someone didn't happen to hear about a given bug or my controller is far too complex to re-create from scratch as a panacea?

    Challenges with creating a script to debug log the above events:
    • I don't see how to get transition names from code in an elegant way; we only get hashes and it looks like there isn't a way to look up a name from a hash.
    • StateMachineBehaviour::OnStateEnter and Exit don't give us all of the transition events and where they are similar, do they even match on a frame-by-frame basis? I guess we'd have to poll GetAnimatorTransitionInfo?
    • Various state info about transitions don't seem to be exposed in the API.
    Also, am I missing anything from item 1.4? I know there have been some cases where I changed a setting that unexpectedly allowed a transition to interrupt, e.g. a transition from Any State that wouldn't interrupt when I wanted it to, but when I turned off its own Exit Time it was allowed to interrupt. What!?

    Please offer your thoughts on the above, let me know where I'm missing something or I'm just wrong, and please link helpful ideas, threads, scripts, etc. for debugging animation transitions.

    BTW, I'm in Unity 5.6

    Thanks!
     
    Last edited: Aug 10, 2017
  2. NoiseFloorDev

    NoiseFloorDev

    Joined:
    May 13, 2017
    Posts:
    104
    It would be really helpful for StateMachineBehaviour instances to have transition started/updated/finished methods. It's one of the glaring missing bits in that interface. You can try to derive this by calling animator.GetAnimatorTransitionInfo, but then you're depending on calling it during the transition--if it's a very short or instant transition you can miss it, and I haven't looked at how it behaves with interruptions. Also, AnimatorTransitionInfo.fullPathHash is supposed to be unique, but it isn't (filed a bug for this), so you have to give every transition an explicit name and compare the userNameHash to unambiguously match them.

    Semi-related, but another missing bit is a method for events firing on clips inside the state machine. The current "call a named function somewhere on the Animator" event interface is ancient--there's no way to tell which state/state engine an event came from and you should be able to handle events at the state machine level.

    There's no way to get a name from a transition (or state, or state engine) from runtime code. You can get it by traversing the graph with the editor APIs. Just yesterday I wrote some code to do this, write it out to JSON, and then read the JSON at runtime so I can look up transition, etc. names. I don't know why I needed to do that--why isn't there a function to retrieve the name of these objects? It's really hard to debug anything when all you're told is -59182391.

    I don't have a standalone version of that code (it's just a helper in my project), but I pulled it out here: https://s3.amazonaws.com/noisefloordev/SerializedAnimator.zip It'll need modification for other projects, but it just adds "Edit -> Update Animator state engine list" which outputs a file in Resources, and then you can call SerializedAnimator.GetStateName and GetStateMachineName to get the name from a hash. This needs Json.Net.Unity3D since Unity's JsonUtility class is broken. I recommend calling SerializedAnimator.GetControllerSet once at startup to preload it, since the first call into that JSON class is expensive (almost 80ms for me, at least in the editor). This doesn't currently serialize transition names since it's not very useful without transition callbacks.

    Huh, what? Surely the solution to "our documentation is poor" is "write documentation", not "write a blog post". How the heck is anyone supposed to find that?
     
    TreeMSI likes this.
  3. s_guy

    s_guy

    Joined:
    Feb 27, 2013
    Posts:
    102
    Thanks for sharing your animation transition tools! I didn't think to go about it this way. These should help a ton. I had to smirk at some of the code comments.

    Anybody else have any tips or tricks for logging / debugging animation transitions? Maybe one of the Unity folk could offer some advice?

    On documentation: To be fair, they have improved their documentation on animation transitions substantially since I first started trying to make sense of them. It'd be good for them to link their blog post on it in the manual too, but they don't seem to like doing that--perhaps because blogs aren't always maintained as the features evolve.
     
  4. s_guy

    s_guy

    Joined:
    Feb 27, 2013
    Posts:
    102
    Wow, if this is in 5.6, it could be the source of some of my problems. Some of my animators have quite a lot of transitions, which would seem to increase the risk of a hash collision. I've never bothered with custom transition names before.
     
  5. NoiseFloorDev

    NoiseFloorDev

    Joined:
    May 13, 2017
    Posts:
    104
    It only matters if you have more than one transition from a source to a target state (eg. multiple transition points between two states), since the full path looks like "Layer.StateEngine.State.Source -> Dest", but there's nothing to make it unique if there are more than one transition from Source to Dest. They don't have to be unique between separate states. Thankfully layer, state and state machine names are forced unique, just transition names aren't.

    I'll probably write an editor script to make the "user name" of transitions unique, by adding a suffix if they're duplicated and generating a name if necessary (the default is blank). That way I can match userNameHash instead of fullPathHash.
     
  6. NoiseFloorDev

    NoiseFloorDev

    Joined:
    May 13, 2017
    Posts:
    104
    The biggest problem I've seen is that StateMachineBehaviour.OnStateMachineEnter and OnStateMachineExit aren't called if you don't go through the Entry/Exit nodes. It's documented, but it doesn't make sense: it means state engine behaviors just don't work correctly if you ever create a transition into the middle of a state engine. You should be able to depend on OnStateMachineEnter always being called when entering a state, and always being matched up with OnStateMachineExit.

    I don't know any workaround for this, other than having a script to throw up warnings if any transitions ever bypass the entry/exit node, and it makes testing hard because you can't Play() to jump to a specific state without breaking behaviors.

    Maybe I can bypass it by having a script monitor the animator state, and fire my own callbacks.
     
  7. s_guy

    s_guy

    Joined:
    Feb 27, 2013
    Posts:
    102
    Ah, got it, thanks for the details.

    That's what I was thinking too, but wanted to check the community wisdom on the topic first, to see if I was missing something.
     
  8. NoiseFloorDev

    NoiseFloorDev

    Joined:
    May 13, 2017
    Posts:
    104
    Just to close the loop on this one, I finally found the solution to this: you can make the parameter to an event take an AnimationEvent object, and you'll get an object with a lot more info about where the event came from. This is documented, but as far as I can tell only by half a sentence in one place (buried in a UI guide) and nowhere that I can see in the API reference. I just chanced upon it while poking around the C# export classes.

    (I guess this is a consequence of them using a freeform reflection-based API--"just enter a function name"--instead of a proper class interface where we'd see the other possible overloads right away.)
     
  9. s_guy

    s_guy

    Joined:
    Feb 27, 2013
    Posts:
    102
    Oh!? That's handy.

    How did you browse that?
     
  10. NoiseFloorDev

    NoiseFloorDev

    Joined:
    May 13, 2017
    Posts:
    104
    In MSVC you can Go To Definition on a symbol, and for Unity's exports it'll show you what's exported on it. I saw animatorClipInfo and animatorStateInfo on AnimationEvent (which I'd only previously used to create events in postprocessors), which I realized would only have any meaning if you could get them while an event was being sent, so I went searching from there.

    (What I was actually doing was poking through the same class in a disassembled copy of UnityEngine. There's an old copy here and I made my own for the current version using the same tool. It's the most important thing I've found so far for figuring out Unity APIs in detail, especially editor APIs since you can poke through most of the UI implementations.)
     
  11. s_guy

    s_guy

    Joined:
    Feb 27, 2013
    Posts:
    102
    I use this a lot, but I didn't see any leads in the derived metadata derived from dlls. I thought that I wouldn't be able to see how the black box was calling my custom function names.

    Oh, wow. Helpful find!
     
unityunity