Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unity3D: When are transition conditions evaluated

I have a very simple game running in Unity, and unfortunately, it is not working as expected.

The Context

I have the following setup:


(source: mazyod.com)

From any state, if an integer parameter direction is equal to 1, set animation player_move_e. Same goes for 2, 3, 4 for the other directions. Once direction is set to 0, the idle animation associated with the direction is played.

An important thing to point here is, since I am using the "Any State" block, I must make sure that once the animation state changes to one of the move animation states, I reset direction to sentinel value, like -1, that keeps the animation state in the current state, and not change states to itself again.

The Problem

When the player is in the, say, move_n state, and I quickly change the direction to the, say, move_e (not giving it enough time to transition to idle), the animation state is stuck at the move_n :(

How can I know if the transition happened, and I am not updating too often? Initially, I was updating direction in FixedUpdate, and then moved it to Update, but it's the same issue in both methods.

Here is the logic I am seeing:

// MY CODE
animator.SetInteger(1); // Initially, it's move_e
...
// WITHIN UNITY (making it up)
Unity.UpdateStates(); // The number is read in the engine, and transition happens
...
// MY CODE
animator.SetInteger(-1); // Stay at current animation
...
// WITHIN UNITY (making it up)
Unity.UpdateStates(); // The number is read in the engine, nothing should happen
...
// MY CODE
animator.SetInteger(0); // The logic briefly changes to idle
...
// WITHIN UNITY (making it up)
Unity.UpdateStates(); // The number is read in the engine, transition to idle
...
// MY CODE
animator.SetInteger(4); // transition to north
...
// WITHIN UNITY (making it up)
// NOTHING HAPPENS!! It doesn't catch up
...
// MY CODE
animator.SetInteger(-1); // Stay at current state, assuming it changed to move_n
...
// WITHIN UNITY (making it up)
Unity.UpdateStates(); // The number is read in the engine, stays at idle_e!
...

UPDATE:

Here is the whole code:

void Update()
{
    // update animator (Stupid transition from any state includes current state -_-")
    int heading = CalculateHeading(horizontalInput, verticalInput);

    if (heading != previousHeading)
    {
        anim.SetInteger("direction", heading);
        previousHeading = heading;
    }
    else
    {
        anim.SetInteger("direction", -heading);
    }
}

void MovementManagement ()
{
    // If there is some axis input...
    if(horizontalInput != 0f || verticalInput != 0f)
    {
        Vector3 position = transform.position;
        position.x += horizontalInput * movementSpeed;
        position.y += verticalInput * movementSpeed;

        transform.position = position;
    }
}

int CalculateHeading(float horizontal, float vertical)
{
    if (horizontal == 0f && vertical == 0f)
    {
        return 0;
    }

    if (Mathf.Abs(horizontal) > Mathf.Abs(vertical))
    {
        return (horizontal > 0 ? 1 : 3);
    }
    else
    {
        return (vertical > 0 ? 4 : 2);
    }
}
like image 944
Mazyod Avatar asked Nov 02 '22 01:11

Mazyod


1 Answers

I built a small scene for myself and played around a little bit. I could reproduce your problem and even found a simple solution.

Why it happens

The problem seems to occur when the animator state machine is still in transition to a new state. During that time, new parameter values are ignored but your code is going to change that value again in the next update() call. The state machine ignored your new direction and now only sees a value for which it has no transition available, direction = -1 for example.

The default behavior for transitions is atomic which means according to the reference that it cannot be interrupted. However, setting it to false made no difference... It could be a bug and you may want to file a bug report.

Solution

You need to check if the state machine is currently in a transition by checking IsInTransition(int layer) like this:

void Update()
{
    // update animator (Stupid transition from any state includes current state -_-")
    int heading = CalculateHeading(horizontalInput, verticalInput);

    if (heading != previousHeading && !anim.IsInTransition(0))  // you may need to adjust the layer index
    {
        anim.SetInteger("direction", heading);
        previousHeading = heading;
    }
    else
    {
        anim.SetInteger("direction", -heading);
    }
}

Another possible solution

I don't know what kind of animations you are using for the different directions, but it may be easier to use one animation and just turn your character around :) This way you have less transitions and states and it may be easier to add more animations later on.

like image 75
Stefan Hoffmann Avatar answered Dec 20 '22 16:12

Stefan Hoffmann