I dont understand NODE state machines but can make enum ones super easy

Im quirte new yto the godot engine been playing with it for a few weeks. I want to make a state machine for my character controler i was able to make an enum based one but iv heard many people say multi-node/object is better. Iv tried to build one but there so confusing

Can i stick with enum or will i have to do node and if so pleace can i have some advice on how to make them

thank you

State machine is a generalized concept, or a “design pattern” in fancy OO speak. If you have a good understanding of the concept, you should be able to vary the implementation.

The gist is to branch the code depending on the state of one or more flags. This can be done in myriad ways, enums and nodes included.

Stick with what works for you. I’m using lambdas for my FSM.

Personally, I think working code is better than any specific way of doing something.

1 Like

Well i do to (like it all in one code)

I like how it split into function with my current code just so many people were telling me nodes is “the best way” im quite new to godot so the node was is just super confusing at the moment

1 Like

(Google translator)
It depends on the scope of your state machine.

I’m not an expert, but I prefer nodes for the following reasons:

  • I have a visual representation of my states.

  • My states have an associated script where I can keep only the code related to that state. Even variables that are only used in that state are only there. This makes it easier for me to clearly understand what I’m doing in that state. This is what I consider most important.

  • If your state machine is hierarchical, you could even see states within states (although I haven’t implemented that myself).

  • You initialize states from the parent, traversing through the children.

  • You can include other child nodes within the states themselves if they require any extra functionality.

  • I like to put the states in the state machine, which is the parent of the states, as exported variables. This way, to perform a state change, I use the variable “machine.idle_state” to indicate the new state to go to, without having to work anywhere with enumerations of state names or callables that represent the states.

To create a state machine with nodes, you would simply create a machine class that extends Node and a state object that also extends Node.

class_name StateMachine extends Node
…

class_name State extends Node
…

Then the script for the StateIdle node will extend State, or, if you use inheritance, from the StateInFloor node, for example.

Anyway, I recommend you start implementing your state machine as you see fit and only modify it as you see problems with your design.

(In this other thread today there is interesting information about state machines that you might like to see: https://forum.godotengine.org/t/godot-architecture-7-steps-for-more-flexible-extensible-testable-code-video/137595/2 )

1 Like

If you mean you like all your code in one file, then this is going to cause you problems down the road. A Node-based state machine, as @capitanlopez mentioned, is a good way to help you divide your code up.

A good first step might be to split your Enum state machine into another file and see ho to make that work first though.

Also, if you follow the link @capitanlopez posted (which is to a much longer post I made on state machines) there’s a link to my State Machine Plugin.

I’m very curious about your implementation.

1 Like

Assuming C# with the state machine as an actual class with methods with initialising the state, changing the state and so on. And then its just a matter of changing the state with a lambda expression but could be wrong.

Sounds like some sort of OOP heresy to me! :wink:

You know what they say about assuming. :smiley: I do not know if @that_duck is using C#. GDScript has lambdas too.

extends Button


func _ready() -> void:
	pressed.connect(func(): text = "BooYeah!")

Or with strict typing:

extends Button


func _ready() -> void:
	pressed.connect(func() -> void: text = "BooYeah!")

Hahaha!

So I dug up this article from a couple years ago on why Godot does not use ECS. In it was a section on how Godot’s OOP structure is intended to use Nodes.

Also, you might be interested in this discussion here: Open Source Tool Suite - Released In which @Ryn explains they’re basically making a game in Assembly with Godot taking care of drivers and display.

1 Like

@OriginalBadBoy I’m using GDScript for this.

class_name CharacterStates

enum States {ACTING, ATTACKING, FLEEING, IDLE, SEEKING}

var current_state := States.IDLE 

func current_state_string() -> String: 
	return States.keys()[current_state]
	

var acting = func() -> void: pass 
var attacking = func() -> void: pass 
var fleeing = func() -> void: pass 
var idle = func() -> void: pass 
var seeking = func() -> void: pass 

var stop_processing := false 

func set_state() -> void:
	if stop_processing: return
	
	match current_state:
		States.ACTING: acting.call()
		States.ATTACKING: attacking.call()
		States.FLEEING: fleeing.call()
		States.IDLE: idle.call()
		States.SEEKING: seeking.call()
		
	

Defined at the character level:

func _physics_process(_delta: float) -> void:
	choose_state()
	fsm.set_state()
	
func define_states() -> void:
	var idle = func() -> void: 
		look_at_player()
		wander()
		
	
	var fleeing = func() -> void: 
		look_away_player()
		evade()
		
	
	var attacking = func() -> void:
		attack()
		
	
	var seeking = func() -> void: 
		look_at_player()
		pursue()
	
	fsm.idle = idle 
	fsm.fleeing = fleeing
	fsm.attacking = attacking
	fsm.seeking = seeking
	character_state = fsm.States.IDLE
	
	
func choose_state() -> void: 
	var too_hurt: bool = attributes.health < attributes.max_health * .90
	
	match character_state: 
		fsm.States.IDLE: 
			if aware_of_enemy(): 
				character_state = fsm.States.SEEKING
			
		fsm.States.ATTACKING: 
			if too_hurt:
				character_state = fsm.States.FLEEING
				
			if not aware_of_enemy(): 
				character_state = fsm.States.IDLE
				
			
		fsm.States.FLEEING: 
			if not aware_of_enemy(): 
				character_state = fsm.States.IDLE 
			
		fsm.States.SEEKING: 
			if not aware_of_enemy(): 
				character_state = fsm.States.IDLE
			elif attack_stop_distance >= player.get_distance():
				character_state = fsm.States.ATTACKING
		_: 
			character_state = fsm.States.IDLE
			
2 Likes

Thanks for the mention, our next devlog video on YouTube will cover all the HOW’S and WHY we’re using assembly. It’ll also include a little demo play test of some of the mechanics for our new game all running in assembly.

I’ll post a link when it’s up.

1 Like

I first liked the Node based one as you can work on each state independently but I had a hard time debugging with it.

For example, I may have flipped a bool somewhere and if it causes problem then I’d have to look for it in all those scripts. Another example is when a certain state script is interfering with a general script and error only shows there’s a problem with a general script.

And this is only when I worked with 4-5 states, I can’t imagine me trying to debug with 10+. How do experienced people do it?

For now I am sticking with enum.

They don’t use state machines like that. The discrete state machine proves to be inadequate for modeling player state as soon as you need to go beyond the textbook idle/walk/run example. Even if you use its hierarchical or concurrent variants.

When processing more complex player state you typically need to branch on configurations of multiple switches while the state machine boxes you into branching on a single switch at a time.

If you force yourself to use discrete state machines in your prototypes, it’ll severely limit your game design options and nudge them towards simplistic well known mechanisms.

1 Like

We have lots of tricks and tools.

Giving each state a class_name will make the errors very clear as to which script it is coming from. It also helps to send the current state to output or logs.

I know exactly what state I’m in and what state I came from if the game crashes or the debugger halts execution.

Well it’s hard to say what you can do, but typically the way to handle this is to make things work, then refactor.

For example, I just decided to add a feature to create an AudioStreamPlayer2D to my states that if I attach a sound file, the player plays the sound. I implemented it for the JumpPlayerState, and then the SlidePlayerState. Then I realized that both were being used when the state was entered. I could see I was going to use it in the HurtCharacterState and DeathCharacterState too. So I added the common functionality to the base CharacterState - from which both inherit - and up until now had only a few lines of code.

So I tested it in the lower states, then moved it up.

That’s a good solution. Especially for simple states. Over time, as you outgrow it, you’ll know what isn’t working for you, and be able to create something that does work for you.

1 Like

Yea I won’t be doing any 3rd person parkour open world anytime soon so enum for player is good enough but I’m curious..

In term of a simple Godot player control with walk idle run , how’d you implement this technique? I’m assuming “switches” are not just ifs / bools.

“Switches” are your state variables that can be anything, bool flag, continuous values, a range of stepped values… And you branch however you like; ifs, callables, virtual calls…