This project demonstrates a modular AI system using a Pluggable ScriptableObject-based Finite State Machine (FSM) in Unity, along with a Weapon System and a 2.5D/2.5 FPS Engine Controller.
Watch the demos here:
- ScriptableObject-based actions: Each AI behavior is encapsulated in a 
SM_ActionScriptableObject. - Interface Segregation: Behaviors implement small, focused interfaces (e.g., 
IEnemyChase,IEnemyWalk,IEnemyAttack) to allow modularity. - Generic behavior management: The 
EnemyControllercaches components implementing behavior interfaces and exposes genericInit<T>()andTick<T>()methods to avoid boilerplate. - Modular & extendable: Adding new behaviors does not require modifying the controller logic.
 - State Machine integration: States can call any behavior generically, keeping the FSM clean and decoupled from specific implementations.
 
// Example: Generic action call
stateController.Enemy.InjectTarget<IEnemyChase>();
stateController.Enemy.Tick<IEnemyChase>();Honestly it's a very humble and simplistic weapon system for fun.
- Component-based: Weapons are managed as separate components that can be attached to player.
 - Flexible firing mechanics: Supports hitscan or projectiles.
 - Modular: Weapons can be swapped or extended without modifying the core character controller.**
 
- Custom character controller: Physics-based movement using raycasts (
RaycastMotor2DandPlatformMotor2D) to handle slopes, collisions, and wall jumps. - Smooth movement & jumping: Acceleration and deceleration differ between air and ground for responsive controls.
 - Camera flexibility: Works for top-down, 3/4, or first-person-like perspectives.
 - Input decoupling: 
PlayerInputclass handles input, allowing the controller to remain modular and testable. 
- EnemyController: Central hub for AI behaviors.
 - Behavior Interfaces: Minimal, focused interfaces allow clear segregation of responsibilities.
 - Pluggable Actions: ScriptableObjects define AI actions (
WalkAction,ChaseAction,AttackAction, etc.). - Generics for simplicity: Generic methods replace repetitive 
InitWalk(),TickWalk(),InitChase(), etc., while preserving modularity. - Polymorphic behavior calls: EnemyController interacts with any behavior via its interface (e.g., IEnemyTickable, IEnemyTargetable) without needing to know the concrete implementation. Each component implements the interface in its own way, allowing generic Tick() and InjectTarget() calls to operate polymorphically.
 
    // Enemy Controller bla bla bla...
    private Transform playerTransform;
    private Dictionary<Type, object> behaviors = new();
    #region Properties
    public int ScoreValue { get => scoreValue; set => scoreValue = value; }
    public float IdleDuration { get => idleDuration; set => idleDuration = value; }
    public float WalkDuration { get => walkDuration; set => walkDuration = value; }
    public float SearchDuration { get => searchDuration; set => searchDuration = value; }
    #endregion
    private void Awake()
    {
        CacheBehavior<IEnemySearch>();
        CacheBehavior<IEnemyPatrol>();
        CacheBehavior<IEnemyWalk>();
        CacheBehavior<IEnemyChase>();
        CacheBehavior<IEnemyLook>();
        CacheBehavior<IEnemyAttack>();
        InjectTargets();
    }
    
    public void Tick<T>() where T : class, IEnemyTickable
    {
        GetBehavior<T>()?.Tick();
    }
    public bool IsLooking()
    {
        return GetBehavior<IEnemyLook>()?.IsLooking() ?? false;
    }
    
    private void CacheBehavior<T>() where T : class
    {
        var behavior = GetComponent<T>();
        if (behavior != null)
        {
            behaviors[typeof(T)] = behavior;
        }
    }
    private void InjectTargets()
    {
        List<IEnemyTargetable> targetables = GetComponents<IEnemyTargetable>().ToList();
        foreach (var t in targetables)
        {
            t.InjectTarget(playerTransform);
        }
    }
    
    private T GetBehavior<T>() where T : class
    {
        behaviors.TryGetValue(typeof(T), out var behavior);
        return behavior as T;
    }// Pluggable FSM Action example
[CreateAssetMenu(menuName = "PluggableAI/SM_Actions/Chase")]
public class ChaseAction : SM_Action
{
    public override void Act(EnemyStateMachine stateController)
    {
        // Polymorphic call: Enemy ticks the IEnemyChase behavior without knowing the concrete class
        stateController.Enemy.Tick<IEnemyChase>();
    }
}- Modularity: Add or remove behaviors without touching core code.
 - Maintainability: Clear separation of concerns.
 - Scalability: Easily extendable for new enemy types, weapons, or input systems.
 - Clean architecture: Avoids boilerplate while keeping the FSM decoupled from specific implementations.
 
- Network synchronization for AI and player.
 - Expand weapon system with status effects and ammo management.
 - Add more pluggable AI states like 
Flee,Patrol,Search, etc. - Maybe more parkour and maps? hehe.
 
- Updated to 
2021.3.16f1.