Search Unity

C# Proper State Machine

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

  1. Rodlaiz

    Rodlaiz

    Joined:
    Jul 30, 2013
    Posts:
    38
    Hello Guys and Gals, I'm finally starting to learn C# so I can make my own games in Unity and now I'm at the stage of trying to develop my very first FSM. I've found this great tutorial youTube and followed all the steps in it, it's very comprehensive, I'll paste the code at the bottom.

    What I wanted to ask you is if this FSM is good to use for a game in production, I'm a noob on code but somehow I feel it's not very good for performance. I've found this other great example but, to be honest, the code is way to advance for me right now. Everyone in the comments are praising the code and saying that it's well implemented using Dictionary, GetHashCode, etc

    Maybe is there a way to improve the code below so it can be used in a real game? or is it good as it is?

    Or maybe you have another example of a good, clean, FMS that can be easily implemented by a noob in a game?

    Thank you for your time.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class FSM_YouTube : MonoBehaviour {
    5.  
    6.     public enum State
    7.     {
    8.         Idle,
    9.         Initialize,
    10.         Setup,
    11.         Combat,
    12.         Looting
    13.     }
    14.  
    15.     public enum Combat
    16.     {
    17.         FindEnemy,
    18.         AttackEnemy,
    19.         EnemyDead
    20.     }
    21.  
    22.     private State _state; //Local variable that represents our state
    23.     public Combat combat;
    24.  
    25.     IEnumerator Start()
    26.     {
    27.         _state = State.Initialize;
    28.         combat = Combat.FindEnemy;
    29.  
    30.         while (true) {
    31.             switch (_state) {
    32.             case State.Initialize:
    33.                 InitMe ();
    34.                 break;
    35.             case State.Setup:
    36.                 SetMeUp ();
    37.                 break;
    38.             case State.Combat:
    39.                 InCombat ();
    40.                 break;
    41.             case State.Looting:
    42.                 Looting ();
    43.                 break;
    44.             }
    45.             yield return 0;
    46.         }
    47.     }
    48.  
    49.     private void InitMe()
    50.     {
    51.         Debug.Log ("This is the InitMe function");
    52.         _state = State.Setup;
    53.     }
    54.     private void SetMeUp()
    55.     {
    56.         Debug.Log ("This is the SetMeUp function");
    57.         _state = State.Combat;
    58.     }
    59.     private void InCombat()
    60.     {
    61.         Debug.Log ("In Combat");
    62.  
    63.         switch (combat)
    64.         {
    65.         case Combat.FindEnemy:
    66.             FindEnemy ();
    67.             break;
    68.         case Combat.AttackEnemy:
    69.             AttackEnemy ();
    70.             break;
    71.         case Combat.EnemyDead:
    72.             EnemyDead ();
    73.             break;
    74.         }
    75.     }
    76.     private void Looting ()
    77.     {
    78.         Debug.Log ("Looting");
    79.         _state = State.Idle;
    80.     }
    81.  
    82.     private void FindEnemy()
    83.     {
    84.         Debug.Log ("Finding Enemy");
    85.         combat = Combat.AttackEnemy;
    86.     }
    87.     private void AttackEnemy()
    88.     {
    89.         Debug.Log ("Attacking Enemy");
    90.         combat = Combat.EnemyDead;
    91.     }
    92.     private void EnemyDead()
    93.     {
    94.         Debug.Log ("Enemy Dead");
    95.         combat = Combat.FindEnemy;
    96.         _state = State.Looting;
    97.     }
    98. }
     
  2. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    I would say it's not a good FSM implementation :) Generally, you'd want each state to be it's own class. This allows for code re-usability and eases debugging (and you will be debugging). In our game we have around 20 states a unit can be in (as of right now, it will increase) so imagine your switch statement running through 20 enums. Yikes!

    Our FSM is based on Mat Buckland's. At its most basic, there is an interface that mandates three methods. Each state implements those methods and gets passed the owner of the state (the thing in the world that is doing the thinking).
    Code (csharp):
    1.  
    2. public interface IState
    3. {
    4.     public void Enter();
    5.     public void Execute();
    6.     public void Exit();
    7. }
    8.  
    9. public class StateMachine
    10. {
    11.     IState currentState;
    12.  
    13.     public void ChangeState(IState newState)
    14.     {
    15.         if (currentState != null)
    16.             currentState.Exit();
    17.  
    18.         currentState = newState;
    19.         currentState.Enter();
    20.     }
    21.  
    22.     public void Update()
    23.     {
    24.         if (currentState != null) currentState.Execute();
    25.     }
    26. }
    27.  
    28. public void TestState : IState
    29. {
    30.     Unit owner;
    31.  
    32.     public TestState(Unit owner) { this.owner = owner; }
    33.  
    34.     public void Enter()
    35.     {
    36.         Debug.Log("entering test state");
    37.     }
    38.  
    39.     public void Execute()
    40.     {
    41.         Debug.Log("updating test state");
    42.     }
    43.  
    44.     public void Exit()
    45.     {
    46.         Debug.Log("exiting test state");
    47.     }
    48. }
    49.  
    50. public class Unit : MonoBehaviour
    51. {
    52.     StateMachine stateMachine = new StateMachine();
    53.    
    54.     void Start()
    55.     {
    56.         stateMachine.ChangeState(new TestState(this));
    57.     }
    58.  
    59.     void Update()
    60.     {
    61.         stateMachine.Update();
    62.     }
    63. }
    64.  
     
  3. Rodlaiz

    Rodlaiz

    Joined:
    Jul 30, 2013
    Posts:
    38
    Thank you very much KelsoMRK. I haven't seen a State Machine like yours before since all the examples I've found were using enum I will try to implement your code on my player an see if I can make it work, it seems pretty straighforward. If I get stuck I'll definitely will send you a PM, thanks again
     
  4. Rodlaiz

    Rodlaiz

    Joined:
    Jul 30, 2013
    Posts:
    38
    So, I'm trying to see if I can make your FSM work but I'm getting a parsing error


    The error directs me to this line: public void TestState : IState

    As I said, I'm a noob and I can't seem to fix it all the { seems to be right

    There was also 3 errors in this bit of code:

    Code (CSharp):
    1. public interface IState
    2. {
    3.     public void Enter();
    4.     public void Execute();
    5.     public void Exit();
    6. }
    But I just removed the public in front of each function and the errors disappear.
    Thanks!
     
  5. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Oops - should be public class TestState : IState
     
  6. Rodlaiz

    Rodlaiz

    Joined:
    Jul 30, 2013
    Posts:
    38
    All right! is working now, thanks!
     
  7. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    The biggest advantage to this is the potential re-usability as states become more complex. For instance - we have a MoveState which calculates a path to a destination and moves the unit there. Units can also enter buildings, so our EnterBuildingState inherits from MoveState and just sets the destination to the entrance of the building then does some extra bits once he gets there. Same goes for ExitBuildingState and Enter/ExitTransportState.
     
    Chmyke likes this.
  8. jtsmith1287

    jtsmith1287

    Joined:
    Aug 3, 2014
    Posts:
    787
    Kelso is defintely correct. Class based states are going to get you more bang for you buck and as your project grows you'll be thanking yourself for doing it.

    That being said, it doesn't mean using enums is a bad approach. Every project is different. If you're writing something you know will be small then simple states are just fine. I just wrote a game in a week. Was kind of a exercise for myself in prioritization. Anyway, I have a single enemy that can be doing a couple different things. Either attacking, or retreating, being aggressive or acting intelligently. Some of the cases share similar code so a few of my switch statement look like this:

    Code (CSharp):
    1. switch (state) {
    2.     case myStateOne:
    3.     case myStateTwo:
    4.         DoBlah():
    5.         break;
    6. }
    In my update method I have calls to things like GetHeading, GetTarget, CanShoot, and each one of those checks the state. Arguably I could have written a class based system but I would have a lot of repeated code since there's a lot of overlap between some states.

    So it's very, very project dependent. If you're just trying to make a simple object have more than one behaviour in a puzzle platformer, enums are probably fine. Are you making an RTS or RPG or something with more depth, then you'd be a fool to not use a class based approach.

    Know your project! Know what you want to make, what the end goal is. This is vital. If you just make it up as you go you'll fail the moment things start getting complicated. "I wish I hadn't planned this so thoroughly" said no one ever!
     
    twitchfactor, maya-papaya and NMJ_GD like this.
  9. Rodlaiz

    Rodlaiz

    Joined:
    Jul 30, 2013
    Posts:
    38
    Thank you very much jtsmith1287 Actually this is me very first game that I'm trying to develop so I should definitely want to keep it simple and it's true that I grasp a bit better the simple example I've posted it over the example of Kelso which I get lost in some parts, even though I kinda "feel" is better and has better performance. I'll keep digging a bit more about FSM, excercise and try to decide which direction I should go. If you have a more complete example of your FSM and you want to share it it will be greatly appreciated :)
     
  10. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
  11. Rodlaiz

    Rodlaiz

    Joined:
    Jul 30, 2013
    Posts:
    38
  12. Munchy2007

    Munchy2007

    Joined:
    Jun 16, 2013
    Posts:
    1,735
    The code as presented may not be polished enough for production, but the principles demonstrated are pretty sound.

    You could do a lot worse than basing your FSM on the method presented in that tutorial.
     
    Nigey likes this.
  13. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    I have to agree with that. The abstraction he uses is a solid enough approach. You can expand and edit as you want.
     
  14. J0hn4n

    J0hn4n

    Joined:
    May 11, 2015
    Posts:
    24
    well for me its more easier use delegates to execute my states . every state haves a list of trasitions and if some of those are true i just execute the delegate and set the current state to the state that references the trasition.
     
    Rodolinc likes this.
  15. J0hn4n

    J0hn4n

    Joined:
    May 11, 2015
    Posts:
    24
    i think you can just simplify your state to moveState, animateState, IdleState etc

    when the character are in moveState checks if needs play some animations if that so go to animeState and play("opendoor"). i think its more redeable that just making a state for that.
     
  16. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    We can't. Trust me :)

    I don't understand what you're trying to explain.
     
  17. J0hn4n

    J0hn4n

    Joined:
    May 11, 2015
    Posts:
    24
    Well i mean that there is some states where you can reuse the states like a simple "animateState" that just plays a animation, if the animation ends the state ends and go back to the default state like Idle. Actually find more suitable have a generic state to just animate. Something like drink cooffe State and smoke cigar state or also Heal/eat state can go to at a animate state.
     
  18. J0hn4n

    J0hn4n

    Joined:
    May 11, 2015
    Posts:
    24
    This can make the code more modular and just hardcode things like jump , move, run, death(also can go to animateState but i dunno).
     
  19. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Yes - we have an animation controller that states can use to play specific animations if they need to. We don't use Mechanim so they aren't states themselves. Tying animation state into an actor's FSM as discrete states seems silly to me.

    What you're describing are actions that an entity performs. The animation is only a small portion of that. If you're talking about an animation state that is in control of what FSM state an entity enters and exits then...I guess you can do that. I wouldn't....
     
  20. forcepusher

    forcepusher

    Joined:
    Jun 25, 2012
    Posts:
    227
  21. J0hn4n

    J0hn4n

    Joined:
    May 11, 2015
    Posts:
    24
  22. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    The approach is very different and my implementation is based on a C++ one outside the context of Unity. With that said, if you want drag-and-drop states that can be configured in the Inspector, then absolutely go the route of that tutorial. @superpig is a much better programmer than I :)
     
  23. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    Supposing you're wanting drag and drop states, it might be worth taking a gander at behaviour trees. Just to double check they're not what you're after. They could well be overkill, but I use them and find it super helpful. A free one is https://assetstore.unity.com/packages/tools/ai/panda-bt-free-33057. The guy who makes it is pretty nice, and the visual debugging is a nice touch to deal with the 'human side' of AI.
     
    reggie5538 and KelsoMRK like this.
  24. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,824
    G'day, I've been looking at this thread a bit and it's been really helpful, so thanks!
    I'm using @KelsoMRK 's setup and it's working really well, but every now and then I get stuck, where I almost feel an action shouldn't be a whole new state. Say if you had a character that was running along and you wanted it to shoot at the same time. In that case would you create some sort of sub state?
    I'm worried that might over complicate the whole thing. Does anyone have any advice on how you might approach that?
     
    KelsoMRK likes this.
  25. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    We have states like AttackMove to handle those types of situations. If you find things getting overly complicated where you're blending more than 2 actions then a finite state machine might not be the best approach.
     
  26. EvilDingo

    EvilDingo

    Joined:
    May 7, 2011
    Posts:
    190
    How do you handle references to MonoBehaviours in the scene? If I want my state to access a particular element in the scene? Since the state itself isn't a MonoBehaviour and is instantiated by the state machine. Right now I have it accessing a static reference of whatever it needs to Execute, but that seems like a bad practice.
     
  27. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Depends on what it is you're trying to access. Most of the time you can pass it to the new state instance from whatever code is initiating the transition. So if you're making an attack state
    Code (csharp):
    1.  
    2. public class AttackState
    3. {
    4.     public AttackState(MonoBehaviour owner, MonoBehaviour target)
    5.     {
    6.          this.owner = owner;
    7.          this.target = target;
    8.     }
    9. }
    10.  
    And wherever you're doing your target acquisition you just pass it to the new state via the constructor.
     
  28. lo-94

    lo-94

    Joined:
    Nov 1, 2013
    Posts:
    282
    This might help you
    http://gameprogrammingpatterns.com/state.html

    This guys' book is like the holy grail of programming patterns for game development
    The whole thing is available for free
    I personally bought a hard copy to support and cause I like reading paperback though, and how he writes is a good couch read :p
     
    palex-nx and pepperopigeon like this.
  29. INDAX

    INDAX

    Joined:
    Jun 29, 2014
    Posts:
    6
    I'm using this to handle my unit's states too. How would you handle the case where you need to use a variant of AttackState? For example a swordman and a bowman can both use ChangeState(new MoveState(this)) to move. Then when it's time for them to attack, bowman and sword use different AttackState. How would you make it possible to make same MoveState transit to different States? or should I use different MoveState for different units in the first place?
     
  30. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Most likely you'd need some sort of configuration on the entity itself. We do this to allow for exactly what you're talking about - an entity uses a custom state in place of the default one.

    We don't do anything fancy - we just check inside the attack state's Enter method to see if the entity has a custom state for attack and if they do we just transition to it immediately.

    With that said - making your attack state more robust is also an option. If the only difference between the swordman and the bowman is the range they attack from then build weapon range into the entity and handle that in the attack state. If they target enemies differently then abstract your targetting logic into something that lives on the entity instead of inside the attack state.

    We do all of the above and it works quite well. We have a unit that attacks from range and specifically targets enemies with lots of other enemies around (splash damage). It doesn't use a custom attack state. We have a controller unit that spawns other units which attack for it - that uses a custom attack state.
     
  31. angel_starr_mauas

    angel_starr_mauas

    Joined:
    May 11, 2018
    Posts:
    8
    Your example is beautiful, and indeed reusable, and elegant, and in terms of being "proper" - it wins.
    But, I have worked in programming > 20years. Design patters, unit-testing, or test-driven development are not always the best thing to employ.
    I think the rules for deciding anywhere on the graph between an implementation which is simple and robust (the original example), and one that is scalable, reusable, and outright lovely (yours) - depends on a few key issues:
    1) is there *AMPLE* time for development, and no tight schedules?
    2) is the game a large-scale project, or a small one? is this a yearly project, or an MVP? a large game, or a small one?
    3) will the state machine be employed by many varying objects, and tests of the game will be convoluted?

    There are other factors - but the considerations remain similar in my experience.
    Sometimes - the best example is the original.
    Sometimes - you don't need a spaceship to go buy cookies at the grocery store.
    And sometimes - the other way around: you come unprepared, and you end up wearing shorts on mars.

    So it all depends IMHO on what is trying to be accomplished? Dictionary example, museum-style: yours wins.
    Practical "let's do this" and it will be instantly readable by new developers as we all work on this small-medium scale game which may or may not have funding to surpass the 0.5 year mark before the company changes direction, or real money is brought in? His design wins for sure.
     
    DunderStorm likes this.
  32. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Agree to disagree. I don't find a giant switch statement to be robust by any measure nor do I find the standard FSM pattern I presented to be difficult to read. If you're a developer trying to get a job at my company and you can't read that implementation then you aren't going to be working at my company. I also didn't mention anything in regards to unit testing or test driven development so not sure why that got looped in? In terms of creating an MVP - MVPs become full products eventually so there's no use in implementing via an inferior method only to burn more time going back and doing it the "correct" way.

    If you've got 3-4 well defined, discrete "states" then sure it might make more sense to go with something like the original example, but I've found in my years of experience building software that things can quickly become more complicated than you anticipated - and this is especially true when making a game, where requirements aren't as baked as they are in the enterprise world.

    Ultimately, none of the context we're talking about was provided by the OP. They asked for a good, clean FSM implementation which was provided and -explained- (because they also mentioned they were new to programming, and it appears that they grasped the concept and understood the code; so mission accomplished.)
     
    sapitz and Suddoha like this.
  33. palex-nx

    palex-nx

    Joined:
    Jul 23, 2018
    Posts:
    1,748
    Well, I doubt state machines worth the efforts you suggest to put in making them to good architecture, splitting states by classes, etc. I've implemented mario like character for 2d platformer using switches and I had only one in my state machine. It was like a

    Code (CSharp):
    1. private void choose(CharacterState state, Action onIdle = null, Action onRunning = null, ...) {
    2. switch(state) {
    3.   case CharacterStates.Idle: onIdle?.Invoke(); break;
    4.   case CharacterStates.Running: onRunning?.Invoke(); break;
    5.   ...
    6.   default: Debug.LogError($"Missing handler for character state {state}!");
    7. }
    8. }
    and typical signal

    Code (CSharp):
    1. public void Jump() {
    2.     choose(_state,
    3.         onIdle: IdleJump,
    4.         onRunning: RunnigJump(),
    5.         ...);
    6. }
    For FSMs having about 10-20 states or less thats fine. It robust, easy to read, support and modify. It has no unnecessay complexity. On the other hand, if your FSM have more than 20 states, layers, stacked states, sub-statemachines, and requires advanced oop design, probably you need to redesign your game model instead, split that huge fsm into simple parts, or use other solutions than FSM because FSMs do not scales good. Unity itself going away from fsms in animator to a playable graphs because of this.
     
    Last edited: Oct 31, 2019
  34. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    That pattern, with 20 states, means your choose method would have 21 arguments, 20 of which are optional. And then you have to know which actions to pass in based on the state? And every time you add a new one you have to go back to all your other methods that call choose and double check that they don't need to handle the case of the new state you made? Yikes, no thank you.

    It's also misleading to say something like 'split that huge fsm into simple parts'. The FSM itself is not huge. It doesn't care how many states you have. It just handles transitioning between an active and new state and then executing it. Your FSM is huge because it's tightly coupled to the states it's managing. The FSM I presented is used in Fractured State for units, UI, and tooling. Completely different states mind you, but the machine is the same.

    Certainly there are times when an FSM isn't the right solution - I don't think anyone is debating that point. My point is that the pattern we used worked very well for us in a game with a large amount of complexity. It gave us an enormous amount of debugging power and we could continue to build out the game without ever having to go back and refactor existing states once they were working correctly nor did we have to modify the FSM implementation itself. (And in fact I was curious so I looked at the file history and it was created in 2013, modified 3 months later when we moved states out of singletons and into instances, then once again in 2015 because we changed how we clean up dead units, and then never again. 17 lines of code that were touched 3 times in 6 years and is leveraged across 3 discrete systems in the game.)
     
    Last edited: Oct 31, 2019
  35. palex-nx

    palex-nx

    Joined:
    Jul 23, 2018
    Posts:
    1,748
    Yes, that's right. In c# this leads to very nice and clear syntax

    Code (CSharp):
    1. choose(state,
    2.     <if stateName>: <then actionName>,
    3.     ...);
    optional parameters are, well, optional and may be skipped.

    Of course no, you don't need to. That's what naming conventions are about. Look, if you have states Run and Jump, then Jump signal in state Run will call method named RunJump and nothing else. If you want to handle Update or CollisionEnter specifically for some state, it will be named <StateName>Update. So you don't need to remember all this because when you type in new code for signal or transition or event handler you just see on the screen all required keywords and names right there, you dont even need to scroll or switch tabs in the code editor.

    Not all. I have FSM chart and I know exactly wich transitions/states i've added and what methods to modify. Naming convention + method name based IDE navigation makes it super easy. Just mark new transitions and states on your state chart, put it in front of yourself, and type in the code where appopriate. It's very obvious. Like a 1st grade student problem, no harder.

    Mario-like character i was making was bout 600 lines of code. Not much for a character controller, I think. Also if you move methods to controller and leave only FSM in that class it will be less than 200, wich is very small. You saying we need a bunch of decoupled classes and universal FSM solution for this, I say it's like using a space station to nail a nail. Waste of time and resources.

    It is very simple FSM. Does it have features like go back to previous state, layers, weights, conditional execution, substatemachine, etc? or It is just like a dictionary of dictionary of actions + interface IState {void enter(); void exit(); }?
     
    Last edited: Nov 1, 2019
  36. palex-nx

    palex-nx

    Joined:
    Jul 23, 2018
    Posts:
    1,748
    What I want to say is you just splitted some code into several files but amount of work to create a playable character or any game behavior of same complexity remains at least the same as in my case, if not more, because your system introducec multiple code files, wich makes things more complex. How do you preserve incapsulation of character state data between it's states, for instance? In my case I just store it in private field.
    Software architecture is a set of measures to make code easy to read and modify, and that's it. My fsm is just one function easy to use and code.Yes, it's new for every file because well, .Net generics are not good with enums, but in better languages my approach may be implemented simpler and faster than yours. The secret of OOP patterns is not to use patterns unless absolutely necccesary.
     
  37. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    No because these were not required for what we were doing. Nor was it mentioned by the OP in this thread.

    Depends on what the data is - no state data is stored on the entity. When the state is transitioned out it's data goes with it because it isn't in that state anymore.

    No I didn't. I said -

    The number of code files is not an indicator of software complexity. If you put your entire app in 1 file is it any less complex?

    So you're just relying on external documentation and your knowledge of the code? I'd rather the code just be self-documenting and easy to read, even for new folks coming into it cold. Also if you add a new state but don't pass any handler in for a particular action then nothing happens (unless I'm not reading it correctly). This might not be the desired behavior. Which is why I said that when you add a new state you need to go look at all your previous transitions to see if that new state should do something as part of that action.

    Not having to scroll or switch tabs is not a valid measure of a good software pattern. Saying your code is easy to read because of the intellisense in your IDE is not a valid measure of a good software pattern.

    Ultimately, I don't know what you're trying to achieve with this discussion. You're offering a Mario-style character controller where the player can perform 2 actions as a comparison to a set of RTS units - it's apples and oranges. You're solution would be untenable and an unmitigated maintenance disaster in an RTS game. If it works for you, go forth and do great things.
     
    Last edited: Nov 4, 2019
    Aethenosity, Novack and xontik like this.
  38. mrCharli3

    mrCharli3

    Joined:
    Mar 22, 2017
    Posts:
    976
    @KelsoMRK Thanks for this, never thought of doing an FSM this way :) I can see this solving a lot of my problems!

    Do you by any chance have a neat way to compare states? ie.
    if(currentState == state_x)
     
    Last edited: Nov 9, 2019
  39. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Like a check if an entity is performing a particular action? Sure.

    Code (csharp):
    1. if (currentState is MoveState)
     
    unity_00esen00 and mrCharli3 like this.
  40. mrCharli3

    mrCharli3

    Joined:
    Mar 22, 2017
    Posts:
    976
    Is there any way to pass a Constant like that as a parameter to a function? i.e

    Code (CSharp):
    1. public bool IsInState(IState state)
    2.     {
    3.         return newState is currentState;
    4.     }
     
  41. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Off the top of my head, maybe something like this?
    Code (csharp):
    1.  
    2. public bool IsInState<T>(IState state) where T: IState
    3. {
    4.     return state is T;
    5. }
    6.  
    But that's a little goofy (technically if T is IState and so is state then this is always true).

    With that said, I'd be curious why you have to check something like this often enough to warrant a convenience method?
     
    mrCharli3 likes this.
  42. mrCharli3

    mrCharli3

    Joined:
    Mar 22, 2017
    Posts:
    976
    I don't, I was just curious if it was possible :) Since I had a vague memory of it being impossible in C#.
     
  43. Lughinu

    Lughinu

    Joined:
    Nov 3, 2016
    Posts:
    11
    instead of Interface I prefer an abstract class:

    Code (CSharp):
    1.  
    2. public abstract class StateBehavior
    3. {
    4.      
    5.     public virtual void OnEnter() { }
    6.     public virtual void OnUpdate() { }
    7.     public virtual void OnExit() { }
    8. }
    9.  
     
  44. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    For what reason?
     
    Simplexws likes this.
  45. Munchy2007

    Munchy2007

    Joined:
    Jun 16, 2013
    Posts:
    1,735
    Personally speaking, the only reason I might choose an abstract class over an interface would be when multiple implementations shared some common functionality.

    In that case, using an abstract class would mean I didn't need to specifically implement those methods in each thing that implemented the interface. Otherwise, my first choice would be an interface.
     
  46. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Contractually, you'd still want the state machine to accept an interface to handle cases where states do not share common implementations.
     
  47. Munchy2007

    Munchy2007

    Joined:
    Jun 16, 2013
    Posts:
    1,735
    I agree, those methods that have different implementations would be declared as abstract, and the common methods would have implementations in the abstract base class.
     
  48. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    We never had a need for abstract implementations. We would just dogfood less complex states to create more complex ones; like my previous example (from forever ago, man this thread has been long-living!) where we have a MoveState and an EnterStructureState where EnterStructureState derived from MoveState and just set its destination to the entrance of the structure.

    In either case I was simply responding to @Lughinu because an abstract class that doesn't actually implement any base behavior seems entirely pointless.
     
  49. Munchy2007

    Munchy2007

    Joined:
    Jun 16, 2013
    Posts:
    1,735
    Which was exactly the point I made in my first post today.
     
  50. Remi_Tribia

    Remi_Tribia

    Joined:
    Apr 29, 2015
    Posts:
    40
    About using an abstract class over an interface even if there's no implementation, on some project I used abstract class to be able to populate List in the inspector, so every monobehaviour inheriting from it could be added to the List<AbstractBaseClass>. It's a ugly hack but it works...