Newer
Older
HierarchicalFSM-Unity3D / Assets / Scripts / NPC / FSM / FSM.cs
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class FSM : MonoBehaviour
{
    [SerializeField] private State initialState;
    [SerializeField] private State currentState;
    [SerializeField] private NPCController controller;
    public NPCController Controller => controller;

    void Awake()
    {
        controller = GetComponent<NPCController>();
    }

    void Start()
    {
        initialState.InitializeSubStates();
        currentState = initialState; //Sets a state
    }

    void Update()
    {
        if (currentState == null) return;

        ExecuteState(currentState);
    }


    private void ExecuteState(State state)
    {
        state.ExecuteActions(this);

        Transition triggeredTransition = state.GetTriggeredTransition(this);

        if (triggeredTransition != null)
        {
            ChangeState(triggeredTransition.TargetState());
        }

        else if (state.SubStates() != null && state.SubStates().Count > 0)
        {
            foreach (State substate in state.SubStates())
            {
                ExecuteState(substate);

                // Check for triggered transitions in the substate
                triggeredTransition = substate.GetTriggeredTransition(this);

                if (triggeredTransition != null)
                {
                    // If a transition is triggered in the substate, change to the target state
                    ChangeState(triggeredTransition.TargetState());
                    return; // Exit after transitioning to avoid running further substates
                }
            }
        }
    }

    //Changes the current state to a new state
    public void ChangeState(State newState)
    {
        currentState?.ExitAction()?.Execute(this); //Executes the exit action of the current state
        currentState = newState; //Changes the state
        currentState?.Enter(this); //Executes entry action
    }

    //Returns the current state
    public State CurrentState() => currentState;


    //DEBUGING
    void OnGUI()
    {
        float yPosition = 10;
        float elementHeight = 20; // Height for each line
        float padding = 5;
        List<string> actions = new List<string>();
        List<string> conditions = new List<string>();

        foreach (Action action in currentState.GetActions())
        {
            actions.Add($"Action: {action.name}");
        }

        foreach (Transition transition in currentState.GetTransitions().ToList())
        {
            conditions.Add($"Transition: {transition}\nCondition: {transition.GetCondition().name}: {transition.GetCondition()}");
        }

        // Create a list to hold all the UI elements
        List<string> uiElements = new List<string> { $"State: {currentState.name}" };
        uiElements.AddRange(actions);
        uiElements.AddRange(conditions);

        // Define the GUIStyle
        GUIStyle style = new GUIStyle();
        style.fontSize = 14;

        for (int i = 0; i < uiElements.Count; i++)
        {
            if (i == 0)
            {
                style.normal.textColor = Color.green;
            }
            else
            {
                style.normal.textColor = Color.yellow;
            }

            // Create the rectangle for each line of text
            Rect rect = new Rect(10, yPosition, 300, elementHeight);

            // Display the label with the appropriate style
            GUI.Label(rect, uiElements[i], style);

            // Move the yPosition down for the next line
            yPosition += elementHeight + padding;
        }
    }
}