Creating State Machine - Considerations and interfaces vs empty abstract classes

This is in response to the Creating Our State Machine video.

First thing is pretty easy - the challenge said to create a void SwitchState method. I think that its beneficial to note that this should be a public method. As I am not aware of what the author’s direction was, I had to ensure I watched everything he did afterward to switch the private void to a public void.

Second thing - this is more a philosophical bend on abstract classes. An abstract State class that only houses three abstract methods and does nothing else to me is better suited as an interface. In my project I called it IGameState.

I have worked several places in my career where a code review will get dinged for empty abstract classes like this. Abstract classes are (again opinion-based) best served as places where duplicated code gets consolidated into to adhere to the SINGLE-USE principal (SOLID). In the instance where there is no code, an Interface will also ensure that any classes implementing it will have the public methods defined in the interface.

Of course - both will work just fine but as a best practice I wanted to put the interface forward instead of the abstract State class which then has an abstract other State class which then has a concrete state class added to it. That type of inheritance chain can get unwieldly, especially during maintenance phase of a project.

    public abstract class StateMachine : MonoBehaviour
    {
        private IGameState _currentState;

        private void Update()
        {
            _currentState?.Tick(Time.deltaTime);                
        }

        public void SwitchState(IGameState newState)
        {
            _currentState?.Exit();
            _currentState = newState;
            _currentState?.Enter();
        }
    }
1 Like

@Chris_Nye I’d respectfully disagree - in the literature the distinction between an interface and abstract class philosophically is the difference between composition and inheritance. The number of members in a class and whether or not a member has a default implementation doesn’t quite attack the heart of the question.

The way to decide which is most appropriate is to ask which concept is true:

  • “MovementState is a type of state”
  • “MovementState has a state in it”

I would say the first is more accurate.

I think it comes down to standards, which will differ from person to person and team to team.

Your answer is correct. You can implement it the way it is implemented in the class, and its fine. In fact, I agree with your assessment on composition vs inheritance.

However, the three levels of inheritance present in the course will also get bounced in many code reviews because it moves to three levels of inheritance and is more complex than what many teams will feel appropriate (what they will cite as violating clean code standards).

There is no right or wrong answer - like in many things with development - just opinions and ultimately whoever is the overall architect gets to decide those rules.

However - thank you for bringing up a great comparison and showing the composition vs inheritance. I am so used to having to adhere to one level of inheritance in many studios in the industry on a base class regardless of composition vs inheritance that it become standard to me, which isn’t always the only or best way to resolve an issue.

Below is an article from a google engineer. This is something that has been hammered into me for the past five or six years as well from several teams, to include Microsoft teams, so its where my brain defaults to.

This gets short-handed in a lot of places to “if you can avoid inheritance do so” but thats not always the correct answer. It again lands on whoever the architect of the project is. I have forwarded this conversation on to my own team and peers for conversation.

There is also one Major Factor to consider when considering the “Prefer Composition rather then Inheritance” is this is a Unity Project. Unity by default does not Serialize Interfaces meaning that they will not show up in the inspector, yes there are ways around this by creating a system that does this. If you are going to have things that can potentially be used in the Inspector you should prefer inheritance to composition, meaning skip the extra composition step. Now there are instances that you would use composition within Unity. I believe that this is not one of them.

If we know that there is a potential for this code to be used outside of Unity.
A better solution would to have an Abstract Node that implements a Node Interface. Have the abstract node have have virtual methods that do nothing or maybe dose some sort of basic set up like taking in a context and caching that context. This way you are not forcing inherited states to Implement Methods that it may not use. I can see cases where there might be a state that doesn’t do anything when it Enters or Exits the state. Of course there should also be a State Machine Interface as well. Doing it this way allows for good mix and allows for some of the code to become a library that you can use in other systems. The benefit of doing it this way would allow for the creation of systems that can be used outside of Unity, like a web based system that has data that can be shared between the web based and the Unity Game.

Thats an interesting perspective. I’ve been using Unity for about a decade now and I’ve never heard anyone mention that before (that you should avoid interfacing if its going to be in the inspector) - so thank you for providing that perspective.

The last couple of projects I’ve had to work on we had to follow interface protocol, and both of those games did not expose their code to the outside; it was a best practice brought in from other application development.

Certainly one of the reasons I post on these forums as well as others though is to uncover those situations or perspectives that I had never considered simply because it had never been brought up before - and one can get tunneled into how they do things like I am simply from doing it a prescribed way that I have to use in code reviews to pass my code through into production that you never stop and think that somewhere above you / me there is an architect who made the decision to put the rule in place in the first place - best practice or not.

So again thank you for your perspective, I appreciate it. :slight_smile: I’m glad that these forums exist for this type of exchange to occur.

The interface protocol is not a bad best practice to use, I just believe that when working in Unity you have to consider Unity Engine’s workflow. When it comes to interfaces they are not by default supported in the inspector so we as programmers have to be creative and specific on how we overcome some of these barriers. When it comes to setting values in the Inspector you have to write custom code that allows us to set things in the inspector. Use the Interface, but in Unity’s specific implementation of that interface restrict the variables to be types that are expose-able in the Inspector, even if those types themselves implement an interface. the other work around is to write custom “Editor” that can be used to expose Interface Types to the Inspector, both solutions are workable and still follow the best practice guide lines. I am sure that must companies that produce games probably already have custom Property drawers that do this.

When I was a beginner this concept was the biggest road block for me to using interfaces I just thought it was one of those concepts when all you have is a Hammer everything is a nail. It wasn’t until I saw a talk by Infallible Code, Advanced Project Setup in Unity, that showed how they did their development. This was when I actually got a full understanding of how to use them. And I was like hey all I had was a Hammer, now I have a screw driver.

Yeah you are bringing up good points. For people that are using the Unity Engine workflow, the way that I have done it is probably not going to be the way they want to do it. I don’t use a lot of the editor, I prefer code (thats not saying I never use the editor or serializable values, its just not something I do unless its something I REALLY want to expose).

With the newer attributes, you can now expose public properties (get;set;) to the editor and I use those alongside the interfaces I work in to allow me to use consolidated objects that do not repeat code and where I can just pass interfaces around to do the job.

One thing I have been blessed with is a career that has let me work alongside many game studios, from xbox studios on down, and the one thing I have noticed is that every game engine and every implementation of a game engine (unreal, unity, etc) are always fairly different, but there are reasons why those particular game devs chose the route that they chose, and there are 100 ways to solve any given problem many times.

Thank you for sharing the link, I will check it out.

1 Like

You are welcome. You bring up a good point about different studios using different engines. I have also seen a Studio switch the Engine they used, not mid-game, at least that I am aware of. But using interfaces for the main code and saying this is a contract it is a simple hey we are using this cool new engine now and we want our awesome game to be built using it, You already have all the code working all you need to do is implement the interfaces in the new engine, way easier then having to go back and rewrite your base code.

I use to be the same way with the Inspector, I found that fine tuning or testing new feature, i.e this new weapon exposing those in the Inspector was way quicker then doing it by code.

I love the newer attributes some of the things that Unity has need for a long time, [field: SerializeField] public int MaxHealth { get; private set; } so we don’t have to have a private baking field.

I always enjoy seeing how different people solve different problems, some times it’s why in the world did they take the long way around and other times it’s wow that’s an interesting solution I like that I like I will try it out and see how it works for me.

1 Like

For sure! And yes thats exactly why I use interfaces as much as I do :smiley:

I am the same way - I like to see the different ways to solve a problem because often mine is not the best, and there’s always a lot you can learn even if you’ve been doing it for 30 years.

As a Teaching Assistant, I’m often asked why an instructor chose to do [solution A] when he could have done [solution B] or even [solution C]. Often, the real reason is that it’s just the way that the instructor chose. There is almost nothing in Unity that can’t be accomplished 5 other ways than the way it’s presented. I’ve seen huge debates over the years over why one path is better than the other. Usually both sides have very sound reasons that apply to their specific situation. Could be a preference… I abhor Singletons, for example, and go out of my way to avoid them. Several of our courses use Singletons, however, and while Singletons aren’t right for my personal coding style, I’m not going to say they’re not right for another instructor’s coding style.

I often answer “There are many paths to the same destination”.

4 Likes

I am with you about the Singletons, mainly because it requires a Component to do more then one thing which breaks S.O.L.I.D. principles, yes you can break them as long as you know that you are breaking them and know why you are doing so. I might use them but I will do everything I can to avoid the Singleton Game Component, as I fell they are just messy.

I also know that the answer to this question is because that was the way I learned, or that’s how I solved it and it works so that is the way I have been doing it for years. Sometimes someone comes along and presents a solution that just makes sense that you don’t think about because you have the tunnel vision going on, walk away and come back or get a second set of eyes that says why did you do that.

As far as this goes I use to lean more towards the inheritance then the composition of things when it comes to doing things in Unity, I use to roll my eyes when I saw a project that was nothing but interfaces, until I saw the talk by Infallible Code.

The beauty of Interfaces is that they are a Contract that says anything that implements this interface must provide these properties and methods which we can inherit from multiple interfaces but we can only inherit from one class. The “is a” versus “has a” debut is one of those that

I know I have change my perspective on using interfaces and composition more in my Unity projects over inheritance.

Thats one reason I don’t have my githubs public lol. Even in the software industry, thats how code reviews go. It can be very frustrating.

I hear you on the singletons. I still use them but I know in a lot of code reviews you will get dinged for using them ‘for reasons’ (usually the reviewer just doesn’t like them or heard you shouldn’t like them so they don’t like them) - even though they can be useful if used properly. I don’t go out of my way to use them though, and often I can find a way around them, but I have a handful of apps I’ve written or been a part of that used them to good effect.

They are a tool in a bag of many tools - and so many code reviews or public code comments are exactly that - style critiques :smiley:

You guys are great - thank you for the good conversation. This is something I’d like to see more of.

1 Like

I actually use both composition and inheritance quite liberally… as to me, they each have distinct purposes.

If I have no base code that I want all objects of this type to have access to, I’ll generally go with an interface. Interfaces don’t implement code, only contract it, and interfaces are more portable, meaning I could put the same interface on completely different classes and still grab them up with FindObjectOfType.

If, however, I’m going to have some code that I want all implementations to have access to, then an abstract class is generally the best answer. For example, I put a method in the PlayerBaseState class that crossfades animations, taking in just the parameter of the state hash.

protected void CrossFadeInFixedTime(int state)
{
    stateMachine.Animator.CrossFadeInFixedTime(state, stateMachine.CrossFadeDuration);
}

For this, an interface wouldn’t have done the trick, you’re stuck with inheritance, and I’ll always flag that as a reason to use an abstract base class.

2 Likes

I agree with you on this now. I use to avoid using anything as an interface that I wanted to possibly be assignable in the Inspector. For example working on an Inventory a weapon I might have done as and abstract class and then maybe have a separate abstract class for A magic based weapon, a separate abstract class for A ranged Weapon. Now I will probably use interfaces for those base types instead. Maybe even a combination of both depending on situation.

This is a wonderful guidepost for me on inheritance vs interfaces

Also, I was trying to figure out how to stop entering cross fade in fixed time everywhere.

This is such a simple solution/brilliant solution.

Fantastic as always Brian

1 Like

Privacy & Terms