- Home /
Variable set twice on the same Update call - Second time doesn't update?
Hey all,
I have a FSM for my movement that keeps track of the current state of the FSM. The state of the FSM gets updated twice in one Update call, but for some reason the value is only set to the first time it gets changed, not the last time. Here is the essence of the code:
There is a state for Dashing. The state buffers inputs on enter, changes states to the "Walking" state on Update(), and sends the buffered inputs on exit.
public CSMoveDash
{
public Enter()
{
Input.BufferInputs();
}
void Update()
{
if (Some condition)
{
FSM.ChangeState("Walk");
}
}
public Exit()
{
Input.SendBufferedInputs();
}
}
When a state changes (E.g. from Dash to Walk), the FSM object keeps track of what the current state is.
public class FSM
{
public State CurrentState;
public void ChangeState(State newState)
{
if (CurrentState != null)
{
CurrentState.Exit();
}
CurrentState = newState;
CurrentState.Enter();
}
public void Update()
{
CurrentState.Update();
Debug.Log(CurrentState); // For debugging only
}
}
Now, when the dash state is exited, this sends the most recent buffered input out to whatever script is listening to inputs. One of these scripts is the attack script, which interrupts the movement by putting the FSM into the "interrupted" state.
public class Attack
{
AttackInputPressed()
{
FSM.ChangeState(InterruptedState);
FSM.CurrentState.Update(); // For debugging only
}
}
Now the issue: I have an attack input buffered. When the dash finishes, it transitions to the walk state and sets the current state to the walk state. And then the exit function is called, sending my buffered attack input to the Attack class (This is done with custom delegate classes). The attack class changes the FSM to the "Interrupt" state. The next line when I call FSM.CurrentState.Update() is only there for debugging, but it properly prints out the "Interrupted state" as the current state. However, on the next Update() call from Unity (And subsequent ones), it still shows the current state of the FSM as being the "Walk" state.
Any idea what the issue is? I've combed through the scripts for hours and there's nothing else affecting this particular system. It also seems like Unity uses a single thread so I don't think race conditions would be the issue here. Especially since the entire flow is working line-by-line rather than coroutines / async. Is there something with Unity and how it handles object referencing / etc that I don't know about that would cause this issue?
Answer by QuariYune · Aug 22, 2021 at 05:31 PM
Hey all, actually I found the issue that was causing this weird behaviour. On the ChangeState method of the FSM, it first runs the Exit function of the current state, then changes to the new state, and then runs the Enter method of the new state. What this means is that Dash was setting the state to Walk, Dash's exit method was setting the state to Interrupted, then the ChangeState method set the CurrentState back to Walk.
This is the fix:
public void ChangeState(State newState)
{
State oldState = CurrentState;
CurrentState = newState;
if (oldState != null)
{
oldState.Exit();
}
CurrentState.Enter();
}
Answer by Bunny83 · Aug 22, 2021 at 11:38 AM
Well, there's something missing in the code you've posted since your class is called FSM and your CurrentState as well as your ChangeState method are instance members of that class. So it's not clear where this instance is managed and if you have more than one instance of that FSM class. I guess you do which is most likely the issue here. Though you seem to have "simplified" some of your code which of course doesn't help in figuring out where your code went wrong. So we don't know what classes may be MonoBehaviours or not. At least most of your public and private modifiers seems to be kinda messed up. So since the code you've posted is just pseudo code we don't know how much that matches with your actual code.
Anyways every log in the console comes with a clear stacktrace. If your FSM and your state classes are all normal instances, it gets tricky to track down multiple instances. Though you could give each instance a name and / or check the constructor of those classes when and where they are created.
If the FSM class is an actual MonoBehaviour you can actually pass it along with your debug.log statements as second parameter. This allows you to click on the log message in the console and Unity will "ping" / highlight that object in your project. This often helps to identify which objects you're actually dealing with.
The FSM and every other class here is just a single instance in this case, but I found the issue. It was due to the order of operation of the ChangeState method. The method would Exit a state before changing the current state, and while logical this means that any state changes done in exit aren't saved. I made the fix in another comment.