Basic AI Concepts: State Machines and Decision Trees

Welcome to the foundation of game AI! In this chapter, you'll learn the two most fundamental AI concepts that power intelligent game characters: state machines for behavior control and decision trees for making smart choices.

What You'll Learn

By the end of this chapter, you'll understand:

  • How state machines control character behavior
  • How decision trees make intelligent choices
  • When to use each AI technique
  • How to implement both in your games

Understanding State Machines

A state machine is a system that can be in one of several states at any time, and transitions between states based on conditions. Think of it like a character's mood - they can be happy, sad, or angry, and switch between these states based on what happens to them.

Basic State Machine Components

States: The different behaviors or conditions your AI can be in

  • Idle (waiting, not doing anything)
  • Patrolling (walking around)
  • Chasing (following the player)
  • Attacking (fighting)
  • Fleeing (running away)

Transitions: The conditions that cause the AI to change states

  • "If player is nearby, switch from Idle to Chasing"
  • "If health is low, switch from Attacking to Fleeing"
  • "If player is far away, switch from Chasing to Patrolling"

Simple State Machine Example

Let's create a basic enemy AI that patrols, chases the player, and attacks:

public class EnemyAI : MonoBehaviour
{
    public enum AIState
    {
        Idle,
        Patrolling,
        Chasing,
        Attacking,
        Fleeing
    }

    public AIState currentState = AIState.Idle;
    public Transform player;
    public float detectionRange = 10f;
    public float attackRange = 2f;
    public float health = 100f;

    void Update()
    {
        switch (currentState)
        {
            case AIState.Idle:
                IdleBehavior();
                break;
            case AIState.Patrolling:
                PatrollingBehavior();
                break;
            case AIState.Chasing:
                ChasingBehavior();
                break;
            case AIState.Attacking:
                AttackingBehavior();
                break;
            case AIState.Fleeing:
                FleeingBehavior();
                break;
        }
    }

    void IdleBehavior()
    {
        // Check if player is nearby
        if (Vector3.Distance(transform.position, player.position) < detectionRange)
        {
            currentState = AIState.Chasing;
        }
        // Start patrolling after a few seconds
        else if (Time.time > lastStateChange + 3f)
        {
            currentState = AIState.Patrolling;
        }
    }

    void PatrollingBehavior()
    {
        // Move to patrol points
        MoveToPatrolPoint();

        // Check if player is nearby
        if (Vector3.Distance(transform.position, player.position) < detectionRange)
        {
            currentState = AIState.Chasing;
        }
    }

    void ChasingBehavior()
    {
        // Move towards player
        MoveTowardsPlayer();

        // Check if close enough to attack
        if (Vector3.Distance(transform.position, player.position) < attackRange)
        {
            currentState = AIState.Attacking;
        }
        // Check if player is too far away
        else if (Vector3.Distance(transform.position, player.position) > detectionRange * 1.5f)
        {
            currentState = AIState.Patrolling;
        }
    }

    void AttackingBehavior()
    {
        // Attack the player
        AttackPlayer();

        // Check if player is still in range
        if (Vector3.Distance(transform.position, player.position) > attackRange)
        {
            currentState = AIState.Chasing;
        }
        // Check if health is too low
        else if (health < 30f)
        {
            currentState = AIState.Fleeing;
        }
    }

    void FleeingBehavior()
    {
        // Run away from player
        RunAwayFromPlayer();

        // Check if health is restored or player is far away
        if (health > 50f || Vector3.Distance(transform.position, player.position) > detectionRange * 2f)
        {
            currentState = AIState.Patrolling;
        }
    }
}

Understanding Decision Trees

A decision tree is a structure that makes decisions by asking yes/no questions in sequence. It's like a flowchart that guides the AI through a series of choices to reach the best decision.

Decision Tree Structure

Root Node: The starting point of the decision Internal Nodes: Questions or conditions to check Leaf Nodes: The final decisions or actions Branches: The paths between nodes

Simple Decision Tree Example

Let's create a decision tree for an AI that decides what to do based on the situation:

public class AIDecisionTree : MonoBehaviour
{
    public Transform player;
    public float health = 100f;
    public float distanceToPlayer;
    public bool hasWeapon = true;
    public bool isLowOnAmmo = false;

    public enum Decision
    {
        Attack,
        Retreat,
        FindAmmo,
        Patrol,
        Hide
    }

    public Decision MakeDecision()
    {
        // Root question: Is the player nearby?
        if (distanceToPlayer < 10f)
        {
            // Player is nearby - check health
            if (health < 30f)
            {
                // Low health - retreat
                return Decision.Retreat;
            }
            else
            {
                // Good health - check weapon status
                if (hasWeapon && !isLowOnAmmo)
                {
                    // Has weapon and ammo - attack
                    return Decision.Attack;
                }
                else if (isLowOnAmmo)
                {
                    // Low on ammo - find ammo
                    return Decision.FindAmmo;
                }
                else
                {
                    // No weapon - hide
                    return Decision.Hide;
                }
            }
        }
        else
        {
            // Player is far away - patrol
            return Decision.Patrol;
        }
    }
}

When to Use Each Technique

Use State Machines When:

  • You need clear, distinct behaviors
  • Behavior changes are event-driven
  • You want predictable AI patterns
  • You need to debug AI behavior easily

Examples: Enemy AI, NPC behavior, game mode switching

Use Decision Trees When:

  • You need complex decision-making
  • Decisions depend on multiple factors
  • You want flexible, data-driven AI
  • You need to balance multiple priorities

Examples: Strategy game AI, resource management, dialogue choices

Combining State Machines and Decision Trees

The most powerful AI systems combine both techniques:

public class AdvancedAI : MonoBehaviour
{
    public enum AIState
    {
        Idle,
        Patrolling,
        Chasing,
        Attacking,
        Fleeing
    }

    public AIState currentState = AIState.Idle;
    private AIDecisionTree decisionTree;

    void Update()
    {
        // Use decision tree to determine what state to be in
        AIState desiredState = decisionTree.DecideState();

        // Use state machine to execute the behavior
        if (desiredState != currentState)
        {
            currentState = desiredState;
        }

        // Execute current state behavior
        ExecuteStateBehavior();
    }
}

Pro Tips for AI Development

State Machine Best Practices

  • Keep states simple and focused
  • Use clear, descriptive state names
  • Avoid too many states (5-7 is usually enough)
  • Always have a default state
  • Test state transitions thoroughly

Decision Tree Best Practices

  • Start with the most important decisions
  • Keep questions simple and clear
  • Avoid overly complex trees
  • Use data to drive decisions
  • Test edge cases and unusual situations

Common Mistakes to Avoid

  • Don't make states too complex
  • Don't create decision trees that are too deep
  • Don't forget to handle edge cases
  • Don't make AI too predictable or too random
  • Don't ignore performance implications

Mini Challenge: Create Your Own AI

Try creating a simple AI character that:

  1. Patrols when the player is far away
  2. Chases when the player is nearby
  3. Attacks when close enough
  4. Flees when health is low

Use a state machine for the behavior and a decision tree for the state transitions.

Troubleshooting Common Issues

AI Gets Stuck in One State

  • Check your transition conditions
  • Make sure conditions can actually be met
  • Add debug logging to see what's happening

AI Makes Poor Decisions

  • Review your decision tree logic
  • Test with different scenarios
  • Consider adding more decision factors

AI Performance Issues

  • Limit the frequency of decision-making
  • Use simple conditions when possible
  • Consider using coroutines for complex AI

What's Next?

Now that you understand the basics of state machines and decision trees, you're ready to learn about pathfinding - how AI characters navigate through your game world. In the next chapter, you'll learn about the A* algorithm and how to implement navigation systems.

Resources

Community Support

  • Discord Server: Get help with AI implementation
  • GitHub Repository: Share your AI code
  • AI Game Development Forums: Learn from other developers
  • Course Discussion: Share your progress and get feedback

Ready to learn how AI characters navigate your game world? Continue to [Chapter 3: Pathfinding: A Algorithm and Navigation](/guides/ai-in-game-development/pathfinding-astar-algorithm-navigation) and discover how to create intelligent movement systems.*