Questions regarding alternative approach of setting up states

Hi GameDevTV Team,

  1. If I needed to call Unity Events from individual classes then how would I go about doing that? (e.g: OnAttack event from attack state) Since each state is a pure C# class, there’s no way to expose events for each state in the inspector.

  2. The way the states are set, if I were to make a character that could walk but not run or a character that couldn’t jump, then I would need to create a new state machine, base state, and respective states that don’t include transitions to the jump state. I think that’s because the transitions are defined in the states themselves. This reduces modularity. How can we tackle this?

Thank you

You are correct, the way the state machine in this course work does not allow for that. You could, however:

  • Have the states as MonoBehaviours with Unity events. Instead of switching to a new state, you would switch to one of these that you retrieved with GetComponent<>, for example. You would have to keep your head when dealing with Update versus ‘Tick’ (and other Unity messages since we won’t be dealing with these) so, perhaps
  • You could use the observer pattern and set up an EventBus component with Unity events and the states would then emit to this bus. The bus, in turn, would invoke the Unity events. Or
  • Just use C# events. You won’t be hooking them up in the inspector but who does that anyway?

I never found this to be a problem, so I never thought of changing it.

You cannot Invoke or declare a UnityEvent from within a State, but you can subscribe to them from within a state using AddListener and RemoveListener

There are a number of approaches you can take for this. You could, for example, have a SerializeField boolean on the StateMachine that indicate if a character can enter a given state or not… for example

[field: SerializeField] public bool CanRun {get; private set;} = true;

Now, before switching to a running state, you can test

if(stateMachine.CanRun) stateMachine.SwitchState(new PlayerRunningState(stateMachine));

This 3rd person course has changed the way I do a lot of things. I have modified and mangled the state machine in so many ways to suit my needs.

My base state machine is not a MonoBehaviour anymore, but a pure C# class. I do have a MonoBehaviour version, but that holds a reference to the non-MB class and relays the methods. You can say it’s a ‘decorator’ for the class. It’s also stack-based so I have a PushState() and PopState() in addition to the SwitchState(). When I go into TargetState, for example, I can just ‘push’ it and when I go out I ‘pop’ it and I’m back to whichever locomotion state I was before. It does mean I have to keep my head when I’m designing the flow.
I also have a NetworkBehaviour version that’s the same as the MonoBehaviour and relays the methods. This one is ‘iffy’ because I don’t know the multiplayer stuff well, but it works. I think.

My states have Tick() and FixedTick(). I would add LateTick() but I hadn’t needed it yet.
The reason I moved the base state machine to a pure class is because I wanted state machines in my states, and since they’re not MonoBehaviours I needed to improvise. Loads of iterations later, and I have what I use in almost all my games now.

Before the course, I used a state machine that Jason Weimann showed on his youtube channel. With it you define actual transitions for states; state a goes to state b if condition x is met. This happens automagically on every frame. It also has ‘any’ transitions; any state can go to state b if condition y is met.
I think I only used it in one project because it was a little tedious for me. I had several states with state machines inside them and the setup was a bit rough (I’m redoing that project now with my new fsm). You can check it out here

You’d still have the problem of not being able to bind events in the inspector.

Personally, if I needed this I would go with the observer pattern. I have a bit of a ‘observer pattern’ setup I use and I’d just make a MonoBehaviour with the Unity events on and make that an observer that would, in turn, invoke the event.

Oooh, I like that, a lot. Really good way to handle “walk to a location before engaging” state pair (I usually handle this by passing a closure to the walk to a location state).

I haven’t watched this video, but you’re describing something a lot like more of a Behaviour Tree (which is technically a form of State Machine, actually). I’ve actually been playing with Unity’s new Muse Behavior, which so far seems quite interesting.

since I’m here, I may as well ask. Is there a way to check for the previous state that lead to a current state? So for example, if I’m running, the past event for that can be either I’m idle or walking. Can I possibly program a solution into our current state machine to check for whether I got into running from walking, or running from idle? For example if I want to place specific conditions when entering the state or what not

I don’t know why I need this, but I recall a few times where I just wished I’d have that. Funnily enough, I don’t even remember where, but it was there at one point in time…

(P.S: My priority, at least on GameDev.TV for now (I’m working on something entirely different behind the scenes, and it needs a lot of refining), is still my knockback issue with the NavMesh)

This issue was just something that crossed my mind

It’s a little BT-ish but also a little like the animator controller without the fancy editor. You set up transitions like

_fsm.AddTransition(to: _alertState, from: _idleState, condition: () => NearbyEnemyCount() > 0);
_fsm.AddTransition(to: _idleState, from: _alertState, condition: () => NearbyEnemyCount() == 0);
_fsm.AddAnyTransition(to: _deathState, condition: () => _health <= 0);

These transition gates are checked every frame before the active state is ticked, with ‘any’ transitions having priority over normal transitions. For my purposes this was a little tedious because I wanted to change state from within other states (like we do in 3rd person course) so to get the conditions to work I had a enum property and the conditions would check the value.

Not sure what you mean with ‘place specific conditions’ but the stack-based solution I mentioned above does this. With a stack, when you ‘push’ a state it goes on top of the stack and becomes the active state. The previous state is exited, but it’s still in the stack. When you are done with this state, it gets ‘popped’ out of the stack and the old state is now at the top and becomes the active state again.
Here’s a version of it

public abstract class StateMachine : MonoBehaviour
{
    #region Properties and Fields

    public IState CurrentState => _stateStack.Count > 0 ? _stateStack.Peek() : null;
    private readonly Stack<IState> _stateStack = new();

    #endregion

    #region Unity Methods

    protected virtual void Update() => CurrentState?.Update(Time.deltaTime);
    protected virtual void FixedUpdate() => CurrentState?.FixedUpdate(Time.fixedDeltaTime);

    #endregion

    #region Public Methods

    public void PopState()
    {
        ExitAndPop();
        CurrentState?.Enter();
    }
    public void PushState(IState state)
    {
        CurrentState?.Exit();
        PushAndEnter(state);
    }
    public void SwitchState(IState state)
    {
        ExitAndPop();
        PushAndEnter(state);
    }

    #endregion

    #region Private Methods

    private void ExitAndPop()
    {
        CurrentState?.Exit();
        if (_stateStack.Count > 0) _stateStack.Pop();
    }
    private void PushAndEnter(IState state)
    {
        _stateStack.Push(state);
        CurrentState?.Enter();
    }

    #endregion
}

I went through your code, it’s not what I meant…

Basically, what I meant was a variable that stores the LastState from which the machine switched to another state, basically something to keep track of who called this state. It could be used to ensure, for example, that an action is done only if the call was from a specific state… it’s a little hard to explain, but I’ll try using an example I use in my own project:

I have a variable called ‘IsHostile()’, which Brian helped me develop, which is responsible for whether an enemy is aggressive towards me or not (I created an entire system on my own out of just that, I’m still refining it though). Anyway, since I introduced into my own project an NPC Ally system, to have NPCs that fight side by side with the player, I had to introduce an ‘InitialHostility’ variable as well, which tracks the default nature of the enemy, so we can return to it if the ally is no longer a part of the player’s team for example. Basically, it’s like a storage system that can allow you to tune Hostility at any moment you desire… it’s something that keeps track of who was the last value like for example (it could do better as a list, but for now it’s a starting point), something that holds the value for times of need

Arghh… it’s a little too hard to explain :sweat_smile:

apart from the crazy-looking code that still terrifies me to this day, that honestly looks somewhat easier to understand once you’re a little better with the language…

AND SOMEHOW, IT PARTIALLY COVERS WHAT I WAS ASKING FOR… :sweat_smile: - just that it doesn’t do it with our own context

  1. I was thinking of converting the states to MonoBehaviours but I have a lot of states (many of which are set up hierarchically). It will easily clutter up the inspector and soon get out of hand.

  2. I had never heard of EventBus before you mentioned it. The topic is a bit advanced for me at the moment but it seems like an interesting way to decouple code.

  3. I found Unity Events to be quite useful when I wanted to call functions of other systems but didn’t want one system to know the other. It also allows me to quickly change things in the inspector without having to modify the code.

For example, if I have a UnityEvent called OnPlayerLanded for when the Player lands on the ground and I want the AudioManager to play audio and VFXManager to play some VFX, I could drag them in as listeners to the event. I can easily remove them from the inspector if I want. That won’t break anything.

I can also export the AudioManager and VFXManager to other projects and they would work since I didn’t subscribe to any project-specific C# events in those scripts.

But if I have a lot of states, then I have to add a boolean for each state. Also, if I ever decided that I wanted to delete a state, then it would break all of those scripts where that specific state is mentioned as a transition.

That’s interesting, I’m eager to know how it’s an improvement to the current system. I’ve seen something similar being used to handle UI.

For example, if you press TAB it will push the inventory screen and set it as currentScreen. Pressing ESC would Pop it and make the currentScreen back to the top of the stack which is the HUD, let’s say.

I’m eager to learn how you used this for your character controller!

This is what I was searching for! I also came across a scriptable-object based State machine where you could specify the goto state for each state in the inspector.

This is exactly what I’ve done. I’ve created a separate script(MonoBehaviour) that listens to C# events from the states and invokes UnityEvents respectively. This seems like the best way to tackle this problem without changing the way the state machine is set up.

I did this by first making the “SwitchState” method virtual so that it can be overwritten by the child classes:

 public virtual void SwitchState(State state)
    {
        currentState?.Exit();
        currentState = state;
        currentState.Enter();
    }

Then inside the child classes(PlayerStatemachine), you can do something like this to get the previous state and next state:

        public override void SwitchState(State state)
        {
            PlayerBaseState previousState = currentState as PlayerBaseState;
            PlayerBaseState nextState = state as PlayerBaseState;
            
            base.SwitchState(state);

            OnStateChanged?.Invoke(previousState, nextState);
        }

Not necessarily, just the states that could be optioned. There are actually a number of ways to handle this, including using a [Flags] enum.

    [Flags]
    public enum StateRules
    {
        None = 0,
        Walking = 1<<0,
        Running = 1<<1,
        Jumping = 1<<2,
        Swimming = 1<<3,
        Flying = 1<<4,
        Climbing = 1<<5,
        Idle= 1<<6,
        Melee=1<<7,
        Ranged=1<<8,
        Magic=1<<9,
        Block=1<<10,
        Dodge=1<<11,
        Crouching=1<<12,
    }

A [Flags] enum is backed by an int, meaning you can have up to 32 values (1<<31). When you add them to the StateMachine with

[field: SerializeField] public StateRules StateRules {get; private set;} the inspector will show them like a LayerMask, where you can select as many or none from a drop down as needed.

I’ll try this out! Thanks @Brian_Trotter

Also, I made a boss battle using Unity Muse. I was using Kiwi Coder’s Behaviour Tree earlier but Unity Muse BT has a lot more options. I’ll post it here after a little bit of polishing.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms