Smart-StateMachine is a lightweight, goal-driven framework for designing AI behaviors in games and robotics. It is a modular system of goals, tactical-goals, and behavior-focused states designed to create, adaptive, and easy to debug AI—while staying scalable for complex projects. See this presentation for a non-technical introduction to Smart-StateMachine.
Smart-StateMachine is built around two core concepts of Goal-States and Behavior-States, you can define both goals and behavior states in one place using an enum
.
public enum State
{
// -------- Behavior States -------- //
Idle,
Rest,
Hide,
Flee,
Dead,
Alert,
Combat,
Patrol,
// -------- Goal States -------- //
RunForDearLife,
Guard
}
Behavior-States: are focused, actionable pieces of logic that direcly translates into AI agent actions e.g. patrol
, combat
. They can be powered by whatever AI model best fits the job—Utility AI, GOAP, or Behavior Trees, for example a patrol
state can be implemented using a Behavior tree.
Root
└── Sequence (Default: Patrol Loop)
├── Action: MoveTo(CurrentWaypoint)
├── Condition: AtWaypoint?
└── Action: SetNextWaypoint
Each state should define only one behavior type, for example idle
, patrol
, combat
, chase
etc. are all separate behaviors. This not only makes state-objects modular, single-purpose and objective-driven but also easy to debug and author.
Goal-States: represent higher-level objectives based on world-state, story or personal interests of the character—like protecting king, guarding an area or escaping danger. They group together the behavior-states needed to achieve those objectives.
Goal (Guard)
├── Alert
├── Patrol
└── Combat
Goal (RunForDearLife)
├── Flee
└── Hide
Apart from goal and behavior states you can create two other types of states, including tactical-goals and global-states. See this example for details.
// In this example combat-state is a tactical-goal
// It consists of one or more behavior-states and does
// not define any actionalble logic by itself.
Goal (Guard)
├── Flee
├── Alert
├── Patrol
└── Combat
├── Melee
└── Ranged
// A global-state is just another behavior-state with one
// exception that it is not contained in any goal-state
// and can be activated regardless of the current goal.
// Examples include Dead or other similar states.
Global
└── Dead
At runtime, the system first decides a goal to pursue (based on triggers such as story context, world events, or character interests). Within the goal, the system then selects the most suitable behavior by evaluating their utility scores.
Using Smart-StateMachine is a simple 3 step process.
-
Start by inheriting from
SmartAgentBase
class, it provides the core setup for creating any ai-agent with Smart-StateMachine. It takes the initial or starting state through the constructor:public class AI_Agent : SmartAgentBase<State> { // Constructor public AI_Agent(State initial_state) : base(initial_state) {} }
-
The
Define_State
method (defined inSmartAgentBase
) creates a new state and registers its associated logic. It takes the following method-callbacks as parameters.Trigger → returns bool. Optionally, takes
int
Priority
param.
Signals weather the state should be activated. If multiple triggers with same priority are active at the same time, then state associated with first activeTrigger
will be activated. Default priority for all triggers is0
.
Utility_Evaluator → returns float. Calculates utility-score (higher scores = higher selection priority) for the state.
Enter → returns bool. Defines state enter behavior. This method is called when entering the state. If it returnsfalse
, theEnter
method will be called again on the next frame (useful for multi-frame setup logic). Returningtrue
signals that the state is ready, and execution moves on to the update phase.
Update → returns void. Runs every frame while the state is active.
Exit → returns bool. Called when transitioning out of a state. If it returnsfalse
, theExit
method will be called again on the next frame (allowing multi-frame cleanup). Returningtrue
signals that the state has fully exited and the transition can proceed.Define_State ( State.Idle, Trigger: () => some_condition, Utility_Evaluator: () => some_score, Enter: () => { if (!some_condition) return false; // state enter logic return true; }, Update: () => {}, Exit: () => { if (!some_condition) return false; // state exit logic return true; }, );
Not, all parameters are required for any state type, infact you only need one or two, see the following table for reference.
State Type Required Parameters Goals Trigger Tactical-goals Utility_Evaluator Behavior-States Utility_Evaluator, Enter (optional), Update, Exit (optional) Global-States Trigger, Enter (optional), Update, Exit (optional) -
The last step is to define goals — The
Begin_Goal_Defs
method returns a fluent builder that lets you create goals in a clean, hierarchical style.- From → adds a new goal.
- To → adds a behavior-state to a goal-state.
- sub_goals → begin tactical-goals.
- end_goal → closes the current goal definition.
- x → move one level up in the hierarchy.
smart_sm.Begin_Goal_Defs() // Goal: Guard .From(State.Guard) .To(State.Flee) .To(State.Alert) .To(State.Patrol) .To(State.Combat) .sub_goals() .To(State.RangedCombat) .To(State.MeleeCombat) .end_goal() // Goal: RunForDearLife .From(State.RunForDearLife) .To(State.Flee) .To(State.Hide) .end_goal()
Finally, create an instance of SmartAgentBase
and call it's Update
method everyframe.
public class AI_Agent : SmartAgentBase<State>
{
// 0) Define states as 'enum'
// Constructor
public AI_Agent(State initial_state)
: base(initial_state)
{
// 1) Create States using 'Define_State' method
// 2) Create Goals using 'Begin_Goal_Defs' method
}
}
public class GHOSTLogicTest : MonoBehaviour
{
private AI_Agent ai_agent; // instance SmartAgentBase
private void Start()
{
ai_agent = new( State.Idle );
}
private void Update()
{
ai_agent.Update();
}
}
That’s it for using Smart-StateMachine for your projects! 🎉
If anything is unclear, feel free to join the Discord server to ask questions or discuss anything. If you encounter any bugs or issues, please open a ticket here on GitHub. And if you enjoy this system, consider supporting it on Patreon
—it took a lot of time, effort, and brainstorming to bring it to life.