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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Mecanim as generic state machine

Discussion in 'Animation' started by mholub, Mar 16, 2015.

  1. mholub

    mholub

    Joined:
    Oct 3, 2012
    Posts:
    123
    I am trying to use Animator as completely generic state machine.
    So all my states don't have Motion and are not related to animation at all.

    I've never used Mecanim before, read a lot of information about it past several days.
    I plan to use Motion-less states with StateMachineBehaviours attached and triggers to transition between states.

    I think I can live with all that stuff related to animations Mecanim shows to me.

    First problem I can't solve yet is how to to make instant transition.

    For example - I want to fire a trigger, which transitions my state machine from Idle state to Fire state, where Fire state has Behaviour, which will call some code (instantly) and instantly transition from Fire state back to Idle.



    On the screenshot attached I made both transitions from Idle to Fire and from Fire to Idle zero duration.
    It looks like even without Motion state has implicit duration of 1 second which can't be changed from UI.
    So Idle -> Fire -> Idle takes 1 second and no less. Is it a bug?

    I assume I can write some editor scripting code which could change state duration. So I can still solve my problem. It feels weird to do so :)

    Is Mecanim intended to be used only as animation state machine?
     
  2. Mecanim-Dev

    Mecanim-Dev

    Unity Technologies

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    This is by design to normalized all the transition time for exactly your case.

    I can't see your screenshot but you are simply missing a small step I think, make sure that Has exit time is not checked on all your transition. Exit time is used to define when exactly a transition can occur while in your state, in your case you would like to transition at any point in your state.
     
    Alic and theANMATOR2b like this.
  3. mholub

    mholub

    Joined:
    Oct 3, 2012
    Posts:
    123
    I put small project here.
    https://www.dropbox.com/sh/c8m10pbakrmt723/AACdbuvH_LP3dHwwD7lRx7ifa?dl=0

    I checked both transitions: Idle->Fire and Fire->Idle, Exit time is off, Transition duration is 0.
    Now when I set trigger, my state machines successfully transitions from Idle to Fire and it never comes back.

    I Debug.Log'ged its behaviour and it outputs normalized time greater than 1, which doesn't make sense at least for me.

    Code (csharp):
    1.  
    2.    //OnStateUpdateiscalledoneachUpdateframebetweenOnStateEnterandOnStateExitcallbacks
    3. override public void OnStateUpdate(Animatoranimator, AnimatorStateInfostateInfo, intlayerIndex) {
    4.   Debug.LogFormat("Fire update | normalized time = {0}", stateInfo.normalizedTime);
    5. }
    6.  
    I get output:
    Looks like a bug, doesn't it?
     
  4. Mecanim-Dev

    Mecanim-Dev

    Unity Technologies

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    This is expected for looping clip, the interger part is the number of loop the state actually did, you can specify an exit time like 4.0 which mean you can exit this state after 4 full loop.

    So now it seem like you can do what you want?
     
  5. mholub

    mholub

    Joined:
    Oct 3, 2012
    Posts:
    123
    I specified 0. And my state doesn't have Motion at all.

    My assumption is I can exit the state after 0 iterations where each iteration takes 0 seconds (because it doesn't have animation at all). It does never exit.

    Where is my assumption wrong?
     
  6. Mecanim-Dev

    Mecanim-Dev

    Unity Technologies

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    If you have no motion your state will have a duration of 1 second but that doesn't really matter since you can transition when you want.
    Like I did explain previously the reason why empty state have a duration of 1 sec is to have normalized time baseline for all transitions going out of this state.

    In you case you shouldn't specify an exit time, you want to transition at any time.
     
  7. mholub

    mholub

    Joined:
    Oct 3, 2012
    Posts:
    123
    1. Can you explain in different words or give me the link to documentation where I can understand that?
    2. I experimented a bit. When my back transition has condition, it works. When it doesn't, it never cames back. Unconditioned transition should work the same as condition which always true, right?

    I updated project https://www.dropbox.com/sh/c8m10pbakrmt723/AACdbuvH_LP3dHwwD7lRx7ifa?dl=0
    It contains two state machines, working and buggy one. Can you check it? It is very small and contain only isolated test case.
    Should I file bugreport?
     
  8. Mecanim-Dev

    Mecanim-Dev

    Unity Technologies

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    Transition time(exit time and duration) is based on the source state, so if you source state has a lenght of 4 second and your exit time is 0.90 and the duration is 0.10. You will exit this state at 4 * 0.9 = 3.6 second and the transition duration will be 4 * 0.1 = 0.4 second.

    So for state without motion the duration is 1 second which allow you to work in second directly rather than in normalized time.

    A transition without any condition and exit time does nothing, this is by design. Otherwise we would need some special workflow rule that would prevent a user to create a transition without any condition but in this case we would need to add a default condition like exit time = 1 and we do think that this is not a good idea in this case.
     
    theANMATOR2b likes this.
  9. mholub

    mholub

    Joined:
    Oct 3, 2012
    Posts:
    123
    Finally it makes sense for me. Thank you very much.

    I was confused because I did same unconditional transitions for character animation and it worked. It worked because I didn't turn off "Exit time".

    Maybe transition logic should be described a bit more verbose in manual. I honestly tried to understand it from manual but your answers were a lot more helpful in the end
     
    theANMATOR2b likes this.
  10. mholub

    mholub

    Joined:
    Oct 3, 2012
    Posts:
    123
    I've just realized that it should work if Has Exit Time is On and Exit Time = 0.
    It doesn't work.
    It works if Has Exit Time is On and Exit Time = 0.0001.

    Something is wrong about exact 0 value.
     
  11. Mecanim-Dev

    Mecanim-Dev

    Unity Technologies

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    Effectively Exit Time = 0 doesn't work but it the same as not setting any exit time so ....
     
  12. mholub

    mholub

    Joined:
    Oct 3, 2012
    Posts:
    123
    Not setting any exit time is "Has Exit Time" = false

    Exit Time = 0 should not have special meaning. It's confusing.
     
  13. mholub

    mholub

    Joined:
    Oct 3, 2012
    Posts:
    123
    Back to the original question:

    Mecanim.Dev, this is question mainly for you as developer of Mecanim so you have a vision of how Mecanim should be used.

    Is Mecanim suitable as completely generic finite state machine? Not bound to animations, time etc. Just plain states, data, events, transitions etc. Should I continue my experiments with it in this role?
     
  14. Mecanim-Dev

    Mecanim-Dev

    Unity Technologies

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    Yes, simply set all your transition Has exit time to false and duration to 0. We did a few test internally and we did manage to use it like that.
     
  15. Xelnath

    Xelnath

    Joined:
    Jan 31, 2015
    Posts:
    400
    I'm using it like this right now for a logic controller on an AI object.

    Works quite well for simple creatures. Here's a screenshot of the state machine and the code that parses it:
    mechanim_ai.png

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class StateNode : StateMachineBehaviour {
    5.  
    6.     public string StateName = "EditMe";
    7.  
    8.      // OnStateEnter is called when a transition starts and the state machine starts to evaluate this state
    9.     override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
    10.         var characterController = animator.gameObject.GetComponent<BaseUnit>();
    11.         if (characterController != null)
    12.             characterController.StateMachineEvent("Enter", StateName, animator, stateInfo, layerIndex);
    13.     }
    14.  
    15.     // OnStateUpdate is called on each Update frame between OnStateEnter and OnStateExit callbacks
    16.     override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
    17.         var characterController = animator.gameObject.GetComponent<BaseUnit>();
    18.         if (characterController != null)
    19.             characterController.StateMachineEvent("Update", StateName, animator, stateInfo, layerIndex);
    20.     }
    21.  
    22.     // OnStateExit is called when a transition ends and the state machine finishes evaluating this state
    23.     override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
    24.         var characterController = animator.gameObject.GetComponent<BaseUnit>();
    25.         if (characterController != null)
    26.             characterController.StateMachineEvent("Exit", StateName, animator, stateInfo, layerIndex);
    27.     }
    28.  
    29.     // OnStateMove is called right after Animator.OnAnimatorMove(). Code that processes and affects root motion should be implemented here
    30.     override public void OnStateMove(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
    31.         var characterController = animator.gameObject.GetComponent<BaseUnit>();
    32.         if (characterController != null)
    33.             characterController.StateMachineEvent("Move", StateName, animator, stateInfo, layerIndex);
    34.     }
    35. }
    36.  
    Code (CSharp):
    1.  
    2.     ///////////////////////////////////////////////////////////////////////////////////////////////////
    3.     // State Machine Driven Logic
    4.     ///////////////////////////////////////////////////////////////////////////////////////////////////
    5.     public override void StateMachineEvent(string smEvent, string stateName, Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    6.     {
    7.         Debug.Log(string.Format("{0} : {1}", smEvent, stateName));
    8.  
    9.         switch ( smEvent )
    10.         {
    11.             case "Enter":
    12.                 switch ( stateName )
    13.                 {
    14.                     case "Idle":
    15.                         Properties["AIState"] = AxeGunLizardState.Idle;
    16.                         break;
    17.                     case "Run":
    18.                         Properties["AIState"] = AxeGunLizardState.Approach;
    19.                         break;
    20.                     case "Aim":
    21.                         Properties["AIState"] = AxeGunLizardState.Aim;
    22.                         break;
    23.                     case "Axe Tell":
    24.                         Properties["AIState"] = AxeGunLizardState.Attack;
    25.                         break;
    26.                     case "Axe Swing":
    27.                         fLastAttack = Time.time;
    28.                         break;
    29.                     case "Fire":
    30.                         fLastShot = Time.time;
    31.                         break;
    32.                     case "Death":
    33.                         Properties["Dead"] = 1;
    34.                         break;
    35.                     case "Shield High":
    36.                         if ( fLastShieldStart == 0.0f ) fLastShieldStart = Time.time;
    37.                         break;
    38.                 }
    39.                 break;
    40.             case "Update":
    41.                 break;
    42.             case "Exit":
    43.                 switch ( stateName )
    44.                 {
    45.                     case "Shield High":
    46.                     case "Shield Low":
    47.                         if ( fLastShieldStart == 0.0f ) fLastShieldStart = Time.time;
    48.                         break;
    49.                 }
    50.                 break;
    51.         }
    52.     }
    53.  
     
    Stardog and theANMATOR2b like this.
  16. Shadowphax

    Shadowphax

    Joined:
    Jul 26, 2013
    Posts:
    8
    Is using Mecanim for a generic state machine a common practice? Or rather is it a necessarily good or bad practice? I'm thinking of using it for a spell caster system instead of my own FSM mainly for the UI aspect. So far I can't see why not.
     
  17. Salazar

    Salazar

    Joined:
    Sep 2, 2013
    Posts:
    235
    How about fps of target device when using animation states.
     
  18. gazihan

    gazihan

    Joined:
    Jan 27, 2017
    Posts:
    1
    Beware though: after you set a trigger, the transition does not happen instantly like a function call would. Instead, the function that set the trigger continues executing, and then the transition happens somewhere towards the end of the frame. Only then the exit/enter functions will be called. If you expect that setting the trigger calls the exit/enter functions like a function call would, you will be frustrated.

    I was often like "I just told that object to switch to this state, why is it still in the previous state???".

    This is the case when exit time is 0.