Creating a State Machine when you haven't decided every behaviour yet! - Rusted Treetops DevBlog - GameDev.net

Creating a State Machine when you haven't decided every behaviour yet!

Published November 24, 2022
Advertisement

I am making a Game Design Document, I promise!
But I wonder if my eagerness to start writing some code is a contributing factor to making the code more dynamic and reusable.
I'm no expert, but I feel it is.

Replaceable and reusable

You see, I already knew I wanted to have a State Machine, and I am making one now for the NPCs (neutral, friends, enemies - they'll all at least derive from the NPCStateMachine).
And I knew there were going to be states. And making this modular is not really that hard.
However, I am also making certain triggers (sensors) that will notice if something enters it (it could be a sound, something moving etc).

I know that I want the code to work in all state machines, and each state should handle the trigger differently.
Sometimes, we may not want to do anything or handle it at all.

So, I know that the state should do something. But I don't know what, and I don't even know what kind of entity will trigger this yet.
In comes - STRATEGY PATTERN.

By assigning a strategy pattern to certain behaviours (some to a state machine, some to a state), I get a very modular system that's easy to replace and easy to reuse.
Strategies can be shared for both a Friendly Idle state and an Enemy's Idle State. And two enemies that otherwise use the same prefab can have different strategies for movement, handling triggers etc.

It is now very easy to create a drunk character without actually re-writing any code. I just have to write a new NavMeshMovementBehaviour (I think that's what I called it). maybe a new HandleSensorTriggerBehaviour etc.
A new TalkBehaviour? The options are endless, really. And the best part? I don't have to know anything about which state should use which behaviour or which possible behaviours I should have in my game yet.

I can still make the StateMachine, because the StateMachine will simply call the current state's “HandleTrigger” method. And each behaviour will handle this call in it's own way.

Monobehaviour or not?

I have been going back and forth on whether or not I should use Monobehaviours for this.
See, the strategy pattern is best implemented using Interfaces. And Interfaces can't be monobehaviours.
But by having Monobehaviours, I can simply attach the strategies onto fields in the inspector.

Now, I could have written a custom editor inspector that would handle interfaces, but honestly - I'm not that good. I dropped out of university to pursue money.
Then I lost all my money and started pursuing filmmaking. I won awards. Then I lost all my money again. But I'm still making movies, so I'm happy.
But I'm sidetracking.

What I ended up with was using an “Interface” that inherits from Monobehaviour and is actually just an abstract class.
The child classes inherit from this “Interface”, and everything can be put into inspectors and suddenly I have a very dynamic system.

This isn't the only way. May not even be the best way.
But what I love about game development is the same thing I love about making movies - taking on huge challenges, breaking them down into smaller pieces and finding solutions to problems.

God, we're lucky to be alive in this day and age!

1 likes 2 comments

Comments

Alberth

More dynamic and reusable code has its costs too, you're writing more generic solutions than you need. That creates additional complexity in writing the code and in execution. It may also slow down its execution (which is usually not that much of a problem unless you find it is).

You seems to think in terms of patterns, please keep in mind there is also always code that doesn't fit in a pattern. That's where things become complicated.

As an example in the NPC area, suppose that two NPCs must co-operate in some action and start at the exact same moment. Then you suddenly get a synchronization requirement between state machines (if A is in state X and B is in state Y, then they must jump to state X2 and Y2 respectively at the same moment). Such a thing isn't covered in standard state machine patterns, and may cause havoc in the design.

November 26, 2022 08:07 PM
AndyPett

@Alberth You make some valid points - and thanks for reading! ?
Of course, I wrote this in a bit of a hurry and didn't give all the details. So of course, if EVERYTHING is going to be dynamic and fit into patterns, it wouldn't be doable.

So the specifics is that I have for example an IHandleThreatStrategy. This is only called if a certain TriggerEvent is happening.
By making this strategy pattern, I can have NPC's and enemies react to the same event, but react to it differently without cluttering the code with a bunch of if-statements (or switch statements).

I can even make certain select NPC's react in a very specific way if I wanted.
This game is very simple compared to “Open World” games, and could probably be compared more to games like “Lost in Random”. So the cost of this solution will not be noticable as there will only be a select number of NPC's at a time.

As for your example with the synchronized action, this would not be the place where I would have the strategy pattern.
It sounds like this is a place for having A and/or B go into a WaitingFor-state and once both were in the WaitingFor-state, they would both switch to the StartAction-state (or something - I didn't think through the details, just wanted to clarify that this is not where strategy pattern would be suitable in my opinion).

And again, I'm by no means an expert in the field, and there might be far better solutions that I don't know about. But for my game, this will work and will make it possible to create a more interesting game without cluttering code. ?

November 26, 2022 08:19 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement