Search Unity

  1. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

C# Proper State Machine

Discussion in 'Scripting' started by Rodlaiz, Jan 19, 2016.

  1. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,503
    True, but perhaps not relevant to the topic. If you need to serialize a list of states in the inspector then you might be more in the realm of what is described in one of the previous posts (updated link is here - https://learn.unity.com/tutorial/5c515373edbc2a001fd5c79d) where you're leveraging ScriptableObject instead of empty abstract base classes.
     
  2. atimtakus

    atimtakus

    Joined:
    Apr 28, 2016
    Posts:
    2
    I am learning unity and recently working on interfaces and states machines. @KelsoMRK how would i do if i had a SecondState and i wanted to change to SecondState inside from TestState?
     
  3. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,503
    You'd want some component on the owner entity that has a reference to its state machine

    Code (csharp):
    1.  
    2. public class StateComponent : MonoBehavior
    3. {
    4.     public StateMachine Machine {get; private set;}
    5. }
    6.  
    7. public class TestState : IState
    8. {
    9.     private readonly owner: StateComponent;
    10.  
    11.     public TestState(StateComponent owner) { this.owner = owner; }
    12.  
    13.     // some omissions for brevity
    14.  
    15.     private void TriggerSomeChange()
    16.     {
    17.         owner.Machine.ChangeState(new SecondState());
    18.     }
    19. }
    20.  
     
  4. atimtakus

    atimtakus

    Joined:
    Apr 28, 2016
    Posts:
    2
    @KelsoMRK Thanks for pointing out the right direction how to reference properly. I have learned very similar state machine to yours where the changeState function was placed in StateComponent. I think your approach is better for modularity.
     
  5. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,503
    You could certainly do it that way if you didn't want to expose the state machine to the outside world. There very well could be value in that.
     
  6. LaurNTU

    LaurNTU

    Joined:
    Aug 15, 2019
    Posts:
    3
    Heyo guys! Glad to find this thread active. I am implementing the FSM which @KelsoMRK posted, with the IState interface and each state being a class. It's great, but I've been wondering: how would I go if I want two states happening in the same time?(for example, shooting and moving. Right now I can either move or shoot, not both in the same time).
    Thanks!
     
  7. R1PFake

    R1PFake

    Joined:
    Aug 7, 2015
    Posts:
    312
    Im not sure if this will work for your specific case but you could make a additional state class, maybe something like MultiState which implements the IState interface. Then you pass your "child" states to the MultiState instance as an array or list (for example Shooting and Moving) and in the MultiState implementation you just loop (foreach) over the "child" states and redirect (call) the Enter, Execute and Exit methods of every "child" state.
     
  8. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,503
    That sort of breaks the "finite" part of Finite State Machine. With that said, what you're describing can be described as "attack move" which we did do as its own state. It inherited from MoveState so we didn't have to duplicate any movement or pathfinding code and then it also did calculations for attacking enemies that came into view.

    This is probably not a good approach because you've got 2 states executing simultaneously which again breaks the Finite rule (only 1 state can possibly be executing at a given time). You'd also have the added complexity of those 2 states competing with each other (ie - the attack state wants to move into range while the move state wants to move to destination).
     
  9. LaurNTU

    LaurNTU

    Joined:
    Aug 15, 2019
    Posts:
    3
    @R1PFake, this actually might work as I might not have conflicted states, but will look more into it. Thanks a lot!
    @KelsoMRK, and did you get to have a lot of overhead, having many combined states? Or is it something not that hard to manage?
    Also, what do you think about having multiple state machines instead of only one? Thanks a lot to you too!
     
  10. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,503
    Overhead in terms of what? Again, we didn't really "combine states", we had AttackState, MoveState, and AttackMoveState. They were always discrete.

    Not sure exactly what you're proposing here.

    In all honesty if you're struggling to keep your states discrete and feel like you're going to have a bunch of blended ones then a finite state machine might not be the best thing to use in your case.
     
  11. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,441
    Hey,
    I'm using @KelsoMRK 's state machine example, and it's working out really great for the most part but I came across an odd problem I thought someone here might be able to help me with.

    There was something at the very start of one state's execute function that could push the statemachine into another state. In this case the exit function would happen, then the enter for the next state, but then the remainder of the original execute state would run after that (causing issues).
    I thought maybe I could look at somehow breaking the execute function at that point? It took a bit to figure out what was going on thought and I thought someone here might have a good approach for that situation.

    Thanks!
    Pete
     
  12. R1PFake

    R1PFake

    Joined:
    Aug 7, 2015
    Posts:
    312
    I don't know your exact code but can't you just add a return statement directly after you push the new state?
     
  13. ensiferum888

    ensiferum888

    Joined:
    May 11, 2013
    Posts:
    279
    I based all my AI logic on this very state machine pattern. I found a way to fix that problem but it might not be the most elegant solution. Normally you add the tasks or states at the end of the queue so the AI will carry them in order.

    MoveToLocation -> PickupItem -> EquipItem for example.

    I have a special state called CombatState that will determine if there's a threat nearby, if so it will calculate if you're in range to attack, if not I'll insert a MoveToLocation state but before the CombatState rather than after so once I determine that there's an enemy I need to walk to my state queue looks like this:

    MoveToLocation -> CombatState

    Once the agent reached it's location the MoveToLocation state will pop from the queue, the CombatState will assess if I'm now in range to attack, if so I'll insert the attack state so my queue will look like this:

    AttackTarget -> CombatState

    Once the threat is gone the CombatState will determine there is no more danger, pop from the queue and the AI will be able to recalculate its current tasks.

    MoveToLocation -> Sleep (or whatever)

    So in my main statemachine code when I assess the current task for an agent the first thing I check is if there are enemies nearby, if so I just insert the CombatState in the queue and let that determine the logic of fighting. In your case you could have an AttackTarget state that basically just steer the agent to the target location, every frame check if you're in range to attack and if your weapon can fire (whether it's on cooldown or need to reload) and if so just add a ShootState in front of the queue. It will then automatically return to the AttackTarget state and continue pursuing until it can fire again (or until the target is dead)
     
  14. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,503
    Hard to say without seeing code but if you transition into the new state and then immediately return it should be fine. In either case, something somewhere is hanging onto a reference to the old state and executing it so it's a matter of hunting that down.
     
  15. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,503
    A lot of what you're describing is goal driven agent behavior. https://web.cs.wpi.edu/~rich/courses/imgd400x-b08/lectures/J-Goal.pdf

    Also outlined in the same book I derived the FSM pattern from https://www.amazon.com/Programming-Example-Wordware-Developers-Library/dp/1556220782
     
  16. Dr_Michael_Um

    Dr_Michael_Um

    Joined:
    Mar 25, 2019
    Posts:
    1
    Hello, @KelsoMRK. I came across this discussion looking for solution about parallel states execution, and yours solution seems to be great. I'll definitely try it out.

    But as you mentioned, if we supposed to have a lot of blended states, what would you recommend to use instead of FSM? Maybe you've come across systems with the same behavior and know an alternative solution.

    Thanks in advance.
     
  17. mrCharli3

    mrCharli3

    Joined:
    Mar 22, 2017
    Posts:
    628
    I use this FSM with great success. I dont use a queue however, instead say I want to go to a tree, cut it down, then stash it. I do:

    FindTreeState()

    In Enter I find the tree, in Execute I check if we have arrived, if we have, I change to:
    WorkTreeState()

    In Enter I start the animation, in Execute I check if the tree has fallen or if worker resources is full, if it is then:
    StashTreeState()

    In enter I find the closest storage with room, in execute I check if we have arrived, if we have, I stash the resources and find a new job in a priority list of professions.

    My agents have a seperate component that looks for enemies every .5 seconds, and if triggered, I simply interupt current state and enter whatever state is appropriate for that unit during combat, for a worker that would be
    FleeState()


    This means I sometimes have very similar code in several states, for example finding a rock is not much different than finding a tree. But I usually derive from a class with functions for the common features. I like it since all the logic related to a state is in the state. Very easy to debug.
     
  18. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,503
    I mean, you can do a state machine and skip the finite part. So you'd just have a collection of states that execute each frame. Seems like it'd be hard to keep them from stepping on each other though.

    At the end of the day, your agent should have some kind of goal they're trying to achieve and the "AI" bit is the steps they take to achieve that goal. There's material abound regarding goal driven agent behavior.
     
    Dr_Michael_Um likes this.
  19. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,441
    Hi all,
    I get caught occasionally by this when using this state machine. Here's a simplified example -
    Code (CSharp):
    1.     StateTest owner;
    2.     public TestState1(StateTest owner) { this.owner = owner; }
    3.  
    4.     public void Enter()
    5.     {
    6.         Debug.Log("Enter State 1");
    7.     }
    8.  
    9.     public void Execute()
    10.     {
    11.         Debug.Log("Update State 1");
    12.         if(SomethingHappened)owner.StateChange(); //Something triggers another state here...
    13.         Debug.Log("Update State 1 more");
    14.     }
    15.  
    16.     public void Exit()
    17.     {
    18.         Debug.Log("Exit State 1");
    19.     }
    The console logs this -
    Screen Shot 2020-08-07 at 8.28.46 am.png

    So if something in the Execute function triggers a new state, the state machine continues that execute function after the new state has started.
    I have a bandaid case by case approach for fixing it and I was wondering if anyone here had some good suggestions. I love the state machine, this is the only issue I run into.
    Thanks!
     
  20. R1PFake

    R1PFake

    Joined:
    Aug 7, 2015
    Posts:
    312
    The state machine can't "cancel" your method, you have to add a return after the state change or move the other code in a else branch. (Or throw a exception after the state change, but please don't do that, it's overkill and a waste of performance, just add a return)
     
    petey likes this.
  21. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,503
    You have to return immediately after the state change or the stale state will continue executing
    Code (csharp):
    1.  
    2. if (thing)
    3. {
    4.     ChangeState();
    5.     return;
    6. }
    7.  
     
    petey likes this.
  22. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,441
    Okay great, I think I was just being a bit loose with the way I put everything together :p
    Thanks!
    ( sorry I missed your earlier post @R1PFake, I appreciate that :) )
     
  23. Slater7

    Slater7

    Joined:
    Oct 10, 2018
    Posts:
    11
    Hey everyone,
    It's a pretty nooby question, but which class do I attach to which gameobject to make this work?
     
    Last edited: Sep 3, 2020
  24. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,503
    You don't. This pattern stipulates that you pass in some MonoBehaviour instance to each class as the "owner" of the state. So you could do something like this

    Code (csharp):
    1.  
    2. public class Agent : MonoBehaviour
    3. {
    4.     private StateMachine _stateMachine = new StateMachine();
    5.  
    6.     private void Start()
    7.     {
    8.         _stateMachine.ChangeState(new SomeState(this));
    9.     }
    10. }
    11.  
     
    SlimeProphet and Slater7 like this.
  25. Slater7

    Slater7

    Joined:
    Oct 10, 2018
    Posts:
    11
    @KelsoMRK Ahh thank you!

    But how do I transition between the states from e.g. "Agent"?

    Say for example the first state that "Agent" is in is the IdleState.
    How do I change the currentState to WalkState and vice versa?

    The only way I can think of is something like this:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Agent : MonoBehaviour
    4. {
    5.     StateMachine stateMachine = new StateMachine();
    6.     Vector3 direction;
    7.  
    8.     private void Start() {
    9.         stateMachine.ChangeState(new IdleState(this));
    10.     }
    11.  
    12.     private void Update() {
    13.         direction = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical"));
    14.  
    15.         stateMachine.Update();
    16.  
    17.         if(direction.magnitude > 0.1f) {
    18.             stateMachine.ChangeState(new RunState(this));
    19.         } else {
    20.             stateMachine.ChangeState(new IdleState(this));
    21.         }
    22.     }
    23. }
    Is that the correct implementation of your state machine or did I totally mess it up?
     
  26. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,503
    That would transition to a new RunState every frame there is input and a new Idle state every frame there was no input.
     
  27. Slater7

    Slater7

    Joined:
    Oct 10, 2018
    Posts:
    11
    I don't quite get the idea behind your state machine, maybe a bit over my head.
    What do you think about state machine mecanim?
     
  28. R1PFake

    R1PFake

    Joined:
    Aug 7, 2015
    Posts:
    312
    There are many different state machine implementations depending on how complex your AI states / transations are, the most basic implementation is shown in this thread, where the state themself would decide when to change to other states.

    Let's say you have a basic unit which walks around and attacks you if you walk near it, it could have a walk state which handles the walking and also check if a target is near it, if it finds a target it would trigger a change to a follow/attack state, the attack state would handle the attack logic and decide if it should stop the attack (maybe the target ran away or was killed) in this case the attack state would trigger to change back to the walk state etc

    So in short, the state handles the state logic and the transition logic if you have a more complex system with many transition conditions etc then a other state machine implementation might be better for your use case.


    Btw you can also use the Unity built in animator as a state machine for game logic if you write custom StateMachineBehaviours (don't forget to set exit time to 0, because you usually don't need a exit time for game logic states), if you want something more visual
     
    Slater7 likes this.
  29. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,503
    It sounds like you're after more of a character controller than a state machine.
     
  30. Slater7

    Slater7

    Joined:
    Oct 10, 2018
    Posts:
    11
    Thanks that made it clearer for me!
    Yeah I started to work with the Unity animator statemachine because I'm going to transition between many animations.
    Got a problem though:
    I want to create a thirdPerson game, just with the standard animations(walk, run, stand). How would I implement this into the Mecanim statemachine?
     
  31. mrCharli3

    mrCharli3

    Joined:
    Mar 22, 2017
    Posts:
    628
    This is probably covered in any character controller tutorial, Google away :)
     
  32. Slater7

    Slater7

    Joined:
    Oct 10, 2018
    Posts:
    11
    Well you find many character controller tutorials online, but most of them are just one monolithic player script where they handle every logic,input and physics inside it.
    That doesn't scale well and is a pain in the ass to work with..

    I'm looking for a way to have a robust state machine pattern to work with, that's all.
     
  33. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,503
    The first video result for "mecanim third person controller" was this
     
  34. Slater7

    Slater7

    Joined:
    Oct 10, 2018
    Posts:
    11
    Yeah saw that one too, but the whole structure is just one big 250+ Player script where you can only move and jump(and that is already clunky). If I would implement just a few features into that, it'll blow up.

    I read about scripts which inherit from state machine behaviours and can be attached to the mecanim pattern, where every state is a script and has all specific logic in them.
    What I don't know is how to implement that into a third person game.
    Thanks for the help
     
  35. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,503
    Might want to open a new thread. You're sort of out of scope for the topic in this one.
     
  36. Mister-D

    Mister-D

    Joined:
    Dec 6, 2011
    Posts:
    1,613
    i stumbled upon this as im trying to make some AI FSM
    i know this is old but how do u use this?
    u need conditions to make a transition right? where do i put them?
    would u be so kind to point me in the right direction?
    tnx
     
  37. R1PFake

    R1PFake

    Joined:
    Aug 7, 2015
    Posts:
    312
    The question was already answerd in previous post(s)
     
unityunity