Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Discussion State machine using all Monobehaviours, bad idea?

Discussion in 'Scripting' started by CPTANT, Nov 23, 2022.

  1. CPTANT

    CPTANT

    Joined:
    Mar 31, 2016
    Posts:
    80
    Hi guys, I am working on creating a finite state machine for the AI of the enemies in my game. I first had all the transitions checked by the AIController, but I felt that this was hard to maintain and expand.

    I try to split things up like this:

    AIController - Monobehaviour: Holds the current state and handles transitions
    State - Monobehaviour: Contains state logic and a list of transitions
    Transition: script: Holds a decision and a state to which is switched if the condition is met
    Decision: Monobehaviour: Checks a certain condition and returns true or false

    I add the AIController and the different State and Decision monobehaviours to the enemy gameObjec:

    upload_2022-11-23_20-40-43.png
    Then I add the transitions in a list on the States like this:
    upload_2022-11-23_20-41-18.png

    I choose monobehaviours for the states and decisions because they often have to contain state (The not FSM kind) and contain parameters. I find it more convenient to tweak the parameters on the object itself than in a seperate scriptable object. The only downside I have so far is that you end up with a lot of monobehaviours as part of the ai. I honestly can't find anyone else that has it set up this way so I am feeling like I am kinda crazy. Are there any obvious pitfalls with this approach?


    Here is the code of my base classes:

    Code (CSharp):
    1. public class AIController : MonoBehaviour
    2. {
    3.     public AI_State initialState;
    4.     private AI_State currentSate;
    5.  
    6.     void Start()
    7.     {
    8.         SwitchState(initialState);
    9.     }
    10.  
    11.     void FixedUpdate()
    12.     {
    13.         currentSate.PerformState();
    14.         currentSate.CheckTransitions(this);
    15.     }
    16.  
    17.     public void SwitchState(AI_State newState)
    18.     {
    19.         if (currentSate != null)
    20.         {
    21.             currentSate.StopState();
    22.             currentSate.DeInitializeDecisions(this);
    23.         }
    24.         newState.StartState();
    25.         newState.InitializeDecisions(this);
    26.         currentSate = newState;
    27.     }
    28. }
    Code (CSharp):
    1. public class Transition
    2. {
    3.     public Decision decision;
    4.     public AI_State newState;
    5.  
    6.     public void CheckTransition(AIController aIController)
    7.     {
    8.         if (decision.Decide(aIController))
    9.             aIController.SwitchState(newState);
    10.     }
    11. }
    Code (CSharp):
    1. public abstract class Decision : MonoBehaviour
    2. {
    3.     public virtual void InitializeDecision(AIController aiController) { }
    4.     public virtual void DeInitializeDecision(AIController aiController) { }
    5.     public abstract bool Decide(AIController aiController);
    6. }
    Code (CSharp):
    1. public abstract class AI_State : MonoBehaviour
    2. {
    3.     public List<Transition> Transitions = new List<Transition>();
    4.  
    5.     public abstract void StartState();
    6.  
    7.     public abstract void PerformState();
    8.  
    9.     public abstract void StopState();
    10.  
    11.     public void InitializeDecisions(AIController aiController) {
    12.         foreach (Transition transition in Transitions)
    13.         {
    14.             transition.decision.InitializeDecision(aiController);
    15.         }
    16.     }
    17.     public void DeInitializeDecisions(AIController aiController)
    18.     {
    19.         foreach (Transition transition in Transitions)
    20.         {
    21.             transition.decision.DeInitializeDecision(aiController);
    22.         }
    23.     }
    24.  
    25.     public void CheckTransitions(AIController aiController) {
    26.         foreach (Transition transition in Transitions)
    27.         {
    28.             transition.CheckTransition(aiController);
    29.         }
    30.     }
    31. }
    Edit: Uh I wanted to move this to general Discussion but now there are two, can I delete this thread?
     
    Last edited: Nov 23, 2022
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    37,210
    FSM finite state machines:

    This is my position on finite state machines (FSMs):

    https://forum.unity.com/threads/state-machine-help.1080983/#post-6970016

    I'm kind of more of a "get it working first" guy.

    Ask yourself, "WHY would I use solution XYZ when I just need a variable and a switch statement?"

    All FSM solutions I have seen don't actually improve the problem space.

    Your mileage may vary.

    "I strongly suggest to make it as simple as possible. No classes, no interfaces, no needless OOP." - Zajoman on the Unity3D forums.
     
  3. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    6,169
    Not every thread about FSM's has to start with your post about how you don't like them. It's borderline offtopic and unhelpful.

    On topic, OP, if your AI set ups are getting that complicated than perhaps you've hit the upper limit of usefulness of state machines.

    They aren't the only option available as far as setting up AI goes; there are other options such as behavioural trees that might be working looking at if you want to design more elaborate behaviours.

    FSM's I find more useful for the player character, as their patterns of behaviour are more straight forward.

    As far as FSM's go I've always worked with interface implementing plain classes, with a single monobehaviour component that run the current state, though this requires custom inspector work to make it visible and tweakable.
     
    lordofduct likes this.
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    37,210
    OP reported that they...

    To me that is a tell for "has too many moving parts for what it is doing."

    As you said, perhaps it is just that a new approach is needed. Either way, I've never seen OO bring anything useful to the discussion of FSMs, and yet again and again we see people tangled up in abstractions and virtualizations and oh my goodness.

    I regularly get called in to fix over-complex OO solutions to what are simple set-and-check-a-variable problems, and this is among peer professional engineers, not hobbyists. Therefore it is important for me to discourage what I consider poor engineering practices.