Abstract State Machine - How to make function run once when switching states

I have a state machine that works for the most part. Problem is each state needs a function to run only once, only when the state is first switched to.

So for example, when the idle state switches to the move state, I want to run a function in the move state but only once.

My absolute bare bones code:

State:

public abstract class State : MonoBehaviour {

    public abstract State RunCurrentState();

}

State Manager:

// I used this tutorial: https://www.youtube.com/watch?v=cnpJtheBLLY&ab_channel=SebastianGravesSebastianGraves

public class StateManager : MonoBehaviour {

    public State currentState;

    void Update() {
        RunStateMachine();
    }

    private void RunStateMachine() {

        State nextState = currentState?.RunCurrentState();

        if (nextState != null) {

            SwitchToTheNextState(nextState);

        }

    }

    private void SwitchToTheNextState(State nextState) {

        currentState = nextState;

    }

}

Move State:

public class MoveState : State {

    public override State RunCurrentState() {
        return this;
    }

}

Hierachy/Inspector:

7204162--865234--States.png

So in the MoveState public override State RunCurrentState() is run every frame.

If I put a start method in the MoveState it is run when the prefab first loads, I actually want to run some code when the State is first switched to.

Also I’m still a beginner, I’m sure there are far better ways of implementing this with ScriptableObjects or something but for now if someone could just help me with this issue that would be fantastic!

Many Thanks!

Add another abstract method to State and implement it in the state classes:

public abstract class State : MonoBehaviour {
    public abstract void OnStart();
    public abstract State RunCurrentState();
}
    private void SwitchToTheNextState(State nextState) {
        if (nextState != currentState) {
            currentState = nextState;
            nextState.OnStart();
        }
    }
public class MoveState : State {
    public override void OnStart() {
      // this will run once.
    }
 
    public override State RunCurrentState() {
        return this;
    }
}
2 Likes

One way is to provide other methods in your base State object that can be overridden by the specific state:

void OnStateExit();
void OnStateEnter();

That said, I have long held the position that completely-generic state machines are not a good idea, in that they impose an awkwardness across every part of your code that uses them, and have a higher debugging cost.

Personally I just make an enum of states and some methods with names that EXPLICITLY capture what I’m doing at each stage for a particular part of your game.

I accept that this is a minority view.

2 Likes

Thank you so much both of you. Works perfectly.

So much easier having someone point you in the right direction when you are not sure what is actually possible.

Appreciate it cheers guys.

1 Like