In this lesson, you will learn how to leverage ScriptableObjects to transform rigid, hard-coded Finite State Machines (FSMs) into flexible, data-driven systems that designers can modify without touching a single line of C#.
The Problem with Hard-Coded States
Previously, we managed state logic inside the MonoBehaviour of our NPC. If you wanted to change the speed of a "Patrol" state or swap an "Attack" state for a "Flee" state, you had to recompile your code. This tightly couples your behavior to your data, which is a common code smell that slows down iteration.
Decoupling Logic with ScriptableObjects
By moving state configuration into ScriptableObjects, we treat each state as a reusable asset. We define a base State class that contains the logic, while the ScriptableObject acts as a container for the parameters (like detection range, movement speed, or animation triggers).
Consider this pattern for a PatrolState configuration:
[CreateAssetMenu(menuName = "AI/States/Patrol")]
public class PatrolStateSO : ScriptableObject
{
public float movementSpeed = 3.5f;
public float waitTime = 2.0f;
public Color gizmoColor = Color.green;
// The NPC agent calls this to get its configuration
public void Execute(NavMeshAgent agent)
{
agent.speed = movementSpeed;
// Logic for patrol goes here...
}
}
Creating Reusable Behaviors
When your NPC’s State Machine runs, it simply references an instance of PatrolStateSO. Because it’s an asset, you can have a "SlowPatrol" and a "FastPatrol" by just creating two instances of the same ScriptableObject with different values in the Inspector.
This architectural shift allows you to swap an NPC's personality entirely. A designer can drag a "CautiousState" into the NPC's state list in the Inspector to make them behave defensively, or a "AggressiveState" to make them hunt the player, all while the game is running. You aren't changing the code; you are simply reconfiguring the data that drives the system.
Key Takeaway: By offloading state data to ScriptableObjects, you decouple your FSM logic from your game’s configuration, enabling designers to iterate on NPC behavior through the Inspector without requiring code recompilation.