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?
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.
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.
//OnStateUpdateiscalledoneachUpdateframebetweenOnStateEnterandOnStateExitcallbacks
override public void OnStateUpdate(Animatoranimator, AnimatorStateInfostateInfo, intlayerIndex) {
Debug.LogFormat("Fire update | normalized time = {0}", stateInfo.normalizedTime);
}
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.
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.
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.
Can you explain in different words or give me the link to documentation where I can understand that?
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 Dropbox
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?
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.
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
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.
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?
using UnityEngine;
using System.Collections;
public class StateNode : StateMachineBehaviour {
public string StateName = "EditMe";
// OnStateEnter is called when a transition starts and the state machine starts to evaluate this state
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
var characterController = animator.gameObject.GetComponent<BaseUnit>();
if (characterController != null)
characterController.StateMachineEvent("Enter", StateName, animator, stateInfo, layerIndex);
}
// OnStateUpdate is called on each Update frame between OnStateEnter and OnStateExit callbacks
override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
var characterController = animator.gameObject.GetComponent<BaseUnit>();
if (characterController != null)
characterController.StateMachineEvent("Update", StateName, animator, stateInfo, layerIndex);
}
// OnStateExit is called when a transition ends and the state machine finishes evaluating this state
override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
var characterController = animator.gameObject.GetComponent<BaseUnit>();
if (characterController != null)
characterController.StateMachineEvent("Exit", StateName, animator, stateInfo, layerIndex);
}
// OnStateMove is called right after Animator.OnAnimatorMove(). Code that processes and affects root motion should be implemented here
override public void OnStateMove(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
var characterController = animator.gameObject.GetComponent<BaseUnit>();
if (characterController != null)
characterController.StateMachineEvent("Move", StateName, animator, stateInfo, layerIndex);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// State Machine Driven Logic
///////////////////////////////////////////////////////////////////////////////////////////////////
public override void StateMachineEvent(string smEvent, string stateName, Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
Debug.Log(string.Format("{0} : {1}", smEvent, stateName));
switch ( smEvent )
{
case "Enter":
switch ( stateName )
{
case "Idle":
Properties["AIState"] = AxeGunLizardState.Idle;
break;
case "Run":
Properties["AIState"] = AxeGunLizardState.Approach;
break;
case "Aim":
Properties["AIState"] = AxeGunLizardState.Aim;
break;
case "Axe Tell":
Properties["AIState"] = AxeGunLizardState.Attack;
break;
case "Axe Swing":
fLastAttack = Time.time;
break;
case "Fire":
fLastShot = Time.time;
break;
case "Death":
Properties["Dead"] = 1;
break;
case "Shield High":
if ( fLastShieldStart == 0.0f ) fLastShieldStart = Time.time;
break;
}
break;
case "Update":
break;
case "Exit":
switch ( stateName )
{
case "Shield High":
case "Shield Low":
if ( fLastShieldStart == 0.0f ) fLastShieldStart = Time.time;
break;
}
break;
}
}
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.
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???”.