Search Unity

C# Proper State Machine

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

  1. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    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,539
    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,539
    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:
    17
    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:
    540
    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,539
    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:
    17
    @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,539
    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,824
    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:
    540
    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:
    317
    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,539
    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,539
    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:
    4
    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:
    976
    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,539
    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,824
    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:
    540
    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,539
    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,824
    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,539
    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,539
    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:
    540
    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,539
    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:
    976
    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,539
    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,539
    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,694
    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:
    540
    The question was already answerd in previous post(s)
     
  38. mgardner3000

    mgardner3000

    Joined:
    Dec 4, 2020
    Posts:
    2
    Sorry to Necro this thread, but I'm using the implementation provided by Kelso and I've run into a bit of a block trying to adapt it to my needs. I'm building a 'status' system for mobs in a roguelike, and I thought it might be worth using a state queue to represent stacking status effects. So I call enter when I enqueue the state, I call exit when I dequeue, but I'm having a problem tracking the expiration of the statuses.

    In practice, it would look something like this prototype for a slow script (BaseState being equivalent to IState in Kelso's):
    Code (CSharp):
    1. public class Slow : BaseState
    2. {
    3.     //expiration is 100 + the current turn at the moment.
    4.     public int expiration;
    5.     public Mob owner;
    6.     public Slow(Mob afflicted) {
    7.         owner = afflicted;
    8.     }
    9.     public void Enter() {
    10.         expiration = MyTimer.turnTimer + 100;
    11.         owner.SetMaxTurn(owner.GetMaxTurn() + 10);
    12.     }
    13.     public void Execute() {
    14.  
    15.     }  
    16.     public void Exit() {
    17.         owner.SetMaxTurn(owner.GetMaxTurn() - 10);
    18.     }
    19. }
    The problem is that when I Peek at the object from my generic state queue, I can't access the expiration as the compiler complains BaseState does not have a expiration value.

    Ideally, it would look something like this in my turn logic:
    Code (CSharp):
    1.     //Creature move
    2.     var status = creature.statusQueue.Peek();
    3.     if (status.expiration <= MyTimer.turnTimer) {
    4.         creature.statusQueue.Dequeue();
    5.         //recurse until all expired statuses are dequeue'd
    6.     }
    If anyone that has any tricks handy here, I'd appreciate it.
     
  39. mrCharli3

    mrCharli3

    Joined:
    Mar 22, 2017
    Posts:
    976
    You would need to make expiration a part of basestate for that to work.
     
  40. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    What you're describing isn't a good use case for a state machine honestly. It sounds like you just want a collection of statuses that "tick" each frame to do something, track how long they have until expiration, and then remove themselves when they expire.
     
  41. mgardner3000

    mgardner3000

    Joined:
    Dec 4, 2020
    Posts:
    2
    I guess the problem is that I have three types of status, which has me a little stuck in the drawing board stage. There's a few that are ticking clocks like you're thinking of, like poison that effects per turn. But then there are adjustments like the slow script, which I just want to adjust stats once when it takes effect then revert on expiry. Finally, there's a set that would effect the the behavior of the AI or Player input, so things like Pokemon's confusion status, where you roll to see if you damage yourself.

    Though writing it all out now, it seems like I was eventually going to be using the states to simply apply flags for AI/player behavior, so I think I get your point on abusing states. Thanks for the input on this, I appreciate it.
     
  42. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Sounds like you just need a common interface for all statuses to implement.
    Code (csharp):
    1.  
    2. public interface IStatus
    3. {
    4.     void Apply();
    5.     void Tick();
    6.     void UnApply();
    7. {
    8.  
    Then keep a queue of those to manage.
     
    mgardner3000 and ensiferum888 like this.
  43. jhindsight

    jhindsight

    Joined:
    Jul 4, 2017
    Posts:
    1
    I'm using @KelsoMRK implementation and it works great but when I try to create a FSM in my game manager script and when I try

    gameStateMachine.ChangeState(new FiniteStateMachine.LoadingState(this));

    I get the following error

    Error CS1503 Argument 1: cannot convert from 'GameManager' to 'FiniteStateMachine.Unit'

    My GameManager.cs script extends a singleton which uses generics but the base class does inherits from monobehaviour.

    Any ideas what I'm doing wrong?
     
  44. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Would need to see actual code. In either case, your inheritance hierarchy is incorrect somewhere.
     
  45. reggie5538

    reggie5538

    Joined:
    Dec 11, 2020
    Posts:
    2
    I'm a bit of a noob with using C# and Unity programming, but out of curiosity, is it possible to have all of these in the same C# script? or would they have their own scripts? I tried separating them bt for some reason I keep getting errors expecting an ending semi-colon. . . it might also be because it's code from 2016but either way it looks super useful and I would love to be able to learn from your State machine design!
     
  46. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    You can. I wouldn't, but you can. You might want to check out the beginner learning materials that Unity provides or do some intro C# courses and then circle back to stuff like this. Otherwise you might end up just getting frustrated, which isn't fun!
     
    Nigey likes this.
  47. Plazmaniac1

    Plazmaniac1

    Joined:
    Mar 20, 2018
    Posts:
    1
    Resurrecting this thread yet again to clarify my understanding.

    The majority of replies seem to agree that @KelsoMRK's implementation of a FSM is the preferred method, but can't the same thing be accomplished with an abstract class instead of an interface? My understanding- correct me if I'm wrong- is that the interface serves only to define the basic functions that each state needs. Here, I'll lay out a whole example to demonstrate my current understanding.

    So in this example, let's say this is our interface:

    Code (CSharp):
    1. public interface ICharacterBaseState
    2. {
    3.     public void EnterState();
    4.     public void ExecuteStateFunctions();
    5.     public void ExitState();
    6. }
    Admittedly, I'm new to interfaces. I looked up some videos on the topic, and I feel like I understand it. Please correct me where I'm mistaken though. As I understand it, an interface

    This interface would be implemented in each state which would each be its own class in separate scripts. For this example, let's say I'm programming two states: Player Idle and Player Jump

    Code (CSharp):
    1. public class PlayerIdleState : ICharacterBaseState
    2. {
    3.     public void EnterState()
    4.     {
    5.         Debug.Log("entering idle state");
    6.     }
    7.     public void ExecuteStateFunctions()
    8.     {
    9.         Debug.Log("performing idle state");
    10.     }
    11.     public void ExitState()
    12.     {
    13.         Debug.Log("exiting idle state");
    14.     }
    15. }
    16.  
    17. public class PlayerJumpState : ICharacterBaseState
    18. {
    19.     public void EnterState()
    20.     {
    21.         Debug.Log("entering jump state");
    22.     }
    23.     public void ExecuteStateFunctions()
    24.     {
    25.         Debug.Log("performing jump state");
    26.     }
    27.     public void ExitState()
    28.     {
    29.         Debug.Log("exiting jump state");
    30.     }
    31. }
    After that, there should be a state machine script that defines the default state on start or awake (probably simply stored in a "currentState" variable) and calls the EnterState method. This script would also have a method to switch states by changing the currentState variable and then calling currentState.EnterState(). This "SwitchStates()" method would be called when the player presses the jump button and be passed the PlayerJumpState as an argument. PlayerJumpState would have code within it to define exiting and switching back to the idle state.

    Okay so now that I've outlined all that... wouldn't there be absolutely no differences if the interface were replaced by an abstract script? (apart from syntax of course) The base state class would look something like this:

    Code (CSharp):
    1. public abstract class PlayerBaseState
    2. {
    3.     public abstract void EnterState();
    4.     public abstract void ExecuteStateFunctions();
    5.     public abstract void ExitState();
    6. }
    and each state would look like this:

    Code (CSharp):
    1. public class PlayerIdleState : PlayerBaseState
    2. {
    3.     public override void EnterState()
    4.     {
    5.         Debug.Log("entering idle state");
    6.     }
    7.     public override void ExecuteStateFunctions()
    8.     {
    9.         Debug.Log("performing idle state");
    10.     }
    11.     public override void ExitState()
    12.     {
    13.         Debug.Log("exiting idle state");
    14.     }
    15.  
    16. }
    To me, both the interface implementation and the abstract class implementation are pretty similar. Am I missing something? Is there a difference I'm not realizing? Did I leave something out? Or is it just down to preference? Why would I want to use an interface for this instead of an abstract class or vice versa?
     
  48. R1PFake

    R1PFake

    Joined:
    Aug 7, 2015
    Posts:
    540
    In the end it will depend on the context, in your example both will work.

    You have to remember that C# only allows a class to have one base class chain but "unlimited" interface implementations.

    So if you use a IState interface you could implement one State as ScriptableObject and an other State as a MonoBehavior and other yet an other state as a "plain object" and all of them could be handled by the same code which only cares about IState, so in the end the code will be more reusable.

    If you use a abstract State class then you can't implement it as a ScriptableObject and MonoBehavior at the same time, because they have no common base.

    (Hint: Im don't say that you should implement your states as a mix of ScriptableObject and MonoBehavior, I just wanted to show a example with classes which exist in the Unity world)

    But again, it depends on your context, if you don't care about it or don't need any of this, then a abstract class will work too.

    If you want more details you can google something like "C# abstract class vs interface" and you will find many things to read, better prepare some popcorn, because people have different opinions about this topic and some people treat it as a religion.
     
    Last edited: May 14, 2022
    Bunny83 and petey like this.
  49. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,689
    It's a great CIS-100 bullet point but in practice rarely provides any actual reusability.

    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 actually working first" guy.

    Your mileage may vary.
     
    Zajoman likes this.
  50. R1PFake

    R1PFake

    Joined:
    Aug 7, 2015
    Posts:
    540
    I have used my state machine implementation in multiple projects without changing the state machine code for any project specific features using a state interface. In some projects I implemented the states as plain object and in other projects I implemented them as ScriptableObject, all using the same state machine code.

    So using the interface instead of abstract approach made it more reusable for me.

    But I don't want to drag this off topic, because the person was asking about abstract vs interface
     
    Bunny83 likes this.