Implementing Finite State Machine AI

What are finite state machines?

The finite state machine system is a way of designing non-player character AI. Characters can only ever be in once ‘state’, with a set of behaviours that happens in that state.

Imagine an enemy character in a game like Metal Gear Solid. The enemy does not, when you enter the level, know where you are (or even if there's someone infiltrating at all!) and so might be in a state called Idle or Patrol. They would then have code that would run only when they are in that state. Similarly, they may spot the player and change to an Attack state. This would have its own associated code that would be different from the other states.

Finite state machine AI is clean and efficient, and can be very readable for the player. However, it doesn’t always mirror human behaviour, and as such makes characters feel very ‘robotic’.

Finite state machines are usually implemented using enums.

Enums allow us to create a variable, similar to a boolean (yes or no), but with more than two possible states.

By using the enum system, we will therefore be able to create discrete states for a game character, such as 'idle', 'attack', 'patrol' etc.

Below is an example of a finite state machine used to toggle between Idle and Chase states:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class NPC : MonoBehaviour
{
    NavMeshAgent nav;
    [SerializeField] Transform target;
    Vector3 startingPos;
    enum AIStates { idle, chase}
    [SerializeField] AIStates currentState = AIStates.idle;

    void Start()
    {
        startingPos = transform.position;
        nav = GetComponent<NavMeshAgent>();
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Alpha1))
        {
            currentState = AIStates.idle;
        }

        if (Input.GetKeyDown(KeyCode.Alpha2))
        {
            currentState = AIStates.chase;
        }

        switch (currentState)
        {
            case AIStates.idle:
                nav.SetDestination(startingPos);
                break;
            case AIStates.chase:
                nav.SetDestination(target.position);
                break;
        }
    }
}

Note that here we don't have methods to actually change the states in a logical way; this is just a debug example. Alpha1 and Alpha2 are the numbers above the keyboard (not the F keys...).

Last updated