Insanely huge initial commit

This commit is contained in:
2026-02-21 17:04:05 -08:00
parent 9cdd36191a
commit 613d75914a
22525 changed files with 4035207 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace MoreMountains.Tools
{
/// <summary>
/// Actions are behaviours and describe what your character is doing. Examples include patrolling, shooting, jumping, etc.
/// </summary>
public abstract class AIAction : MonoBehaviour
{
public enum InitializationModes { EveryTime, OnlyOnce, }
public InitializationModes InitializationMode;
protected bool _initialized;
public string Label;
public abstract void PerformAction();
public bool ActionInProgress { get; set; }
protected AIBrain _brain;
protected virtual bool ShouldInitialize
{
get
{
switch (InitializationMode)
{
case InitializationModes.EveryTime:
return true;
case InitializationModes.OnlyOnce:
return _initialized == false;
}
return true;
}
}
/// <summary>
/// On Awake we grab our AIBrain
/// </summary>
protected virtual void Awake()
{
_brain = this.gameObject.GetComponentInParent<AIBrain>();
}
/// <summary>
/// Initializes the action. Meant to be overridden
/// </summary>
public virtual void Initialization()
{
_initialized = true;
}
/// <summary>
/// Describes what happens when the brain enters the state this action is in. Meant to be overridden.
/// </summary>
public virtual void OnEnterState()
{
ActionInProgress = true;
}
/// <summary>
/// Describes what happens when the brain exits the state this action is in. Meant to be overridden.
/// </summary>
public virtual void OnExitState()
{
ActionInProgress = false;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4d7020c7ee7492e40848350adbd515be
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,262 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
namespace MoreMountains.Tools
{
/// <summary>
/// the AI brain is responsible from going from one state to the other based on the defined transitions. It's basically just a collection of states, and it's where you'll link all the actions, decisions, states and transitions together.
/// </summary>
[AddComponentMenu("More Mountains/Tools/AI/AIBrain")]
public class AIBrain : MonoBehaviour
{
[Header("Debug")]
/// the owner of that AI Brain, usually the associated character
[MMReadOnly]
public GameObject Owner;
/// the collection of states
public List<AIState> States;
/// this brain's current state
public AIState CurrentState { get; protected set; }
/// the time we've spent in the current state
[MMReadOnly]
public float TimeInThisState;
/// the current target
[MMReadOnly]
public Transform Target;
/// the last known world position of the target
[MMReadOnly]
public Vector3 _lastKnownTargetPosition = Vector3.zero;
[Header("State")]
/// whether or not this brain is active
public bool BrainActive = true;
public bool ResetBrainOnStart = true;
public bool ResetBrainOnEnable = false;
[Header("Frequencies")]
/// the frequency (in seconds) at which to perform actions (lower values : higher frequency, high values : lower frequency but better performance)
public float ActionsFrequency = 0f;
/// the frequency (in seconds) at which to evaluate decisions
public float DecisionFrequency = 0f;
/// whether or not to randomize the action and decision frequencies
public bool RandomizeFrequencies = false;
/// the min and max values between which to randomize the action frequency
[MMVector("min","max")]
public Vector2 RandomActionFrequency = new Vector2(0.5f, 1f);
/// the min and max values between which to randomize the decision frequency
[MMVector("min","max")]
public Vector2 RandomDecisionFrequency = new Vector2(0.5f, 1f);
protected AIDecision[] _decisions;
protected AIAction[] _actions;
protected float _lastActionsUpdate = 0f;
protected float _lastDecisionsUpdate = 0f;
protected AIState _initialState;
public virtual AIAction[] GetAttachedActions()
{
AIAction[] actions = this.gameObject.GetComponentsInChildren<AIAction>();
return actions;
}
public virtual AIDecision[] GetAttachedDecisions()
{
AIDecision[] decisions = this.gameObject.GetComponentsInChildren<AIDecision>();
return decisions;
}
protected void OnEnable()
{
if (ResetBrainOnEnable)
{
ResetBrain();
}
}
/// <summary>
/// On awake we set our brain for all states
/// </summary>
protected virtual void Awake()
{
foreach (AIState state in States)
{
state.SetBrain(this);
}
_decisions = GetAttachedDecisions();
_actions = GetAttachedActions();
if (RandomizeFrequencies)
{
ActionsFrequency = Random.Range(RandomActionFrequency.x, RandomActionFrequency.y);
DecisionFrequency = Random.Range(RandomDecisionFrequency.x, RandomDecisionFrequency.y);
}
}
/// <summary>
/// On Start we set our first state
/// </summary>
protected virtual void Start()
{
if (ResetBrainOnStart)
{
ResetBrain();
}
}
/// <summary>
/// Every frame we update our current state
/// </summary>
protected virtual void Update()
{
if (!BrainActive || (CurrentState == null) || (Time.timeScale == 0f))
{
return;
}
if (Time.time - _lastActionsUpdate > ActionsFrequency)
{
CurrentState.PerformActions();
_lastActionsUpdate = Time.time;
}
if (!BrainActive)
{
return;
}
if (Time.time - _lastDecisionsUpdate > DecisionFrequency)
{
CurrentState.EvaluateTransitions();
_lastDecisionsUpdate = Time.time;
}
TimeInThisState += Time.deltaTime;
StoreLastKnownPosition();
}
/// <summary>
/// Transitions to the specified state, trigger exit and enter states events
/// </summary>
/// <param name="newStateName"></param>
public virtual void TransitionToState(string newStateName)
{
if (CurrentState == null)
{
CurrentState = FindState(newStateName);
if (CurrentState != null)
{
CurrentState.EnterState();
}
return;
}
if (newStateName != CurrentState.StateName)
{
CurrentState.ExitState();
OnExitState();
CurrentState = FindState(newStateName);
if (CurrentState != null)
{
CurrentState.EnterState();
}
}
}
/// <summary>
/// When exiting a state we reset our time counter
/// </summary>
protected virtual void OnExitState()
{
TimeInThisState = 0f;
}
/// <summary>
/// Initializes all decisions
/// </summary>
protected virtual void InitializeDecisions()
{
if (_decisions == null)
{
_decisions = GetAttachedDecisions();
}
foreach(AIDecision decision in _decisions)
{
decision.Initialization();
}
}
/// <summary>
/// Initializes all actions
/// </summary>
protected virtual void InitializeActions()
{
if (_actions == null)
{
_actions = GetAttachedActions();
}
foreach(AIAction action in _actions)
{
action.Initialization();
}
}
/// <summary>
/// Returns a state based on the specified state name
/// </summary>
/// <param name="stateName"></param>
/// <returns></returns>
protected AIState FindState(string stateName)
{
foreach (AIState state in States)
{
if (state.StateName == stateName)
{
return state;
}
}
if (stateName != "")
{
Debug.LogError("You're trying to transition to state '" + stateName + "' in " + this.gameObject.name + "'s AI Brain, but no state of this name exists. Make sure your states are named properly, and that your transitions states match existing states.");
}
return null;
}
/// <summary>
/// Stores the last known position of the target
/// </summary>
protected virtual void StoreLastKnownPosition()
{
if (Target != null)
{
_lastKnownTargetPosition = Target.transform.position;
}
}
/// <summary>
/// Resets the brain, forcing it to enter its first state
/// </summary>
public virtual void ResetBrain()
{
InitializeDecisions();
InitializeActions();
BrainActive = true;
this.enabled = true;
if (CurrentState != null)
{
CurrentState.ExitState();
OnExitState();
}
if (States.Count > 0)
{
CurrentState = States[0];
CurrentState?.EnterState();
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: eec89e4158bf96841b9bc830fc5385ca
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,52 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace MoreMountains.Tools
{
/// <summary>
/// Decisions are components that will be evaluated by transitions, every frame, and will return true or false. Examples include time spent in a state, distance to a target, or object detection within an area.
/// </summary>
public abstract class AIDecision : MonoBehaviour
{
/// Decide will be performed every frame while the Brain is in a state this Decision is in. Should return true or false, which will then determine the transition's outcome.
public abstract bool Decide();
public string Label;
public bool DecisionInProgress { get; set; }
protected AIBrain _brain;
/// <summary>
/// On Awake we grab our Brain
/// </summary>
protected virtual void Awake()
{
_brain = this.gameObject.GetComponentInParent<AIBrain>();
}
/// <summary>
/// Meant to be overridden, called when the game starts
/// </summary>
public virtual void Initialization()
{
}
/// <summary>
/// Meant to be overridden, called when the Brain enters a State this Decision is in
/// </summary>
public virtual void OnEnterState()
{
DecisionInProgress = true;
}
/// <summary>
/// Meant to be overridden, called when the Brain exits a State this Decision is in
/// </summary>
public virtual void OnExitState()
{
DecisionInProgress = false;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 86aa60c7eb6e3fe4a8c3624c6b3f1abc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,131 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace MoreMountains.Tools
{
[System.Serializable]
public class AIActionsList : MMReorderableArray<AIAction>
{
}
[System.Serializable]
public class AITransitionsList : MMReorderableArray<AITransition>
{
}
/// <summary>
/// A State is a combination of one or more actions, and one or more transitions. An example of a state could be "_patrolling until an enemy gets in range_".
/// </summary>
[System.Serializable]
public class AIState
{
/// the name of the state (will be used as a reference in Transitions
public string StateName;
[MMReorderableAttribute(null, "Action", null)]
public AIActionsList Actions;
[MMReorderableAttribute(null, "Transition", null)]
public AITransitionsList Transitions;/*
/// a list of actions to perform in this state
public List<AIAction> Actions;
/// a list of transitions to evaluate to exit this state
public List<AITransition> Transitions;*/
protected AIBrain _brain;
/// <summary>
/// Sets this state's brain to the one specified in parameters
/// </summary>
/// <param name="brain"></param>
public virtual void SetBrain(AIBrain brain)
{
_brain = brain;
}
/// <summary>
/// On enter state we pass that info to our actions and decisions
/// </summary>
public virtual void EnterState()
{
foreach (AIAction action in Actions)
{
action.OnEnterState();
}
foreach (AITransition transition in Transitions)
{
if (transition.Decision != null)
{
transition.Decision.OnEnterState();
}
}
}
/// <summary>
/// On exit state we pass that info to our actions and decisions
/// </summary>
public virtual void ExitState()
{
foreach (AIAction action in Actions)
{
action.OnExitState();
}
foreach (AITransition transition in Transitions)
{
if (transition.Decision != null)
{
transition.Decision.OnExitState();
}
}
}
/// <summary>
/// Performs this state's actions
/// </summary>
public virtual void PerformActions()
{
if (Actions.Count == 0) { return; }
for (int i=0; i<Actions.Count; i++)
{
if (Actions[i] != null)
{
Actions[i].PerformAction();
}
else
{
Debug.LogError("An action in " + _brain.gameObject.name + " on state " + StateName + " is null.");
}
}
}
/// <summary>
/// Tests this state's transitions
/// </summary>
public virtual void EvaluateTransitions()
{
if (Transitions.Count == 0) { return; }
for (int i = 0; i < Transitions.Count; i++)
{
if (Transitions[i].Decision != null)
{
if (Transitions[i].Decision.Decide())
{
if (!string.IsNullOrEmpty(Transitions[i].TrueState))
{
_brain.TransitionToState(Transitions[i].TrueState);
break;
}
}
else
{
if (!string.IsNullOrEmpty(Transitions[i].FalseState))
{
_brain.TransitionToState(Transitions[i].FalseState);
break;
}
}
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6e1187ab043b6154493c3407ed149566
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,20 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace MoreMountains.Tools
{
/// <summary>
/// Transitions are a combination of one or more decisions and destination states whether or not these transitions are true or false. An example of a transition could be "_if an enemy gets in range, transition to the Shooting state_".
/// </summary>
[System.Serializable]
public class AITransition
{
/// this transition's decision
public AIDecision Decision;
/// the state to transition to if this Decision returns true
public string TrueState;
/// the state to transition to if this Decision returns false
public string FalseState;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 36bf680dabd04c14288ec99ad204ee89
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: