Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Bug Animator didn't work right when frequent calls in just one update! Please help me

Discussion in 'Animation' started by UUUnity, Jun 16, 2022.

  1. UUUnity

    UUUnity

    Joined:
    May 6, 2018
    Posts:
    10
    Test environment unity 2019 and 2018
    I have tree animations { idle,run,walk }
    I am currently run animation. Press the F key to play the run, walk, idle and run animation in the same update
    The animator cannot play to the run animation finally
    You may ask why you need to call so many animations in the same update. The system I am working on is very complex. Some animations are triggered by the network, and some are triggered by user input,
    Is there a way for the animator to correctly play the animation of the last call
     

    Attached Files:

  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,533
    Could you post the code please? In case there are issues with it.
    Currently my gut feeling tells me, since Animator is a statemachine, that usually only one transition per statemachine update is allowed. Can‘t say if it‘s the case for Animator but that‘s how all other statemachines I worked with function. Mainly to prevent endless loops locking the main thread.
     
  3. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,533
    The simple workaround would be to register all requested animator updates in Update, and in LateUpdate you play the one that was requested last.
     
  4. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,554
    Yep, that's how Animator Controllers work because they were designed stupidly. Unlike @SteffenItterheim, I've never seen another state machine system with a restriction like that. It should be trivial for the system to simply replace whatever the first Play/CrossFade call does with the second one. I'm not sure how an endless loop could be involved.

    Keeping track of which animation you want to play then only telling the last one to play is the right approach, but playing the animation in LateUpdate isn't a good solution because it happens after the animation update so you'd always be delaying everything by one frame. Instead, you should make a script with a
    [DefaultExecutionOrder(10000)]
    attribute so that it executes after everything else. Then you can just use its Update method which will be the last thing to execute before the animation update.

    Alternatively, you could stop using Animator Controllers and find a better animation system without such silly limitations.
     
  5. UUUnity

    UUUnity

    Joined:
    May 6, 2018
    Posts:
    10
    Code (CSharp):
    1. if (GUILayout.Button("Play"))
    2.         {
    3.             string[] ss = { "run", "idle", "walk", "run" };
    4.             for (int i = 0; i < ss.Length; i++)
    5.             {
    6.                 m_animator.CrossFade(ss[i], 0.1f);
    7.             }
    8.         }
    i play animation by code in most time,not by a transition line ,so the prolem occured when complex sistuation
     
  6. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,533
    Quite simple, although I am talking about Statemachines that contain logic, not just "dumb" states as in the Animator (which perhaps works this way to avoid additional overhead when calling 10 animator updates per frame rather than the one you want).

    Example deadlock:
    • State A: Goto State B
    • State B: Goto State A
    If you do not wait 1 frame after each state change any script engineer can (and will) easily deadlock the app by doing something (usually much less obvious) than the above. By waiting you at least guarantee safe execution without deadlocking.
     
  7. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,554
    Why would you need 10 updates per frame?

    This is how it already works:
    1. User calls CrossFade("Idle", 0.1f); -> Animator does nothing yet and stores "Idle" and 0.1f somewhere.
    2. Animation update -> Animator starts transitioning to the stored "Idle" state.
    So why couldn't it just do this?
    1. User calls CrossFade("Idle", 0.1f); -> Animator does nothing yet and stores "Idle" and 0.1f somewhere.
    2. User calls CrossFade("Walk", 0.2f); -> Animator does nothing yet and replaces the stored "Idle" with "Walk" and 0.1f with 0.2f.
    3. Animation update -> Animator starts transitioning to the stored "Walk" state.
    As for logical state machines, the only time I've ever wanted a state entry function to immediately start another state was when implementing a special state with the sole purpose of going through a list of other states to enter the first one that is allowed. Could you make an infinite loop there? Sure. But I wouldn't want to add delays all throughout my logic for such an easily detectable and fixable problem. Allowing a character to get into a situation where they're alternating between two states every frame is still probably a bug that needs fixing and it's not as if implementing a delay is free, it would make your state machine notably more complex and create even more opportunities for other bugs.
     
  8. UUUnity

    UUUnity

    Joined:
    May 6, 2018
    Posts:
    10
    Thank you for your proposal, brother. Your suggestion is very valuable. I will consider it carefully. It's just that using delay queue really makes me uncomfortable at such a basic function
     
  9. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,533
    Sure but when you work in a company with 10-100 (or more) developers you wouldn‘t want to give anyone the ability to accidentally and unknowingly deadlock the game and then leave for the day (or vacation).

    Same for when you develop on console where it‘s costly to make a build only to find that it deadlocks when that deadlock could be easily prevented. Also on consoles you do want to limit scripting time per frame and adding a 1-frame (or more!) delay between state changes is a quick and easy way to lower CPU usage. It rarely matters, for example whether the NPC starts talking or moving this or the next frame is not an issue.

    Sorry for offtopic … ;)
     
  10. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,554
    You sure wouldn't, but:
    • Even if such a company somehow wasn't using version control, each other developer would still have their own local copy.
    • Preventing this one unlikely deadlock requires your state machine to be more complex than it would otherwise need to be, thereby introducing many other potential bugs. It's not just "we avoid a potential bug and no one will notice the gameplay difference", it's "we avoid a potential bug and now there are plenty of new potential bugs that are much more common instead".
    I gave one example of where I've seen a state need to immediately start another state and the chances of seeing a deadlock there are negligible. Do you have a more common situation you're thinking of?
    For NPC talking, you won't notice.

    For player logic in a fast paced fighting game, people will definitely notice.

    But that's still kind of beside the point. To see an actual performance problem that could be solved by a one frame delay, you would need:
    1. Multiple state changes in one frame. OP mentioned networking and user input, but that's still only two (and likely isn't relevant to NPC dialogue).
    2. Those states have to each be doing something notably expensive in their entry/exit functions.
    3. The performance benefit of smoothing out the bump has to outweigh the performance cost of implementing the delay for every state change. Starting a coroutine or whatever other approach you use to implement the delay will always add to the overall cost.
    Splitting expensive operations over multiple frames when the need arises is good.

    Splitting simple operations over multiple frames all the time just because maybe they might potentially cause a problem if the stars align sounds like a sign that the state machine system should be replaced.
     
  11. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,533
    @Kybernetic Trust me, I‘ve worked for those companies. ;) Try finding the commit that causes a deadlock from around 100 and even so, perhaps you‘ve just merged your feature branch and there‘s no simple way of going back to the last working state. And scripters aka level designers may not have the programmer‘s attitude and aptitude to consider such cases (especially where you have the copy-paste scripter who tends to miss changing all variable names).

    There‘s reasons and use-cases for both frame-stepped and immediate state changing SMs. Frame-stepping is trivial to implement, you simply exit processing the SM when a state changes and continue in the next update cycle. This enables you to easily tweak performance of the system even on a per map or per actor basis, and we‘ve had to do so in very complex maps. Even interleaving SM updates, Team A scripts update every odd frame, Team B scripts every even frame, something like this becomes trivial and relatively safe to do when the entire system does not rely on instant execution. It can also prevent CPU spikes, ie you may have situations where something triggers a sequence of going from State A all the way through State Z in one frame and each State has lots of conditions to check.

    In very rare cases the one-frame delay caused, well, a delay that was undesirable. But that was rare since everyone understood the implications of frame-stepping, and such particular cases can be solved by providing the proper condition/action the designer needs implemented in code, rather than replicating it in the SM (or more commonly, a better SM architecture to do the same thing more efficiently). Again, designers usually had little to no programming background.

    But I‘ll stop there this is going far offtopic now. ;)