Skip to content

rehman-obaid/Smart-StateMachine

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Smart-StateMachine

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.

💡How Smart-StateMachine works ?

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.

Usage Instrutions: Basic setup

Using Smart-StateMachine is a simple 3 step process.

  1. 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) {}
    }
    
  2. The Define_State method (defined in SmartAgentBase) 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 active Trigger will be activated. Default priority for all triggers is 0.
    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 returns false, the Enter method will be called again on the next frame (useful for multi-frame setup logic). Returning true 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 returns false, the Exit method will be called again on the next frame (allowing multi-frame cleanup). Returning true 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)
  3. 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.

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •  

Languages