You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
721 lines
28 KiB
C#
721 lines
28 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using MalbersAnimations.Events;
|
|
using UnityEngine.AI;
|
|
using MalbersAnimations.Scriptables;
|
|
using UnityEngine.Serialization;
|
|
using System;
|
|
using System.Linq;
|
|
|
|
#if UNITY_EDITOR
|
|
using UnityEditor;
|
|
#endif
|
|
|
|
namespace MalbersAnimations.Controller.AI
|
|
{
|
|
[AddComponentMenu("Malbers/Animal Controller/AI/Animal Brain")]
|
|
public class MAnimalBrain : MonoBehaviour, IAnimatorListener
|
|
{
|
|
/// <summary>Reference for the Ai Control Movement</summary>
|
|
public IAIControl AIControl;
|
|
[Obsolete("Use AIControl Instead")]
|
|
public IAIControl AIMovement => AIControl;
|
|
|
|
|
|
/// <summary>Transform used to raycast Rays to interact with the world</summary>
|
|
[RequiredField, Tooltip("Transform used to raycast Rays to interact with the world")]
|
|
public Transform Eyes;
|
|
/// <summary>Time needed to make a new transition. Necesary to avoid Changing to multiple States in the same frame</summary>
|
|
[Tooltip("Time needed to make a new transition. Necessary to avoid Changing to multiple States in the same frame")]
|
|
public FloatReference TransitionCoolDown = new FloatReference(0.2f);
|
|
|
|
/// <summary>Reference AI State for the animal</summary>
|
|
[CreateScriptableAsset] public MAIState currentState;
|
|
|
|
[Tooltip("Removes all AI Components when the Animal Dies. (Brain, AiControl, Agent)")]
|
|
[FormerlySerializedAs("RemoveAIOnDeath")]
|
|
public bool DisableAIOnDeath = true;
|
|
public bool debug = false;
|
|
public bool debugAIStates = false;
|
|
|
|
|
|
public IntEvent OnTaskStarted = new IntEvent();
|
|
public IntEvent OnDecisionSucceeded = new IntEvent();
|
|
public IntEvent OnAIStateChanged = new IntEvent();
|
|
|
|
|
|
/// <summary>Last Time the Animal make a new transition</summary>
|
|
private float TransitionLastTime;
|
|
|
|
/// <summary>Last Time the Animal started a transition</summary>
|
|
public float StateLastTime { get; set; }
|
|
|
|
|
|
/// <summary>Check if all the Task are done..</summary>
|
|
public bool AllTasksDone()
|
|
{
|
|
foreach (var done in TasksDone)
|
|
{
|
|
if (!done) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/// <summary>Check if an Specific Task is Done..</summary>
|
|
public bool IsTasksDone(int index)
|
|
{
|
|
return TasksDone[index % TasksDone.Length];
|
|
}
|
|
|
|
/// <summary>Tasks Local Vars (1 Int,1 Bool,1 Float)</summary>
|
|
public BrainVars[] TasksVars;
|
|
/// <summary>Saves on the a Task that it has finish is stuff</summary>
|
|
internal bool[] TasksDone;
|
|
/// <summary>Current Decision Results</summary>
|
|
internal bool[] DecisionResult;
|
|
/// <summary>Store if a Task has Started</summary>
|
|
internal bool[] TasksStarted;
|
|
/// <summary>Decision Local Vars to store values on Prepare Decision</summary>
|
|
public BrainVars[] DecisionsVars;
|
|
internal bool BrainInitialize;
|
|
|
|
|
|
|
|
|
|
#region Properties
|
|
|
|
|
|
/// <summary>Reference for the Animal</summary>
|
|
public MAnimal Animal { get; private set; }
|
|
|
|
/// <summary>Reference for the AnimalStats</summary>
|
|
public Dictionary<int, Stat> AnimalStats { get; set; }
|
|
|
|
#region Target References
|
|
/// <summary>Reference for the Current Target the Animal is using</summary>
|
|
public Transform Target { get; set; }
|
|
//{
|
|
// get => target;
|
|
// set
|
|
// {
|
|
// target = value;
|
|
// }
|
|
//}
|
|
//private Transform target;
|
|
|
|
/// <summary>Reference for the Target the Animal Component</summary>
|
|
public MAnimal TargetAnimal { get; set; }
|
|
|
|
public Vector3 Position => AIControl.Transform.position;
|
|
|
|
public float AIHeight => Animal.transform.lossyScale.y * AIControl.StoppingDistance;
|
|
|
|
/// <summary>True if the Current Target has Stats</summary>
|
|
public bool TargetHasStats { get; private set; }
|
|
|
|
/// <summary>Reference for the Target the Stats Component</summary>
|
|
public Dictionary<int, Stat> TargetStats { get; set; }
|
|
#endregion
|
|
|
|
/// <summary>Reference for the Last WayPoint the Animal used</summary>
|
|
public IWayPoint LastWayPoint { get; set; }
|
|
|
|
/// <summary>Time Elapsed for the Tasks on an AI State</summary>
|
|
public float[] TasksStartTime { get; set; }
|
|
public float[] TasksUpdateTime { get; set; }
|
|
|
|
/// <summary>Time Elapsed for the State Decisions</summary>
|
|
[HideInInspector] public float[] DecisionsTime;// { get; set; }
|
|
|
|
#endregion
|
|
|
|
#region Unity Callbakcs
|
|
void Awake()
|
|
{
|
|
if (Animal == null) Animal = gameObject.FindComponent<MAnimal>();
|
|
if (AIControl == null) AIControl = gameObject.FindInterface<IAIControl>();
|
|
|
|
var AnimalStatscomponent = Animal.FindComponent<Stats>();
|
|
if (AnimalStatscomponent) AnimalStats = AnimalStatscomponent.stats_D;
|
|
|
|
Animal.isPlayer.Value = false; //If is using a brain... disable that he is the main player
|
|
ResetVarsOnNewState();
|
|
}
|
|
|
|
|
|
void OnEnable()
|
|
{
|
|
//AIMovement.OnTargetArrived.AddListener(OnTargetArrived);
|
|
//AIMovement.OnTargetPositionArrived.AddListener(OnPositionArrived);
|
|
AIControl.TargetSet.AddListener(OnTargetSet);
|
|
AIControl.OnArrived.AddListener(OnTargetArrived);
|
|
|
|
Animal.OnStateChange.AddListener(OnAnimalStateChange);
|
|
Animal.OnStanceChange.AddListener(OnAnimalStanceChange);
|
|
Animal.OnModeStart.AddListener(OnAnimalModeStart);
|
|
Animal.OnModeEnd.AddListener(OnAnimalModeEnd);
|
|
|
|
Invoke(nameof(StartBrain), 0.1f); //Start AI a Frame later;
|
|
}
|
|
|
|
void OnDisable()
|
|
{
|
|
//AIMovement.OnTargetArrived.RemoveListener(OnTargetArrived);
|
|
//AIMovement.OnTargetPositionArrived.RemoveListener(OnPositionArrived);
|
|
AIControl.TargetSet.RemoveListener(OnTargetSet);
|
|
AIControl.OnArrived.RemoveListener(OnTargetArrived);
|
|
|
|
|
|
Animal.OnStateChange.RemoveListener(OnAnimalStateChange);
|
|
Animal.OnStanceChange.RemoveListener(OnAnimalStanceChange);
|
|
Animal.OnModeStart.RemoveListener(OnAnimalModeStart);
|
|
Animal.OnModeEnd.RemoveListener(OnAnimalModeEnd);
|
|
|
|
// AIControl.Stop();
|
|
StopAllCoroutines();
|
|
|
|
if (currentState)
|
|
{
|
|
for (int i = 0; i < currentState.tasks.Length; i++) //Exit the Current Tasks
|
|
currentState.tasks[i]?.ExitAIState(this, i);
|
|
}
|
|
BrainInitialize = false;
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
if (BrainInitialize && currentState != null) currentState.Update_State(this);
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
public void StartBrain()
|
|
{
|
|
if (currentState)
|
|
{
|
|
for (int i = 0; i < currentState.tasks.Length; i++)
|
|
{
|
|
if (currentState.tasks[i] == null)
|
|
{
|
|
Debug.LogError($"The [{currentState.name}] AI State has an Empty Task. AI States can't have empty Tasks. {Animal.name}", currentState);
|
|
// enabled = false;
|
|
return;
|
|
};
|
|
|
|
}
|
|
|
|
StartNewState(currentState);
|
|
}
|
|
else
|
|
{
|
|
enabled = false;
|
|
return;
|
|
}
|
|
|
|
AIControl.AutoNextTarget = false;
|
|
|
|
|
|
LastWayPoint = null;
|
|
|
|
if (AIControl.Target)
|
|
SetLastWayPoint(AIControl.Target);
|
|
|
|
BrainInitialize = true;
|
|
}
|
|
|
|
|
|
|
|
public virtual void TransitionToState(MAIState nextState, bool decisionValue, MAIDecision decision, int Index)
|
|
{
|
|
if (MTools.ElapsedTime(TransitionLastTime, TransitionCoolDown)) //This avoid making transition on the same Frame ****IMPORTANT
|
|
{
|
|
if (nextState != null && nextState != currentState) //Do not transition to itself!
|
|
{
|
|
TransitionLastTime = Time.time;
|
|
|
|
decision.FinishDecision(this, Index);
|
|
|
|
|
|
Debuging($"<color=white>Changed AI State from <B>[{currentState.name}]</B> to" +
|
|
$" <B>[{nextState.name}]</B>. Decision: <b>[{decision.name}]</b> = <B>[{decisionValue}]</B>.</color>", currentState);
|
|
|
|
InvokeDecisionEvent(decisionValue, decision);
|
|
|
|
StartNewState(nextState);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected virtual void Debuging(string Log, UnityEngine.Object val) { if (debug) Debug.Log($"<B>[{Animal.name}] - </B> " + Log,val); }
|
|
|
|
private void InvokeDecisionEvent(bool decisionValue, MAIDecision decision)
|
|
{
|
|
if (decision.send == MAIDecision.WSend.SendTrue && decisionValue)
|
|
{
|
|
OnDecisionSucceeded.Invoke(decision.DecisionID);
|
|
}
|
|
else if (decision.send == MAIDecision.WSend.SendFalse && !decisionValue)
|
|
{
|
|
OnDecisionSucceeded.Invoke(decision.DecisionID);
|
|
}
|
|
}
|
|
|
|
public virtual void StartNewState(MAIState newState)
|
|
{
|
|
if (!enabled) enabled = true; //Make sure the Brain is enabled!!!! IMPORTANT
|
|
|
|
StateLastTime = Time.time; //Store the last time the Animal made a transition
|
|
|
|
if (currentState != null && currentState != newState)
|
|
{
|
|
currentState.Finish_Tasks(this); //Finish all the Task on the Current State
|
|
// currentState.Finish_Decisions(this); //Finish all the Decisions on the Current State
|
|
}
|
|
|
|
currentState = newState; //Set a new State
|
|
|
|
ResetVarsOnNewState();
|
|
|
|
OnAIStateChanged.Invoke(currentState.ID);
|
|
currentState.Start_AIState(this); //Start all Tasks on the new State
|
|
currentState.Prepare_Decisions(this); //Start all Tasks on the new State
|
|
|
|
|
|
Debuging($"<color=white> Set AI State <B>[{currentState.name}]</B> </color>", currentState);
|
|
|
|
}
|
|
|
|
|
|
/// <summary>Prepare all the local variables on the New State before starting new tasks</summary>
|
|
private void ResetVarsOnNewState()
|
|
{
|
|
if (currentState)
|
|
{
|
|
var tasks = (currentState.transitions != null && currentState.tasks.Length > 0) ? currentState.tasks.Length : 1;
|
|
var transitions = (currentState.transitions != null && currentState.transitions.Length > 0) ? currentState.transitions.Length : 1;
|
|
|
|
TasksVars = new BrainVars[tasks]; //Local Variables you can use on your tasks
|
|
TasksUpdateTime = new float[tasks]; //Reset all the Tasks Time elapsed time
|
|
TasksStartTime = new float[tasks]; //Reset all the Tasks Time elapsed time
|
|
|
|
TasksDone = new bool[tasks]; //Reset if they are Done
|
|
TasksStarted = new bool[tasks]; //Reset if they tasks are started
|
|
|
|
DecisionsVars = new BrainVars[transitions]; //Local Variables you can use on your Decisions
|
|
DecisionsTime = new float[transitions]; //Reset all the Decisions Time elapsed time
|
|
DecisionResult = new bool[transitions]; //Reset if they tasks are started
|
|
}
|
|
}
|
|
|
|
|
|
public bool IsTaskDone(int TaskIndex) => TasksDone[TaskIndex];
|
|
|
|
public void TaskDone(int TaskIndex, bool value = true) //If the first task is done then go and do the next one
|
|
{
|
|
TasksDone[TaskIndex] = value;
|
|
|
|
if (TaskIndex + 1 < currentState.tasks.Length && currentState.tasks[TaskIndex + 1].WaitForPreviousTask) //Start the next task that needs to wait for the previus one
|
|
{
|
|
// Debug.Log($"*Task DONE!!!!: [{name}] [{TaskIndex}]-[{currentState.tasks[TaskIndex].name }]");
|
|
currentState.StartWaitforPreviusTask(this, TaskIndex + 1);
|
|
}
|
|
}
|
|
|
|
/// <summary> Check if the time elapsed of a task using a duration or CountDown time </summary>
|
|
/// <param name="duration">Duration of the countDown|CoolDown</param>
|
|
/// <param name="index">Index of the Task on the AI State Tasks list</param>
|
|
public bool CheckIfDecisionsCountDownElapsed(float duration, int index)
|
|
{
|
|
DecisionsTime[index] += Time.deltaTime;
|
|
return DecisionsTime[index] >= duration;
|
|
}
|
|
|
|
/// <summary>Set the time on which a task has started on the current AI State</summary>
|
|
public void SetTaskStartTime(int Index)
|
|
{
|
|
TasksStartTime[Index] = Time.time;
|
|
}
|
|
|
|
|
|
/// <summary>Reset the Time elapsed on a Decision using its index from the Transition List </summary>
|
|
/// <param name="Index">Index of the Decision on the AI State Transition List</param>
|
|
public void ResetDecisionTime(int Index) => DecisionsTime[Index] = 0;
|
|
|
|
public virtual bool OnAnimatorBehaviourMessage(string message, object value) => this.InvokeWithParams(message, value);
|
|
|
|
|
|
|
|
#region SelfAnimal Event Listeners
|
|
void OnAnimalStateChange(int state)
|
|
{
|
|
currentState?.OnAnimalStateEnter(this, Animal.ActiveState);
|
|
currentState?.OnAnimalStateExit(this, Animal.LastState);
|
|
|
|
|
|
if (state == StateEnum.Death) //meaning this animal has died
|
|
{
|
|
for (int i = 0; i < currentState.tasks.Length; i++) //Exit the Current Tasks
|
|
currentState.tasks[i].ExitAIState(this, i);
|
|
|
|
enabled = false;
|
|
|
|
if (DisableAIOnDeath)
|
|
{
|
|
AIControl.SetActive(false);
|
|
enabled = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void OnAnimalStanceChange(int stance) => currentState.OnAnimalStanceChange(this, Animal.Stance.ID);
|
|
|
|
void OnAnimalModeStart(int mode, int ability) => currentState.OnAnimalModeStart(this, Animal.ActiveMode);
|
|
|
|
void OnAnimalModeEnd(int mode, int ability) => currentState.OnAnimalModeEnd(this, Animal.ActiveMode);
|
|
|
|
|
|
#endregion
|
|
|
|
#region TargetAnimal Event Listeners
|
|
//void OnTargetAnimalStateChange(int state)
|
|
//{
|
|
// currentState.OnTargetAnimalStateEnter(this, Animal.ActiveState);
|
|
// currentState.OnTargetAnimalStateExit(this, Animal.LastState);
|
|
//}
|
|
|
|
private void OnTargetArrived(Transform target) => currentState.OnTargetArrived(this, target);
|
|
|
|
//private void OnPositionArrived(Vector3 position) => currentState.OnPositionArrived(this, position);
|
|
#endregion
|
|
|
|
|
|
/// <summary>Stores if the Current Target is an Animal and if it has the Stats component </summary>
|
|
private void OnTargetSet(Transform target)
|
|
{
|
|
Target = target;
|
|
|
|
if (target)
|
|
{
|
|
TargetAnimal = target.FindComponent<MAnimal>();// ?? target.GetComponentInChildren<MAnimal>();
|
|
|
|
TargetStats = null;
|
|
var TargetStatsC = target.FindComponent<Stats>();// ?? target.GetComponentInChildren<Stats>();
|
|
|
|
TargetHasStats = TargetStatsC != null;
|
|
if (TargetHasStats) TargetStats = TargetStatsC.stats_D;
|
|
}
|
|
}
|
|
|
|
public bool CheckForPreviusTaskDone(int index)
|
|
{
|
|
if (index == 0) return true;
|
|
|
|
if (!TasksStarted[index] && IsTaskDone(index - 1))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
public void SetLastWayPoint(Transform target)
|
|
{
|
|
var newLastWay = target.gameObject.FindInterface<IWayPoint>();
|
|
if (newLastWay != null) LastWayPoint = target?.gameObject.FindInterface<IWayPoint>(); //If not is a waypoint save the last one
|
|
}
|
|
|
|
|
|
|
|
[SerializeField] private int Editor_Tabs1;
|
|
#if UNITY_EDITOR
|
|
void Reset()
|
|
{
|
|
// remainInState = MTools.GetInstance<MAIState>("Remain in State");
|
|
AIControl = this.FindComponent<MAnimalAIControl>();
|
|
|
|
if (AIControl != null)
|
|
{
|
|
AIControl.AutoNextTarget = false;
|
|
AIControl.UpdateDestinationPosition = false;
|
|
AIControl.LookAtTargetOnArrival = false;
|
|
|
|
if (Animal) Animal.isPlayer.Value = false; //Make sure this animal is not the Main Player
|
|
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning("There's No AI Control in this GameObject, Please add one");
|
|
}
|
|
}
|
|
|
|
void OnDrawGizmos()
|
|
{
|
|
if (isActiveAndEnabled && currentState && Eyes)
|
|
{
|
|
Gizmos.color = currentState.GizmoStateColor;
|
|
Gizmos.DrawWireSphere(Eyes.position, 0.2f);
|
|
|
|
if (debug)
|
|
{
|
|
if (currentState != null)
|
|
{
|
|
if (currentState.tasks != null)
|
|
foreach (var task in currentState.tasks)
|
|
task?.DrawGizmos(this);
|
|
|
|
|
|
if (currentState.transitions != null)
|
|
foreach (var tran in currentState.transitions)
|
|
tran?.decision?.DrawGizmos(this);
|
|
}
|
|
}
|
|
|
|
if (Application.isPlaying && debugAIStates)
|
|
{
|
|
string desicions = "";
|
|
|
|
var Styl = new GUIStyle(EditorStyles.boldLabel);
|
|
Styl.normal.textColor = Color.yellow;
|
|
|
|
UnityEditor.Handles.Label(Eyes.position, "State: " + currentState.name + desicions, Styl);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
public enum Affected { Self, Target }
|
|
public enum ExecuteTask { OnStart, OnUpdate, OnExit }
|
|
|
|
[System.Serializable]
|
|
public struct BrainVars
|
|
{
|
|
public int intValue;
|
|
public float floatValue;
|
|
public bool boolValue;
|
|
public Vector3 vector3;
|
|
public Component[] Components;
|
|
public GameObject[] gameobjects;
|
|
|
|
public Dictionary<int,int> ints;
|
|
public Dictionary<int, float> floats;
|
|
public Dictionary<int, bool> bools;
|
|
|
|
public void SetVar(int key, bool value) => bools[key] = value;
|
|
public void SetVar(int key, int value) => ints[key] = value;
|
|
public void SetVar(int key, float value) => floats[key] = value;
|
|
public bool GetBool(int key) => bools[key];
|
|
public int GetInt(int key) => ints[key];
|
|
public float GetFloat(int key) => floats[key];
|
|
|
|
|
|
public bool TryGetBool(int key, out bool value) => bools.TryGetValue(key, out value);
|
|
public bool TryGetInt(int key, out int value) => ints.TryGetValue(key, out value);
|
|
public bool TryGetFloat(int key, out float value) => floats.TryGetValue(key, out value);
|
|
|
|
public void AddVar(int key, bool value)
|
|
{
|
|
if (bools == null) bools = new Dictionary<int, bool>();
|
|
bools.Add(key, value);
|
|
}
|
|
|
|
public void AddVar(int key, int value)
|
|
{
|
|
if (bools == null) bools = new Dictionary<int, bool>();
|
|
ints.Add(key, value);
|
|
}
|
|
|
|
public void AddVar(int key, float value)
|
|
{
|
|
if (bools == null) bools = new Dictionary<int, bool>();
|
|
floats.Add(key, value);
|
|
}
|
|
|
|
public void AddComponents(Component[] components)
|
|
{
|
|
if (Components == null || Components.Length == 0) Components = components;
|
|
else
|
|
{
|
|
Components = Components.Concat(components).ToArray();
|
|
}
|
|
}
|
|
|
|
|
|
public void AddComponent(Component comp)
|
|
{
|
|
if (Components == null || Components.Length == 0) Components = new Component[1] { comp };
|
|
else
|
|
{
|
|
var ComponentsL = Components.ToList();
|
|
ComponentsL.Add(comp);
|
|
Components = ComponentsL.ToArray();
|
|
}
|
|
}
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
[CustomEditor(typeof(MAnimalBrain)), CanEditMultipleObjects]
|
|
public class MAnimalBrainEditor : Editor
|
|
{
|
|
SerializedProperty Eyes, debug, TransitionCoolDown, DisableAIOnDeath, Editor_Tabs1, debugAIStates,
|
|
currentState, OnTaskStarted, OnDecisionSucceded, OnAIStateChanged;
|
|
|
|
protected string[] Tabs1 = new string[] { "AI States" , "Events" ,"Debug"};
|
|
|
|
MAnimalBrain M;
|
|
|
|
private void OnEnable()
|
|
{
|
|
M = (MAnimalBrain)target;
|
|
|
|
Eyes = serializedObject.FindProperty("Eyes");
|
|
TransitionCoolDown = serializedObject.FindProperty("TransitionCoolDown");
|
|
DisableAIOnDeath = serializedObject.FindProperty("DisableAIOnDeath");
|
|
currentState = serializedObject.FindProperty("currentState");
|
|
|
|
OnTaskStarted = serializedObject.FindProperty("OnTaskStarted");
|
|
OnDecisionSucceded = serializedObject.FindProperty("OnDecisionSucceeded");
|
|
OnAIStateChanged = serializedObject.FindProperty("OnAIStateChanged");
|
|
Editor_Tabs1 = serializedObject.FindProperty("Editor_Tabs1");
|
|
debug = serializedObject.FindProperty("debug");
|
|
// AISource = serializedObject.FindProperty("AISource");
|
|
debugAIStates = serializedObject.FindProperty("debugAIStates");
|
|
}
|
|
|
|
public override void OnInspectorGUI()
|
|
{
|
|
serializedObject.Update();
|
|
MalbersEditor.DrawDescription("Brain Logic for the Animal");
|
|
EditorGUILayout.BeginVertical(MTools.StyleGray);
|
|
{
|
|
|
|
Editor_Tabs1.intValue = GUILayout.Toolbar(Editor_Tabs1.intValue, Tabs1);
|
|
|
|
|
|
if (Editor_Tabs1.intValue == 0) DrawGeneral();
|
|
else if (Editor_Tabs1.intValue == 1) DrawEvents();
|
|
else DrawDebug();
|
|
|
|
|
|
if (Eyes.objectReferenceValue == null) EditorGUILayout.HelpBox("The AI Eyes [Reference] is missing. Please add a transform the AI Eyes parameters", MessageType.Error);
|
|
|
|
}
|
|
EditorGUILayout.EndVertical();
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
}
|
|
|
|
private void DrawDebug()
|
|
{
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
{
|
|
EditorGUILayout.PropertyField(debugAIStates, new GUIContent("Debug On Screen"));
|
|
|
|
if (Application.isPlaying)
|
|
{
|
|
EditorGUI.BeginDisabledGroup(true);
|
|
Repaint();
|
|
|
|
|
|
EditorGUILayout.ObjectField("Brain Target", M.Target, typeof(Transform), false);
|
|
|
|
if (M.enabled && M.BrainInitialize && M.currentState != null)
|
|
{
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
{
|
|
EditorGUILayout.ObjectField("AI State", M.currentState, typeof(MAIState), false);
|
|
|
|
|
|
EditorGUILayout.LabelField("Tasks", EditorStyles.boldLabel);
|
|
|
|
for (int i = 0; i < M.currentState.tasks.Length; i++)
|
|
{
|
|
EditorGUILayout.BeginHorizontal();
|
|
EditorGUILayout.ObjectField(GUIContent.none, M.currentState.tasks[i], typeof(MTask), false, GUILayout.MinWidth(100));
|
|
EditorGUILayout.LabelField($" Started: {(M.TasksStarted[i] ? "☑" : "[ ]")}. Done: {(M.TasksDone[i] ? "☑" : "[ ]")}", GUILayout.MinWidth(100));
|
|
EditorGUILayout.LabelField($"Start Time: {M.TasksStartTime[i]:F2}", GUILayout.MinWidth(50));
|
|
EditorGUILayout.EndHorizontal();
|
|
}
|
|
|
|
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
{
|
|
EditorGUILayout.LabelField("Task Variables", EditorStyles.boldLabel);
|
|
for (int i = 0; i < M.currentState.tasks.Length; i++)
|
|
{
|
|
var TasksVars = serializedObject.FindProperty("TasksVars");
|
|
|
|
if (TasksVars != null && TasksVars.arraySize > i)
|
|
{
|
|
EditorGUI.indentLevel++;
|
|
EditorGUILayout.PropertyField(TasksVars.GetArrayElementAtIndex(i),
|
|
new GUIContent(M.currentState.tasks[i].name), true);
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
}
|
|
}
|
|
EditorGUILayout.EndVertical();
|
|
|
|
}
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
EditorGUILayout.Space();
|
|
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
{
|
|
EditorGUILayout.LabelField("Decision Variables", EditorStyles.boldLabel);
|
|
for (int i = 0; i < M.currentState.transitions.Length; i++)
|
|
{
|
|
var DecisionsVars = serializedObject.FindProperty("DecisionsVars");
|
|
var Des = M.currentState.transitions[i].decision;
|
|
|
|
var waiting = "";
|
|
|
|
if (Des.WaitForAllTasks && !M.AllTasksDone()) waiting = "[WAIT T*]";
|
|
if (Des.waitForTask != -1 && !M.IsTaskDone(Des.waitForTask)) waiting = "[WAIT T]";
|
|
|
|
|
|
EditorGUILayout.ObjectField($"Decision [{i }] {waiting}", Des, typeof(MAIDecision), false, GUILayout.MinWidth(100));
|
|
if (DecisionsVars != null && DecisionsVars.arraySize > i)
|
|
{
|
|
EditorGUI.indentLevel++;
|
|
EditorGUILayout.PropertyField(DecisionsVars.GetArrayElementAtIndex(i),
|
|
new GUIContent(M.currentState.transitions[i].decision.name), true);
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
}
|
|
}
|
|
EditorGUILayout.EndVertical();
|
|
|
|
EditorGUI.EndDisabledGroup();
|
|
}
|
|
}
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
|
|
private void DrawGeneral()
|
|
{
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
EditorGUILayout.PropertyField(Eyes);
|
|
EditorGUILayout.BeginHorizontal();
|
|
EditorGUILayout.PropertyField(currentState);
|
|
MalbersEditor.DrawDebugIcon(debug);
|
|
EditorGUILayout.EndHorizontal();
|
|
EditorGUILayout.Space(2);
|
|
// EditorGUILayout.PropertyField(remainInState);
|
|
EditorGUILayout.PropertyField(TransitionCoolDown);
|
|
EditorGUILayout.PropertyField(DisableAIOnDeath);
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
|
|
private void DrawEvents()
|
|
{
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
EditorGUILayout.PropertyField(OnAIStateChanged);
|
|
EditorGUILayout.PropertyField(OnTaskStarted);
|
|
EditorGUILayout.PropertyField(OnDecisionSucceded);
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
}
|
|
#endif
|
|
} |