Mecanim trigger stays down (queued?)

Hello. I’ve been fighting around this problem for a long time, but now that unity5 is out I was hoping there was an option to change this.

Whenever I call setTrigger(), and the state-machine is not in a state where it would cause a transition, it causes the trigger to stay true.

The red square represents the input to start the drill state.

Whenever I’m in the idle state, the transition works fine. But when running or jumping and calling setTrigger(“tryDig”) just once, it stays locked until I enter the idle state. Which then goes into the digging state because there’s a transition from idle->digging when the trigger is true.

It seems like the trigger is queued, which is not something I want. I could fix this by using booleans, but it feels incredibly ugly to enable and immediatly disable a boolean just to trigger a transition.

Thanks!

This is by design, a trigger is reset to false only once it has been consumed by a transition.

1 Like

I should’ve taken a better look at the docs, thanks for the response!

Is there a way to change this behaviour? I want to have triggers that are guaranteed to only be true for one frame. I just spent two weeks designing a system around that assumption.

It’s pretty easy to make the extension method. Assuming you have some sort of static coroutine system:

//usage:
myAnimator.SetTriggerOneFrame("Trigger");

//extension methods to allow that usage:
public static class AnimatorExtension {

    public static void SetTriggerOneFrame(this Animator anim, string trigger) {
        StaticCoroutine.Start(TriggerOneFrame(anim, trigger));
    }

    private static IEnumerator TriggerOneFrame(Animator anim, string trigger) {
        anim.SetTrigger(trigger);
        yield return null;
        if (anim != null) {
            anim.ResetTrigger(trigger);
        }
    }
}

If you don’t have a static coroutine system, you can send the MonoBehaviour as a parameter to the method, and run the coroutine on that:

//usage:
myAnimator.SetTriggerOneFrame("Trigger", this);

//extension methods to allow that usage:
public static class AnimatorExtension {

    public static void SetTriggerOneFrame(this Animator anim, MonoBehaviour coroutineRunner, string trigger) {
        coroutineRunner.StartCoroutine(TriggerOneFrame(anim, trigger));
    }

    private static IEnumerator TriggerOneFrame(Animator anim, string trigger) {
        anim.SetTrigger(trigger);
        yield return null;
        if (anim != null) {
            anim.ResetTrigger(trigger);
        }
    }
}

Since the animator API is so sparse, extension methods can help make it a lot more readable. Here’s an example:

//what you want:
if(myAnimator.IsInState("SomeState"))

//The horribly cumbersome API you're stuck with:
if(myAnimator.GetCurrentAnimatorStateInfo(0).IsName("SomeState"))

//extension method that allows the first version:
public static bool IsInState(this Animator animator, string stateName, int layer = 0) {
    return animator.GetCurrentAnimatorStateInfo(layer).IsName(stateName);
}
3 Likes

Just be aware that GetCurrentAnimatorStateInfo is not a free call, if you only need to check the name that ok but if you need to check a few setting and each time you call GetCurrentAnimatorStateInfo you may get a performance hit

Checking what state the animator is in is really important for a lot of applications, and it should be possible to do it for (almost) free. Implementing that would be really easy - just store the hash of the current state on the animator, and expose an IsInState method that checks against that hash.

The #1 question people ask about with regards to the animator is “how do I check which animation is playing”. Since neither OnEnter nor OnExit gives accurate information about what state you’re in (and require a lot of boilerplate work), you should really just create Animator.IsInState.

I do not agree, on each statemachine behaviour callback you get the AnimatorStateInfo for the current state
http://docs.unity3d.com/ScriptReference/StateMachineBehaviour.OnStateEnter.html
http://docs.unity3d.com/ScriptReference/StateMachineBehaviour.OnStateExit.html

Why this information is not reliable?

1 Like

A bit poor wording on my side. They give accurate information about “some state was entered”, but can’t give information about what state the system is in.

Say you need to check what state the animator is in, and need it to be fast, so you can’t use GetCurrentAnimatorStateInfo. It’s possible to work around it by putting something like this on all of the states:

public class ReportState : StateMachineBehaviour {

    [SerializeField] private string name;
    private SomeBehaviour behaviour; //Some behaviour that needs to know the current state

    public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
        if (behaviour == null) {
            behaviour = animator.GetComponent<SomeBehaviour>();
        }
        behaviour.ReportAnimatorInState(name);
    }
}

That works, for sure. It’s unreliable because you have to remember to add it to every new state. So if you add a new state, and forget to add the behaviour, the query “what state am I in” will stop working.

The point I was trying to make is that the StateMachineBehaviour system does not give a good way to check “what state is the animator in”.

1 Like

yes i agree, using a SMB to do this imay not be the right tool.

This is not true, you can add a SMB on a statemachine and all state and substatemachine in this statemachine will inherit from this SMB. The important twist is to add the attribut [SharedBetweenAnimators] on this behaviour to avoid having one instance per state, take a look at this package, the controller have a layer with a 3 level deep statemachine, on the top most statemachine there is one SMB that does all the work

2779948–201112–sharedsmb.unitypackage (8.66 KB)

1 Like

I also fell into this “trap”:

For each input, I set some trigger, which even if it is “blocked” by a currently playing animation with no exit time, or in a state which doesn’t care about that particular trigger, gets stored and triggers itself when it feels it’s its turn.

If I want the trigger to immediately expire if it’s not able to trigger in the current “update”, is the above frame example still the way to do?

I guess I perhaps shouldn’t use triggers, but booleans, and only play an animation state when that particular boolean is true and the immediately exit the state once it turns false…

Weird that this is the only thread on this topic, so apparently it gives all the answers. I just have to figure them out, I guess. :slight_smile:

1 Like

Agreed. This is confusing for anyone not extremely experienced with Mecanim.
I have used only Triggers and they bare causing me headaches in that the seem to be called twice.

If setting the trigger in some kind of update, i’d just reset the trigger at the start of that update function