Behavior
Behaviors helps NPCs and Enemies to work with a limited set of states, this gives flexibility on controlling how they act in the world. It is basically a state machine within a state machine.
How behaviors work:
Behaviors are managed by the class BehaviorManager
. Each behavior contains a StateManager
with a unique set of states for the current behavior. It can be used to control state flow for NPCs and Enemies.
BehaviorManager
The BehaviorManager
is initialized by the owner, then it initializes the StateMachine
which then initializes all the states passing the reference of the owner responsible for all the states and behaviors.
init
It is called by the owner, initializing all the behaviors and states within each behavior. It then passes the reference of the owner & change to the initial state for all the behaviors.
change_behavior
It changes the current behavior, runs the exit() function from the previous behavior and then enter() function from the current behavior, acting as some sort of _ready() and exit_tree() for the states.
physics_process & process
It is fueled by the _physics_process & _process from the owner. It won't run, unless the behavior & state is active.
StateManager
The StateManager
works similarly to the BehaviorManager
, however it controls only states, instead of states & behaviors.
BaseBehavior
BaseBehavior
is the base class that any other base behavior can inherit.
Example: The BaseBehavior contains all the variables and functions necessary to work within the BehaviorManager. BaseBehavior
> EnemyBaseBehavior
> PouncerBehavior
BaseState
BaseState
is the base class that any other base state can inherit.
Example: The BaseState contains all the variables and functions necessary to work within the StateManager. BaseState
> EnemyBaseState
> IdleState
Flow

Adding a new base behavior:
To add a new base behavior you need to go through two steps.
1) Add a base behavior script extending BaseBehavior
in res://src/AI/BehaviorManager/BaseBehaviors/
and name it NameBaseBehavior.gd, change the "Name" to whatever name your behavior has.
2) Each base behavior must have a new init function to pass the reference of the context. Behaviors must never extend the BaseBehavior
directly.
class_name EnemyBaseBehavior
extends BaseBehavior
var enemy
func init(_context) -> void:
enemy = _context
state_manager.init(enemy)
func enter() -> void:
.enter()
func exit() -> void:
.exit()
func process(_delta: float) -> void:
pass
func physics_process(_delta: float) -> void:
pass
Adding a new behavior:
To add a new behavior you need to go through six steps.
1) Add a behavior script extending the base behavior you want in res://src/AI/BehaviorManager/Behaviors/
and name it NameBehavior.gd, change the "Name" to whatever name your behavior has.
2) Add a new Node scene with the behavior script just created in res://src/AI/BehaviorManager/Behaviors/
.
3) To the recently created behavior scene, create a new StateManager
Node as a child and attach the StateManager.gd
to it.
4) Add all the necessary states as children of the StateManager
node & attach the state script to each.
5) Set the Starting State NodePath parameter of the StateManager
to the desired starting state you want for the behavior.
6) Don't forget to initialize the BehaviorManager
from the entity you will be using in the _ready function.
class_name PouncerBehavior
extends EnemyBaseBehavior
export var proximity_distance:int = 80
func enter() -> void:
.enter()
func exit() -> void:
.exit()
func process(_delta: float) -> void:
var state = state_manager.current_state.name
if state != "Telegraph" and state != "Attack":
if context.sight_target:
var distance = context.global_position.distance_to(context.sight_target.global_position)
if distance <= proximity_distance:
context.change_state("Telegraph")
func physics_process(_delta: float) -> void:
pass
Adding a new base state:
To add a new state you need to go through two steps.
1) Add a base state script extending BaseState
in res://src/AI/BehaviorManager/BaseStates/
and name it NameBaseState.gd, change the "Name" to whatever name your base state has.
2) Each base state must have a new init function to pass the reference of the context. States must never extend the BaseState
directly.
class_name EnemyBaseState
extends BaseState
var enemy:Enemy
export var animation_name:String
func init(_context):
enemy = _context
func enter() -> void:
enemy.animation_player.play(animation_name)
.enter()
func physics_process(_delta: float) -> void:
pass
func process(_delta: float) -> void:
pass
func exit() -> void:
.exit()
Adding a new state:
To add a new state you need to go through six steps.
1) Add a state script extending the base state you want in res://src/AI/BehaviorManager/States/
and name it NameState.gd, change the "Name" to whatever name your state has.
2) Add a new Node scene with the state script just created in res://src/AI/BehaviorManager/States/
.
#Idle State Example
extends EnemyBaseState
func enter() -> void:
.enter()
func exit() -> void:
.exit()
func process(_delta: float) -> void:
.process(_delta)
func physics_process(_delta: float) -> void:
.physics_process(_delta)