If this helps, here is my state controller:
extends Node2D
@onready var human: CharacterBody2D = get_parent()
var current_state_controller: Node
var states_dict: Dictionary = {}
var current_state_name: String = ""
func _ready() -> void:
for state_controller in get_children():
var state_name: String = state_controller.name
states_dict[state_name] = state_controller
func change_state(new_state_name: String, current_velocity: Vector2 = Vector2.ZERO) -> void:
if current_state_name == "Dead":
return
if new_state_name == current_state_name:
return
if is_instance_valid(current_state_controller):
current_state_controller.exit_state()
current_state_name = new_state_name
current_state_controller = states_dict[current_state_name]
current_state_controller.enter_state(current_velocity)
I use the same thing with the behaviour manager only ‘current_state_controller’ becomes ‘current_behaviour_controller’ etc.
As for the actual ‘intelligence’, the decision making, that actually turns out to be relatively straight forward. You pass any alerts (signals) from your raycasts or area2Ds etc to the behaviour manager, that passes them onto the current behaviour. So now you know, say, your character is hunting and it has seen an animal. What type of animal is it? Is it a bear, change behaviour to run_away, is it a rabbit, change state to ranged attack etc.
I use inheritance here, so all my behaviours extend the same behaviour_class, so all calls from alerts are in the base class, and overwritten by the behaviour itself. It is surprising how few actual alerts you have to deal with sometimes.
The only thing to watch out for is the planning. Otherwise you get trapped moving code from states to behaviours and then back to states, or splitting behaviours when you realise they are too general etc.
Root Node
- BehaviourManager
- BehaviourStateManager
- Hunting
- Buildings
- ....
I don’t know about the code patterns. I suppose it is a state-pattern, or perhaps a startegy-pattern, with observer-patterns for the detectors. I don’t think that code patterns have really ever helped me with coding, apart from perhaps thinking about things in hindsight.
Anyway, best of luck with it all.
PS I like to keep the actual state_managers purely for state switching and state referencing. All the ‘gubbins’ for each NPC is their own behaviour manager. This is where all the ‘ifs’ and match statements and stat references are kept.