How can I create functional NPC AI for a 2D game

Godot Version

4.5.1

Question

How can I create functional NPC AI for a 2D game similar to the Kingdom series?

I’m new to Godot and this is my first experience with the engine. I’m trying to create functional NPC AI for a 2D game similar to the Kingdom series, but my attempts so far haven’t been successful. I’ve tried several approaches and even used ChatGPT, but I still can’t get it to work. How should I approach building this kind of AI?

Unfortanetly i dont know the kingdom series. Can you elaborate on what exactly the npc is supposed to do?

There are a few ways you can create AI in video games. If you dont want to implement your own system you can look into the beehave-plugin

This could be a solution, but I see in the GitHub issues that there are problems with the latest version of Godot.

About npcs:

  1. Each NPC has one primary job: to hunt, build, or farm.
  2. During the day, they do their jobs automatically without commands.
  3. At night, their behavior changes: commoners hide and flee, while archers and knights switch to a defensive mode.
  4. Archers automatically attack enemies from behind walls, making them the main line of defense at night.

If I were doing this I would aim for each NPC to have both a behaviour_manager and a state_manager.

The behaviour manager has states (called behaviours) and is very much like a state manager. When I write these I have them scan their children to create a dictionary of names: node_refs that I can then use in the change_state or change_behaviour functions. So both are very similar in structure. Now I can just share states and behaviours amongst NPCs. (The player of course does not need a behaviour manager, as that is the players job, and I tend to keep player states distinct and unique).

The behaviours control the states. Only the behaviour manager can change the state of an NPC. A single behaviour may use many states, but the NPC can be in only one state at a time and in one behaviour at a time. So lets say the behaviour is “bedtime”. The first state it will change to is walk_to_target (the target being decided by the behaviour as ‘home’ or ‘shelter’). When it reaches the target the state alerts the behaviour manager that alerts the current behaviour that the target has been reached. The behaviour then does whatever, is there a sleeping position, do I need to eat first before sleeping etc).

Your rules that the behaviour manager follows is the intelligence. Lets say it’s moving to a target and you see an enemy nearby, the behaviour manager is alerted and changes the behaviour state to ‘evade’. Once the distance is far enough perhaps it then changes to ‘hide’, ie move to a target, hide in target.

The NPC has various ‘detectors’ that alert the behaviour manager like “enemy_detected”, or “sun_setting”. The NPC will also have a stats_manager that will also alert the behaviour manager like “food_needed”, “rest_needed” etc. The behaviour manage does all the controls by responding to these alerts.

I sometimes have a third “mind” node (with similar states I call moods). The mind gets all the alerts and switches the behaviours. The mind keeps all the rules and depending on its mood, can behave in different ways. So if it is in a ‘fearful’ mood (injured or low health might cause this) then it is very unlikely to switch to an attack behaviour. This can get complicated though and needs careful planning, but done well produces some amazingly complex behaviours.

Anyway, I’ve typed enough. But that is the approach I would use. I hope I described it well enough to be of some use as a general direction. Even with just combining two state machines, behaviours and states, the outcome can be quite sophisticated.

2 Likes

Thank you for the advice! I’ll try to implement it this way. Previously, I started with the “wandering” state for NPCs but ended up getting completely stuck. When I added more than one NPC to the scene, problems began. And then more problems appeared when adding other states. Could you also suggest what patterns I should pay attention to? And when writing the managers, what should I definitely avoid to prevent future problems when there are more than 30 NPCs? And what anti-patterns should i avoid?

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.

1 Like

Thank you very much! It definitely will help

1 Like