first commit

This commit is contained in:
Chris
2025-03-12 14:22:16 -04:00
commit 0ad0c01249
1999 changed files with 189708 additions and 0 deletions

View File

@@ -0,0 +1,234 @@
using System.Linq;
using System.Collections.Generic;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using UnityEngine;
using Logger = ParadoxNotion.Services.Logger;
using ParadoxNotion;
namespace NodeCanvas.StateMachines
{
///<summary> Use FSMs to create state like behaviours</summary>
[GraphInfo(
packageName = "NodeCanvas",
docsURL = "https://nodecanvas.paradoxnotion.com/documentation/",
resourcesURL = "https://nodecanvas.paradoxnotion.com/downloads/",
forumsURL = "https://nodecanvas.paradoxnotion.com/forums-page/"
)]
[CreateAssetMenu(menuName = "ParadoxNotion/NodeCanvas/FSM Asset")]
public class FSM : Graph
{
///<summary>Transition Calling Mode (see "EnterState")</summary>
public enum TransitionCallMode
{
Normal = 0,
Stacked = 1,
Clean = 2,
}
private List<IUpdatable> updatableNodes;
private IStateCallbackReceiver[] callbackReceivers;
private Stack<FSMState> stateStack;
private bool enterStartStateFlag;
public event System.Action<IState> onStateEnter;
public event System.Action<IState> onStateUpdate;
public event System.Action<IState> onStateExit;
public event System.Action<IState> onStateTransition;
///<summary>The current FSM state</summary>
public FSMState currentState { get; private set; }
///<summary>The previous FSM state</summary>
public FSMState previousState { get; private set; }
///<summary>The current state name. Null if none</summary>
public string currentStateName => currentState != null ? currentState.name : null;
///<summary>The previous state name. Null if none</summary>
public string previousStateName => previousState != null ? previousState.name : null;
public override System.Type baseNodeType => typeof(FSMNode);
public override bool requiresAgent => true;
public override bool requiresPrimeNode => true;
public override bool isTree => false;
public override bool allowBlackboardOverrides => true;
sealed public override bool canAcceptVariableDrops => false;
public sealed override PlanarDirection flowDirection => PlanarDirection.Auto;
///----------------------------------------------------------------------------------------------
protected override void OnGraphInitialize() {
//we may be loading in async
ThreadSafeInitCall(GatherCallbackReceivers);
updatableNodes = new List<IUpdatable>();
for ( var i = 0; i < allNodes.Count; i++ ) {
if ( allNodes[i] is IUpdatable ) {
updatableNodes.Add((IUpdatable)allNodes[i]);
}
}
}
protected override void OnGraphStarted() {
stateStack = new Stack<FSMState>();
enterStartStateFlag = true;
}
protected override void OnGraphUpdate() {
if ( enterStartStateFlag ) {
//use a flag so that other nodes can do stuff on graph started
enterStartStateFlag = false;
EnterState((FSMState)primeNode, TransitionCallMode.Normal);
}
if ( currentState != null ) {
//Update defer IUpdatables
for ( var i = 0; i < updatableNodes.Count; i++ ) {
updatableNodes[i].Update();
}
//this can only happen if FSM stoped just now (from the above update)
if ( currentState == null ) { Stop(false); return; }
//Update current state
currentState.Execute(agent, blackboard);
//this can only happen if FSM stoped just now (from the above update)
if ( currentState == null ) { Stop(false); return; }
if ( onStateUpdate != null && currentState.status == Status.Running ) {
onStateUpdate(currentState);
}
//this can only happen if FSM stoped just now (from the above update)
if ( currentState == null ) { Stop(false); return; }
//state has nowhere to go..
if ( currentState.status != Status.Running && currentState.outConnections.Count == 0 ) {
//...but we have a stacked state -> pop return to it
if ( stateStack.Count > 0 ) {
var popState = stateStack.Pop();
EnterState(popState, TransitionCallMode.Normal);
return;
}
//...and no updatables -> stop
if ( !updatableNodes.Any(n => n.status == Status.Running) ) {
Stop(true);
return;
}
}
}
//if null state, stop.
if ( currentState == null ) {
Stop(false);
return;
}
}
protected override void OnGraphStoped() {
if ( currentState != null ) {
if ( onStateExit != null ) {
onStateExit(currentState);
}
}
previousState = null;
currentState = null;
stateStack = null;
}
///<summary>Enter a state providing the state itself</summary>
public bool EnterState(FSMState newState, TransitionCallMode callMode) {
if ( !isRunning ) {
Logger.LogWarning("Tried to EnterState on an FSM that was not running", LogTag.EXECUTION, this);
return false;
}
if ( newState == null ) {
Logger.LogWarning("Tried to Enter Null State", LogTag.EXECUTION, this);
return false;
}
if ( currentState != null ) {
if ( onStateExit != null ) { onStateExit(currentState); }
currentState.Reset(false);
if ( callMode == TransitionCallMode.Stacked ) {
stateStack.Push(currentState);
if ( stateStack.Count > 5 ) {
Logger.LogWarning("State stack exceeds 5. Ensure that you are not cycling stack calls", LogTag.EXECUTION, this);
}
}
}
if ( callMode == TransitionCallMode.Clean ) {
stateStack.Clear();
}
previousState = currentState;
currentState = newState;
if ( onStateTransition != null ) { onStateTransition(currentState); }
if ( onStateEnter != null ) { onStateEnter(currentState); }
currentState.Execute(agent, blackboard);
return true;
}
///<summary>Trigger a state to enter by it's name. Returns the state found and entered if any</summary>
public FSMState TriggerState(string stateName, TransitionCallMode callMode) {
var state = GetStateWithName(stateName);
if ( state != null ) {
EnterState(state, callMode);
return state;
}
Logger.LogWarning("No State with name '" + stateName + "' found on FSM '" + name + "'", LogTag.EXECUTION, this);
return null;
}
///<summary>Get all State Names</summary>
public string[] GetStateNames() {
return allNodes.Where(n => n is FSMState).Select(n => n.name).ToArray();
}
///<summary>Get a state by it's name</summary>
public FSMState GetStateWithName(string name) {
return (FSMState)allNodes.Find(n => n is FSMState && n.name == name);
}
//Gather IStateCallbackReceivers and subscribe them to state events
void GatherCallbackReceivers() {
if ( agent == null ) { return; }
callbackReceivers = agent.gameObject.GetComponents<IStateCallbackReceiver>();
if ( callbackReceivers.Length > 0 ) {
onStateEnter += (x) => { foreach ( var m in callbackReceivers ) m.OnStateEnter(x); };
onStateUpdate += (x) => { foreach ( var m in callbackReceivers ) m.OnStateUpdate(x); };
onStateExit += (x) => { foreach ( var m in callbackReceivers ) m.OnStateExit(x); };
}
}
public FSMState PeekStack() {
return stateStack != null && stateStack.Count > 0 ? stateStack.Peek() : null;
}
///----------------------------------------------------------------------------------------------
///---------------------------------------UNITY EDITOR-------------------------------------------
#if UNITY_EDITOR
[UnityEditor.MenuItem("Tools/ParadoxNotion/NodeCanvas/Create/State Machine Asset", false, 1)]
static void Editor_CreateGraph() {
var newGraph = EditorUtils.CreateAsset<FSM>();
UnityEditor.Selection.activeObject = newGraph;
}
#endif
}
}

View File

@@ -0,0 +1,24 @@
fileFormatVersion: 2
guid: f945e777233a59f4aba40aeca29093a6
labels:
- AI
- visualscripting
- FSM
- state
- flow
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 14914
packageName: NodeCanvas
packageVersion: 3.3.1
assetPath: Assets/ParadoxNotion/NodeCanvas/Modules/StateMachines/FSM.cs
uploadId: 704937

View File

@@ -0,0 +1,78 @@
using NodeCanvas.Framework;
using UnityEngine;
namespace NodeCanvas.StateMachines
{
///<summary>The connection object for FSM nodes. AKA Transitions</summary>
public class FSMConnection : Connection, ITaskAssignable<ConditionTask>
{
[SerializeField]
private FSM.TransitionCallMode _transitionCallMode;
[SerializeField]
private ConditionTask _condition;
public ConditionTask condition {
get { return _condition; }
set { _condition = value; }
}
public Task task {
get { return condition; }
set { condition = (ConditionTask)value; }
}
public FSM.TransitionCallMode transitionCallMode {
get { return _transitionCallMode; }
private set { _transitionCallMode = value; }
}
//...
public void EnableCondition(Component agent, IBlackboard blackboard) {
if ( condition != null ) {
condition.Enable(agent, blackboard);
}
}
//...
public void DisableCondition() {
if ( condition != null ) {
condition.Disable();
}
}
///<summary>Perform the transition disregarding whether or not the condition (if any) is valid</summary>
public void PerformTransition() {
( graph as FSM ).EnterState((FSMState)targetNode, transitionCallMode);
}
///----------------------------------------------------------------------------------------------
///---------------------------------------UNITY EDITOR-------------------------------------------
#if UNITY_EDITOR
public override TipConnectionStyle tipConnectionStyle {
get { return TipConnectionStyle.Arrow; }
}
public override bool animate {
get { return status == Status.Failure; }
}
protected override string GetConnectionInfo() {
var result = transitionCallMode == FSM.TransitionCallMode.Normal ? string.Empty : string.Format("<b>[{0}]</b>\n", transitionCallMode.ToString());
result += condition != null ? condition.summaryInfo : "OnFinish";
return result;
}
protected override void OnConnectionInspectorGUI() {
UnityEditor.EditorGUILayout.HelpBox("Stacked Call Mode will push the current state to the stack and pop return to it later when another finished state without outgoing transitions has been encountered within the FSM. If you decide to use this feature make sure that you are not cycle stacking states.\nA Clean transition will clear the FSM stack.", UnityEditor.MessageType.None);
transitionCallMode = (FSM.TransitionCallMode)UnityEditor.EditorGUILayout.EnumPopup("Call Mode (Experimental)", transitionCallMode);
ParadoxNotion.Design.EditorUtils.Separator();
NodeCanvas.Editor.TaskEditor.TaskFieldMulti<ConditionTask>(condition, graph, (c) => { condition = c; });
}
#endif
}
}

View File

@@ -0,0 +1,23 @@
fileFormatVersion: 2
guid: 3d69dfa1e1e10d2448da5e61340175dd
labels:
- AI
- visualscripting
- FSM
- state
- flow
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 14914
packageName: NodeCanvas
packageVersion: 3.3.1
assetPath: Assets/ParadoxNotion/NodeCanvas/Modules/StateMachines/FSMConnection.cs
uploadId: 704937

View File

@@ -0,0 +1,230 @@
using NodeCanvas.Framework;
using ParadoxNotion;
using UnityEngine;
namespace NodeCanvas.StateMachines
{
///<summary> Super base class for FSM nodes that live within an FSM Graph.</summary>
public abstract class FSMNode : Node
{
public override bool allowAsPrime { get { return false; } }
public override bool canSelfConnect { get { return false; } }
public override int maxInConnections { get { return -1; } }
public override int maxOutConnections { get { return -1; } }
sealed public override System.Type outConnectionType { get { return typeof(FSMConnection); } }
sealed public override Alignment2x2 commentsAlignment { get { return Alignment2x2.Bottom; } }
sealed public override Alignment2x2 iconAlignment { get { return Alignment2x2.Bottom; } }
///<summary>The FSM this state belongs to</summary>
public FSM FSM { get { return (FSM)graph; } }
///----------------------------------------------------------------------------------------------
///---------------------------------------UNITY EDITOR-------------------------------------------
#if UNITY_EDITOR
private static GUIPort clickedPort { get; set; }
private static int dragDropMisses { get; set; }
class GUIPort
{
public FSMNode parent { get; private set; }
public Vector2 pos { get; private set; }
public GUIPort(FSMNode parent, Vector2 pos) {
this.parent = parent;
this.pos = pos;
}
}
//Draw the ports and connections
sealed protected override void DrawNodeConnections(Rect drawCanvas, bool fullDrawPass, Vector2 canvasMousePos, float zoomFactor) {
var e = Event.current;
//Receive connections first
if ( clickedPort != null && e.type == EventType.MouseUp && e.button == 0 ) {
if ( rect.Contains(e.mousePosition) ) {
graph.ConnectNodes(clickedPort.parent, this);
clickedPort = null;
e.Use();
} else {
dragDropMisses++;
if ( dragDropMisses == graph.allNodes.Count && clickedPort != null ) {
var source = clickedPort.parent;
var pos = Event.current.mousePosition;
var menu = new UnityEditor.GenericMenu();
clickedPort = null;
menu.AddItem(new GUIContent("Add Action State"), false, () =>
{
var newState = graph.AddNode<ActionState>(pos);
graph.ConnectNodes(source, newState);
});
//PostGUI cause of zoom factors
Editor.GraphEditorUtility.PostGUI += () => { menu.ShowAsContext(); };
Event.current.Use();
e.Use();
}
}
}
var portRectLeft = new Rect(0, 0, 20, 20);
var portRectRight = new Rect(0, 0, 20, 20);
var portRectBottom = new Rect(0, 0, 20, 20);
portRectLeft.center = new Vector2(rect.x - 11, rect.center.y);
portRectRight.center = new Vector2(rect.xMax + 11, rect.center.y);
portRectBottom.center = new Vector2(rect.center.x, rect.yMax + 11);
if ( maxOutConnections != 0 ) {
if ( fullDrawPass || drawCanvas.Overlaps(rect) ) {
UnityEditor.EditorGUIUtility.AddCursorRect(portRectLeft, UnityEditor.MouseCursor.ArrowPlus);
UnityEditor.EditorGUIUtility.AddCursorRect(portRectRight, UnityEditor.MouseCursor.ArrowPlus);
UnityEditor.EditorGUIUtility.AddCursorRect(portRectBottom, UnityEditor.MouseCursor.ArrowPlus);
GUI.color = new Color(1, 1, 1, 0.3f);
GUI.DrawTexture(portRectLeft, Editor.StyleSheet.arrowLeft);
GUI.DrawTexture(portRectRight, Editor.StyleSheet.arrowRight);
if ( maxInConnections == 0 ) {
GUI.DrawTexture(portRectBottom, Editor.StyleSheet.arrowBottom);
}
GUI.color = Color.white;
if ( Editor.GraphEditorUtility.allowClick && e.type == EventType.MouseDown && e.button == 0 ) {
if ( portRectLeft.Contains(e.mousePosition) ) {
clickedPort = new GUIPort(this, portRectLeft.center);
dragDropMisses = 0;
e.Use();
}
if ( portRectRight.Contains(e.mousePosition) ) {
clickedPort = new GUIPort(this, portRectRight.center);
dragDropMisses = 0;
e.Use();
}
if ( maxInConnections == 0 && portRectBottom.Contains(e.mousePosition) ) {
clickedPort = new GUIPort(this, portRectBottom.center);
dragDropMisses = 0;
e.Use();
}
}
}
}
//draw new linking
if ( clickedPort != null && clickedPort.parent == this ) {
UnityEditor.Handles.DrawBezier(clickedPort.pos, e.mousePosition, clickedPort.pos, e.mousePosition, new Color(0.5f, 0.5f, 0.8f, 0.8f), Editor.StyleSheet.bezierTexture, 2);
}
//draw out connections
for ( var i = 0; i < outConnections.Count; i++ ) {
var connection = outConnections[i] as FSMConnection;
var targetState = connection.targetNode as FSMNode;
if ( targetState == null ) { //In case of MissingNode type
continue;
}
var targetPos = targetState.GetConnectedInPortPosition(connection);
var sourcePos = Vector2.zero;
if ( rect.center.x <= targetPos.x ) {
sourcePos = portRectRight.center;
}
if ( rect.center.x > targetPos.x ) {
sourcePos = portRectLeft.center;
}
if ( maxInConnections == 0 && rect.center.y < targetPos.y - 50 && Mathf.Abs(rect.center.x - targetPos.x) < 200 ) {
sourcePos = portRectBottom.center;
}
var boundRect = RectUtils.GetBoundRect(sourcePos, targetPos);
if ( fullDrawPass || drawCanvas.Overlaps(boundRect) ) {
connection.DrawConnectionGUI(sourcePos, targetPos);
}
}
}
//...
Vector2 GetConnectedInPortPosition(Connection connection) {
var sourcePos = connection.sourceNode.rect.center;
var thisPos = rect.center;
var style = 0;
if ( style == 0 ) {
if ( sourcePos.x <= thisPos.x ) {
if ( sourcePos.y <= thisPos.y ) {
return new Vector2(rect.center.x - 15, rect.yMin - ( this == graph.primeNode ? 20 : 0 ));
} else {
return new Vector2(rect.center.x - 15, rect.yMax + 2);
}
}
if ( sourcePos.x > thisPos.x ) {
if ( sourcePos.y <= thisPos.y ) {
return new Vector2(rect.center.x + 15, rect.yMin - ( this == graph.primeNode ? 20 : 0 ));
} else {
return new Vector2(rect.center.x + 15, rect.yMax + 2);
}
}
}
// //Another idea
// if (style == 1){
// if (sourcePos.x <= thisPos.x){
// if (sourcePos.y >= thisPos.y){
// return new Vector2(rect.xMin - 3, rect.yMax - 10);
// } else {
// return new Vector2(rect.xMin - 3, rect.yMin + 10);
// }
// }
// if (sourcePos.x > thisPos.x){
// if (sourcePos.y >= thisPos.y){
// return new Vector2(rect.center.x, rect.yMax + 2);
// } else {
// return new Vector2(rect.center.x, rect.yMin - (this == graph.primeNode? 20 : 0 ));
// }
// }
// }
// //YET Another idea
// if (style >= 2){
// if (sourcePos.x <= thisPos.x){
// if (sourcePos.y >= thisPos.y){
// return new Vector2(rect.xMin - 3, rect.yMax - 10);
// } else {
// return new Vector2(rect.xMin - 3, rect.yMin + 10);
// }
// }
// if (sourcePos.x > thisPos.x){
// if (sourcePos.y >= thisPos.y){
// return new Vector2(rect.xMax + 3, rect.yMax - 10);
// } else {
// return new Vector2(rect.xMax + 3, rect.yMin + 10);
// }
// }
// }
return thisPos;
}
#endif
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 5cb7af7f552ce3749a4ebe68c9e0fd32
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 14914
packageName: NodeCanvas
packageVersion: 3.3.1
assetPath: Assets/ParadoxNotion/NodeCanvas/Modules/StateMachines/FSMNode.cs
uploadId: 704937

View File

@@ -0,0 +1,26 @@
using System.Collections.Generic;
using NodeCanvas.Framework;
using NodeCanvas.Framework.Internal;
using ParadoxNotion.Design;
using UnityEngine;
namespace NodeCanvas.StateMachines
{
[Category("SubGraphs")]
[Color("ffe4e1")]
abstract public class FSMNodeNested<T> : FSMNode, IGraphAssignable<T> where T : Graph
{
[SerializeField] private List<BBMappingParameter> _variablesMap;
abstract public T subGraph { get; set; }
abstract public BBParameter subGraphParameter { get; }
public T currentInstance { get; set; }
public Dictionary<Graph, Graph> instances { get; set; }
public List<BBMappingParameter> variablesMap { get { return _variablesMap; } set { _variablesMap = value; } }
Graph IGraphAssignable.subGraph { get { return subGraph; } set { subGraph = (T)value; } }
Graph IGraphAssignable.currentInstance { get { return currentInstance; } set { currentInstance = (T)value; } }
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: b98b283dbb2c2b44a8b5ca06796167df
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 14914
packageName: NodeCanvas
packageVersion: 3.3.1
assetPath: Assets/ParadoxNotion/NodeCanvas/Modules/StateMachines/FSMNodeNested.cs
uploadId: 704937

View File

@@ -0,0 +1,70 @@
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using UnityEngine;
namespace NodeCanvas.StateMachines
{
///<summary> Add this component on a gameobject to behave based on an FSM.</summary>
[AddComponentMenu("NodeCanvas/FSM Owner")]
public class FSMOwner : GraphOwner<FSM>
{
///<summary>The current state name of the root fsm.</summary>
public string currentRootStateName => behaviour?.currentStateName;
///<summary>The previous state name of the root fsm.</summary>
public string previousRootStateName => behaviour?.previousStateName;
///<summary>The current deep state name of the fsm including sub fsms if any.</summary>
public string currentDeepStateName => GetCurrentState(true)?.name;
///<summary>The previous deep state name of the fsm including sub fsms if any.</summary>
public string previousDeepStateName => GetPreviousState(true)?.name;
///<summary>Returns the current fsm state optionally recursively by including SubFSMs.</summary>
public IState GetCurrentState(bool includeSubFSMs = true) {
if ( behaviour == null ) { return null; }
var current = behaviour.currentState;
if ( includeSubFSMs ) {
while ( current is NestedFSMState subState ) {
current = subState.currentInstance?.currentState;
}
}
return current;
}
///<summary>Returns the previous fsm state optionally recursively by including SubFSMs.</summary>
public IState GetPreviousState(bool includeSubFSMs = true) {
if ( behaviour == null ) { return null; }
var current = behaviour.currentState;
var previous = behaviour.previousState;
if ( includeSubFSMs ) {
while ( current is NestedFSMState subState ) {
current = subState.currentInstance?.currentState;
previous = subState.currentInstance?.previousState;
}
}
return previous;
}
///<summary>Enter a state of the root FSM by its name.</summary>
public IState TriggerState(string stateName) { return TriggerState(stateName, FSM.TransitionCallMode.Normal); }
public IState TriggerState(string stateName, FSM.TransitionCallMode callMode) {
return behaviour?.TriggerState(stateName, callMode);
}
///<summary>Get all root state names, excluding non-named states.</summary>
public string[] GetStateNames() {
return behaviour?.GetStateNames();
}
#if UNITY_EDITOR
protected override void OnDrawGizmos() {
UnityEditor.Handles.Label(transform.position + new Vector3(0, -0.3f, 0), currentDeepStateName, Styles.centerLabel);
}
#endif
}
}

View File

@@ -0,0 +1,24 @@
fileFormatVersion: 2
guid: 4a7dbb6bbdf4ffb46ab7dd87ba8191e7
labels:
- AI
- visualscripting
- FSM
- state
- flow
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: b5eeec78a0081094cb8dfeb83808d762, type: 3}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 14914
packageName: NodeCanvas
packageVersion: 3.3.1
assetPath: Assets/ParadoxNotion/NodeCanvas/Modules/StateMachines/FSMOwner.cs
uploadId: 704937

View File

@@ -0,0 +1,227 @@
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using UnityEngine;
using Logger = ParadoxNotion.Services.Logger;
namespace NodeCanvas.StateMachines
{
///<summary> Base class for fsm nodes that are actually states</summary>
// [Color("ff6d53")]
abstract public class FSMState : FSMNode, IState
{
public enum TransitionEvaluationMode
{
CheckContinuously,
CheckAfterStateFinished,
CheckManually
}
[SerializeField]
private TransitionEvaluationMode _transitionEvaluation;
private bool _hasInit;
public override bool allowAsPrime => true;
public override bool canSelfConnect => true;
public override int maxInConnections => -1;
public override int maxOutConnections => -1;
public TransitionEvaluationMode transitionEvaluation {
get { return _transitionEvaluation; }
set { _transitionEvaluation = value; }
}
///<summary>Returns all transitions of the state</summary>
public FSMConnection[] GetTransitions() {
var result = new FSMConnection[outConnections.Count];
for ( var i = 0; i < outConnections.Count; i++ ) {
result[i] = (FSMConnection)outConnections[i];
}
return result;
}
///<summary>Declares that the state has finished</summary>
public void Finish() { Finish(Status.Success); }
public void Finish(bool inSuccess) { Finish(inSuccess ? Status.Success : Status.Failure); }
public void Finish(Status status) { this.status = status; }
///----------------------------------------------------------------------------------------------
public override void OnGraphPaused() { if ( status == Status.Running ) { OnPause(); } }
///----------------------------------------------------------------------------------------------
//avoid connecting from same source
protected override bool CanConnectFromSource(Node sourceNode) {
if ( this.IsChildOf(sourceNode) ) {
Logger.LogWarning("States are already connected together. Consider using multiple conditions on an existing transition instead", LogTag.EDITOR, this);
return false;
}
return true;
}
//avoid connecting to same target
protected override bool CanConnectToTarget(Node targetNode) {
if ( this.IsParentOf(targetNode) ) {
Logger.LogWarning("States are already connected together. Consider using multiple conditions on an existing transition instead", LogTag.EDITOR, this);
return false;
}
return true;
}
//OnEnter...
sealed protected override Status OnExecute(Component agent, IBlackboard bb) {
if ( !_hasInit ) {
_hasInit = true;
OnInit();
}
if ( status == Status.Resting ) {
status = Status.Running;
for ( int i = 0; i < outConnections.Count; i++ ) {
( (FSMConnection)outConnections[i] ).EnableCondition(agent, bb);
}
OnEnter();
} else {
var case1 = transitionEvaluation == TransitionEvaluationMode.CheckContinuously;
var case2 = transitionEvaluation == TransitionEvaluationMode.CheckAfterStateFinished && status != Status.Running;
if ( case1 || case2 ) {
CheckTransitions();
}
if ( status == Status.Running ) {
OnUpdate();
}
}
return status;
}
///<summary>Checks and performs possible transition. Returns true if a transition was performed.</summary>
public bool CheckTransitions() {
for ( var i = 0; i < outConnections.Count; i++ ) {
var connection = (FSMConnection)outConnections[i];
var condition = connection.condition;
if ( !connection.isActive ) {
continue;
}
if ( ( condition != null && condition.Check(graphAgent, graphBlackboard) ) || ( condition == null && status != Status.Running ) ) {
FSM.EnterState((FSMState)connection.targetNode, connection.transitionCallMode);
connection.status = Status.Success; //editor vis
return true;
}
connection.status = Status.Failure; //editor vis
}
return false;
}
//OnExit...
sealed protected override void OnReset() {
for ( int i = 0; i < outConnections.Count; i++ ) {
( (FSMConnection)outConnections[i] ).DisableCondition();
}
#if UNITY_EDITOR
//Done for visualizing in editor
for ( var i = 0; i < inConnections.Count; i++ ) {
inConnections[i].status = Status.Resting;
}
#endif
OnExit();
}
//Converted
virtual protected void OnInit() { }
virtual protected void OnEnter() { }
virtual protected void OnUpdate() { }
virtual protected void OnExit() { }
virtual protected void OnPause() { }
//
///----------------------------------------------------------------------------------------------
///---------------------------------------UNITY EDITOR-------------------------------------------
#if UNITY_EDITOR
//just a default orange color
public override void OnCreate(Graph assignedGraph) {
base.customColor = new Color(1, 0.42f, 0.32f);
}
//...
protected override void OnNodeInspectorGUI() {
ShowTransitionsInspector();
DrawDefaultInspector();
}
protected override void OnNodeExternalGUI() {
var peek = FSM.PeekStack();
if ( peek != null && FSM.currentState == this ) {
UnityEditor.Handles.color = Color.grey;
UnityEditor.Handles.DrawAAPolyLine(rect.center, peek.rect.center);
UnityEditor.Handles.color = Color.white;
}
}
//...
protected override UnityEditor.GenericMenu OnContextMenu(UnityEditor.GenericMenu menu) {
if ( Application.isPlaying ) {
menu.AddItem(new GUIContent("Enter State"), false, () => { FSM.EnterState(this, FSM.TransitionCallMode.Normal); });
} else { menu.AddDisabledItem(new GUIContent("Enter State")); }
menu.AddItem(new GUIContent("Breakpoint"), isBreakpoint, () => { isBreakpoint = !isBreakpoint; });
return menu;
}
//...
protected void ShowTransitionsInspector() {
EditorUtils.CoolLabel("Transitions");
if ( outConnections.Count == 0 ) {
UnityEditor.EditorGUILayout.HelpBox("No Transition", UnityEditor.MessageType.None);
}
var onFinishExists = false;
EditorUtils.ReorderableList(outConnections, (i, picked) =>
{
var connection = (FSMConnection)outConnections[i];
GUILayout.BeginHorizontal("box");
if ( connection.condition != null ) {
GUILayout.Label(connection.condition.summaryInfo, GUILayout.MinWidth(0), GUILayout.ExpandWidth(true));
} else {
GUILayout.Label("OnFinish" + ( onFinishExists ? " (exists)" : string.Empty ), GUILayout.MinWidth(0), GUILayout.ExpandWidth(true));
onFinishExists = true;
}
GUILayout.FlexibleSpace();
GUILayout.Label("► '" + connection.targetNode.name + "'");
GUILayout.EndHorizontal();
});
transitionEvaluation = (TransitionEvaluationMode)UnityEditor.EditorGUILayout.EnumPopup(transitionEvaluation);
EditorUtils.BoldSeparator();
}
#endif
///----------------------------------------------------------------------------------------------
}
}

View File

@@ -0,0 +1,23 @@
fileFormatVersion: 2
guid: 48b2cee75b6f6b54299cda56fe9f6bad
labels:
- AI
- visualscripting
- FSM
- state
- flow
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 14914
packageName: NodeCanvas
packageVersion: 3.3.1
assetPath: Assets/ParadoxNotion/NodeCanvas/Modules/StateMachines/FSMState.cs
uploadId: 704937

View File

@@ -0,0 +1,26 @@
using System.Collections.Generic;
using NodeCanvas.Framework;
using NodeCanvas.Framework.Internal;
using ParadoxNotion.Design;
using UnityEngine;
namespace NodeCanvas.StateMachines
{
[Category("SubGraphs")]
[Color("ffe4e1")]
abstract public class FSMStateNested<T> : FSMState, IGraphAssignable<T> where T : Graph
{
[SerializeField] private List<BBMappingParameter> _variablesMap;
abstract public T subGraph { get; set; }
abstract public BBParameter subGraphParameter { get; }
public T currentInstance { get; set; }
public Dictionary<Graph, Graph> instances { get; set; }
public List<BBMappingParameter> variablesMap { get { return _variablesMap; } set { _variablesMap = value; } }
Graph IGraphAssignable.subGraph { get { return subGraph; } set { subGraph = (T)value; } }
Graph IGraphAssignable.currentInstance { get { return currentInstance; } set { currentInstance = (T)value; } }
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: d4794125a8045f14c984577a1089b4f6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 14914
packageName: NodeCanvas
packageVersion: 3.3.1
assetPath: Assets/ParadoxNotion/NodeCanvas/Modules/StateMachines/FSMStateNested.cs
uploadId: 704937

View File

@@ -0,0 +1,22 @@
namespace NodeCanvas.StateMachines
{
//An interface for states. Works together with IStateCallbackReceiver
public interface IState
{
///<summary>The name of the state</summary>
string name { get; }
///<summary>The tag of the state</summary>
string tag { get; }
///<summary>The elapsed time of the state</summary>
float elapsedTime { get; }
///<summary>The FSM this state belongs to</summary>
FSM FSM { get; }
///<summary>An array of the state's transition connections</summary>
FSMConnection[] GetTransitions();
///<summary>Evaluates the state's transitions and returns true if a transition has been performed</summary>
bool CheckTransitions();
///<summary>Marks the state as Finished</summary>
void Finish(bool success);
}
}

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: f08e7012c92f50c47a70934e30c10ec1
timeCreated: 1570883650
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 14914
packageName: NodeCanvas
packageVersion: 3.3.1
assetPath: Assets/ParadoxNotion/NodeCanvas/Modules/StateMachines/IState.cs
uploadId: 704937

View File

@@ -0,0 +1,14 @@
namespace NodeCanvas.StateMachines
{
///<summary>Implement this interface in any MonoBehaviour attached on FSMOwner gameobject to get relevant state callbacks</summary>
public interface IStateCallbackReceiver
{
///<summary>Called when a state enters</summary>
void OnStateEnter(IState state);
///<summary>Called when a state updates</summary>
void OnStateUpdate(IState state);
///<summary>Called when a state exists</summary>
void OnStateExit(IState state);
}
}

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: b3fa35b5f43db4943a1913b7079e3461
timeCreated: 1570883613
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 14914
packageName: NodeCanvas
packageVersion: 3.3.1
assetPath: Assets/ParadoxNotion/NodeCanvas/Modules/StateMachines/IStateCallbackReceiver.cs
uploadId: 704937

View File

@@ -0,0 +1,5 @@
fileFormatVersion: 2
guid: 2efa1f9c70536f14c9f2f666efa364c4
folderAsset: yes
DefaultImporter:
userData:

View File

@@ -0,0 +1,87 @@
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using UnityEngine;
namespace NodeCanvas.StateMachines
{
[Name("Action State", 100)]
[Description("Execute a number of Action Tasks OnEnter. All actions will be stoped OnExit. This state is Finished when all Actions are finished as well")]
public class ActionState : FSMState, ITaskAssignable
{
[SerializeField]
private ActionList _actionList;
[SerializeField]
private bool _repeatStateActions;
public Task task {
get { return actionList; }
set { actionList = (ActionList)value; }
}
public ActionList actionList {
get { return _actionList; }
set { _actionList = value; }
}
public bool repeatStateActions {
get { return _repeatStateActions; }
set { _repeatStateActions = value; }
}
public override void OnValidate(Graph assignedGraph) {
if ( actionList == null ) {
actionList = (ActionList)Task.Create(typeof(ActionList), assignedGraph);
actionList.executionMode = ActionList.ActionsExecutionMode.ActionsRunInParallel;
}
}
protected override void OnEnter() { OnUpdate(); }
protected override void OnUpdate() {
var actionListStatus = actionList.Execute(graphAgent, graphBlackboard);
if ( !repeatStateActions && actionListStatus != Status.Running ) {
Finish(actionListStatus);
}
}
protected override void OnExit() {
actionList.EndAction(null);
}
protected override void OnPause() {
actionList.Pause();
}
///----------------------------------------------------------------------------------------------
///---------------------------------------UNITY EDITOR-------------------------------------------
#if UNITY_EDITOR
protected override void OnNodeGUI() {
if ( repeatStateActions ) {
GUILayout.Label("<b>[REPEAT]</b>");
}
base.OnNodeGUI();
}
protected override void OnNodeInspectorGUI() {
ShowTransitionsInspector();
if ( actionList == null ) {
return;
}
EditorUtils.CoolLabel("Actions");
GUI.color = repeatStateActions ? GUI.color : new Color(1, 1, 1, 0.5f);
repeatStateActions = UnityEditor.EditorGUILayout.ToggleLeft("Repeat State Actions", repeatStateActions);
GUI.color = Color.white;
actionList.ShowListGUI();
actionList.ShowNestedActionsGUI();
}
#endif
}
}

View File

@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: ddb1611039b7dac4ab5c471307d8c524
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
AssetOrigin:
serializedVersion: 1
productId: 14914
packageName: NodeCanvas
packageVersion: 3.3.1
assetPath: Assets/ParadoxNotion/NodeCanvas/Modules/StateMachines/Nodes/ActionState.cs
uploadId: 704937

View File

@@ -0,0 +1,122 @@
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using UnityEngine;
namespace NodeCanvas.StateMachines
{
[Name("Any State")]
[Description("The transitions of this node will be constantly checked. If any becomes true, that transition will take place. This is not a state.")]
[Color("b3ff7f")]
public class AnyState : FSMNode, IUpdatable
{
[Tooltip("If enabled, a transition to an already running state will not happen.")]
public bool dontRetriggerStates = false;
public override string name { //yei for caps
get { return "FROM ANY STATE"; }
}
public override int maxInConnections { get { return 0; } }
public override int maxOutConnections { get { return -1; } }
public override bool allowAsPrime { get { return false; } }
public override void OnGraphStarted() {
for ( var i = 0; i < outConnections.Count; i++ ) {
( outConnections[i] as FSMConnection ).EnableCondition(graphAgent, graphBlackboard);
}
}
public override void OnGraphStoped() {
for ( var i = 0; i < outConnections.Count; i++ ) {
( outConnections[i] as FSMConnection ).DisableCondition();
}
}
void IUpdatable.Update() {
if ( outConnections.Count == 0 ) {
return;
}
status = Status.Running;
for ( var i = 0; i < outConnections.Count; i++ ) {
var connection = (FSMConnection)outConnections[i];
var condition = connection.condition;
if ( !connection.isActive || condition == null ) {
continue;
}
if ( dontRetriggerStates ) {
if ( FSM.currentState == (FSMState)connection.targetNode && FSM.currentState.status == Status.Running ) {
continue;
}
}
if ( condition.Check(graphAgent, graphBlackboard) ) {
FSM.EnterState((FSMState)connection.targetNode, connection.transitionCallMode);
connection.status = Status.Success; //editor vis
return;
}
connection.status = Status.Failure; //editor vis
}
}
///----------------------------------------------------------------------------------------------
///---------------------------------------UNITY EDITOR-------------------------------------------
#if UNITY_EDITOR
protected override void OnNodeGUI() {
base.OnNodeGUI();
if ( dontRetriggerStates ) {
UnityEngine.GUILayout.Label("<b>[NO RETRIGGER]</b>");
}
}
public override string GetConnectionInfo(int index) {
if ( ( outConnections[index] as FSMConnection ).condition == null ) {
return "* Never Triggered *";
}
return null;
}
protected override void OnNodeInspectorGUI() {
EditorUtils.CoolLabel("Transitions");
if ( outConnections.Count == 0 ) {
UnityEditor.EditorGUILayout.HelpBox("No Transition", UnityEditor.MessageType.None);
}
var anyNullCondition = false;
EditorUtils.ReorderableList(outConnections, (i, picked) =>
{
var connection = (FSMConnection)outConnections[i];
GUILayout.BeginHorizontal("box");
if ( connection.condition != null ) {
GUILayout.Label(connection.condition.summaryInfo, GUILayout.MinWidth(0), GUILayout.ExpandWidth(true));
} else {
GUILayout.Label("OnFinish (This is never triggered)", GUILayout.MinWidth(0), GUILayout.ExpandWidth(true));
anyNullCondition = true;
}
GUILayout.FlexibleSpace();
GUILayout.Label("► '" + connection.targetNode.name + "'");
GUILayout.EndHorizontal();
});
EditorUtils.BoldSeparator();
if ( anyNullCondition ) {
UnityEditor.EditorGUILayout.HelpBox("This is not a state and as such it never finish, thus OnFinish transitions are never called.\nPlease add a condition in all transitions of this node.", UnityEditor.MessageType.Warning);
}
dontRetriggerStates = UnityEditor.EditorGUILayout.ToggleLeft("Don't Retrigger Running States", dontRetriggerStates);
}
#endif
}
}

View File

@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 07c3b9fe69fee1641aa48869d70de213
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
AssetOrigin:
serializedVersion: 1
productId: 14914
packageName: NodeCanvas
packageVersion: 3.3.1
assetPath: Assets/ParadoxNotion/NodeCanvas/Modules/StateMachines/Nodes/AnyState.cs
uploadId: 704937

View File

@@ -0,0 +1,39 @@
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using UnityEngine;
namespace NodeCanvas.StateMachines
{
[Name("Parallel Sub FSM", -1)]
[Description("Execute a Sub FSM in parallel and for as long as this FSM is running.")]
[Category("SubGraphs")]
[Color("ff64cb")]
public class ConcurrentSubFSM : FSMNodeNested<FSM>, IUpdatable
{
[SerializeField, ExposeField, Name("Parallel FSM")]
protected BBParameter<FSM> _subFSM = null;
public override string name => base.name.ToUpper();
public override int maxInConnections => 0;
public override int maxOutConnections => 0;
public override bool allowAsPrime => false;
public override FSM subGraph { get { return _subFSM.value; } set { _subFSM.value = value; } }
public override BBParameter subGraphParameter => _subFSM;
///----------------------------------------------------------------------------------------------
public override void OnGraphStarted() {
if ( subGraph == null ) { return; }
status = Status.Running;
this.TryStartSubGraph(graphAgent, (result) => { status = result ? Status.Success : Status.Failure; });
}
void IUpdatable.Update() {
this.TryUpdateSubGraph();
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 0a8f80842481d7642b7398217e48a47e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 14914
packageName: NodeCanvas
packageVersion: 3.3.1
assetPath: Assets/ParadoxNotion/NodeCanvas/Modules/StateMachines/Nodes/ConcurrentSubFSM.cs
uploadId: 704937

View File

@@ -0,0 +1,39 @@
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using UnityEngine;
using NodeCanvas.BehaviourTrees;
namespace NodeCanvas.StateMachines
{
[Name("Parallel Sub Behaviour Tree", -1)]
[Description("Execute a Sub Behaviour Tree in parallel and for as long as this FSM is running.")]
[Category("SubGraphs")]
[Color("ff64cb")]
public class ConcurrentSubTree : FSMNodeNested<BehaviourTree>, IUpdatable
{
[SerializeField, ExposeField, Name("Parallel Tree")]
protected BBParameter<BehaviourTree> _subTree = null;
public override string name => base.name.ToUpper();
public override int maxInConnections => 0;
public override int maxOutConnections => 0;
public override bool allowAsPrime => false;
public override BehaviourTree subGraph { get { return _subTree.value; } set { _subTree.value = value; } }
public override BBParameter subGraphParameter => _subTree;
///----------------------------------------------------------------------------------------------
public override void OnGraphStarted() {
if ( subGraph == null ) { return; }
status = Status.Running;
this.TryStartSubGraph(graphAgent, (result) => { status = result ? Status.Success : Status.Failure; });
}
void IUpdatable.Update() {
this.TryUpdateSubGraph();
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: bd5d84502c0a56842a0576cfbe8ce754
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 14914
packageName: NodeCanvas
packageVersion: 3.3.1
assetPath: Assets/ParadoxNotion/NodeCanvas/Modules/StateMachines/Nodes/ConcurrentSubTree.cs
uploadId: 704937

View File

@@ -0,0 +1,21 @@
using ParadoxNotion.Design;
namespace NodeCanvas.StateMachines
{
[Description("This node has no functionality and you can use this for organization.\nIn comparison to an empty Action State, Transitions here are immediately evaluated in the same frame that this node is entered.")]
[Color("6ebbff")]
[Name("Pass", 98)]
public class EmptyState : FSMState
{
public override string name {
get { return base.name.ToUpper(); }
}
protected override void OnEnter() {
Finish();
CheckTransitions();
}
}
}

View File

@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 8abe07e17374d204abec1e16fc1f0072
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
AssetOrigin:
serializedVersion: 1
productId: 14914
packageName: NodeCanvas
packageVersion: 3.3.1
assetPath: Assets/ParadoxNotion/NodeCanvas/Modules/StateMachines/Nodes/EmptyState.cs
uploadId: 704937

View File

@@ -0,0 +1,104 @@
using NodeCanvas.BehaviourTrees;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using UnityEngine;
namespace NodeCanvas.StateMachines
{
[Name("Sub BehaviourTree")]
[Description("Execute a Behaviour Tree OnEnter. OnExit that Behavior Tree will be stoped or paused based on the relevant specified setting. You can optionaly specify a Success Event and a Failure Event which will be sent when the BT's root node status returns either of the two. If so, use alongside with a CheckEvent on a transition.")]
[DropReferenceType(typeof(BehaviourTree))]
[ParadoxNotion.Design.Icon("BT")]
public class NestedBTState : FSMStateNested<BehaviourTree>
{
public enum BTExecutionMode
{
Once,
Repeat
}
public enum BTExitMode
{
StopAndRestart,
PauseAndResume
}
[SerializeField, ExposeField, Name("Sub Tree")]
private BBParameter<BehaviourTree> _nestedBT = null;
[Tooltip("What will happen to the BT when this state exits.")]
public BTExitMode exitMode = BTExitMode.StopAndRestart;
[Tooltip("Sould the BT repeat?")]
public BTExecutionMode executionMode = BTExecutionMode.Repeat;
[DimIfDefault, Tooltip("The event to send when the BT finish in Success.")]
public string successEvent;
[DimIfDefault, Tooltip("The event to send when the BT finish in Failure.")]
public string failureEvent;
public override BehaviourTree subGraph { get { return _nestedBT.value; } set { _nestedBT.value = value; } }
public override BBParameter subGraphParameter => _nestedBT;
//
protected override void OnEnter() {
if ( subGraph == null ) {
Finish(false);
return;
}
currentInstance = (BehaviourTree)this.CheckInstance();
currentInstance.repeat = ( executionMode == BTExecutionMode.Repeat );
currentInstance.updateInterval = 0;
this.TryWriteAndBindMappedVariables();
currentInstance.StartGraph(graph.agent, graph.blackboard.parent, Graph.UpdateMode.Manual, OnFinish);
OnUpdate();
}
protected override void OnUpdate() {
currentInstance.UpdateGraph(this.graph.deltaTime);
if ( !string.IsNullOrEmpty(successEvent) && currentInstance.rootStatus == Status.Success ) {
currentInstance.Stop(true);
}
if ( !string.IsNullOrEmpty(failureEvent) && currentInstance.rootStatus == Status.Failure ) {
currentInstance.Stop(false);
}
}
void OnFinish(bool success) {
if ( this.status == Status.Running ) {
this.TryReadAndUnbindMappedVariables();
if ( !string.IsNullOrEmpty(successEvent) && success ) {
SendEvent(successEvent);
}
if ( !string.IsNullOrEmpty(failureEvent) && !success ) {
SendEvent(failureEvent);
}
Finish(success);
}
}
protected override void OnExit() {
if ( currentInstance != null ) {
if ( this.status == Status.Running ) {
this.TryReadAndUnbindMappedVariables();
}
if ( exitMode == BTExitMode.StopAndRestart ) {
currentInstance.Stop();
} else {
currentInstance.Pause();
}
}
}
}
}

View File

@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: f783a6a0ceff66b4ab995ee26ffe5948
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
AssetOrigin:
serializedVersion: 1
productId: 14914
packageName: NodeCanvas
packageVersion: 3.3.1
assetPath: Assets/ParadoxNotion/NodeCanvas/Modules/StateMachines/Nodes/NestedBTState.cs
uploadId: 704937

View File

@@ -0,0 +1,63 @@
using NodeCanvas.DialogueTrees;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using UnityEngine;
namespace NodeCanvas.StateMachines
{
[Name("Sub Dialogue")]
[Description("Execute the assigned Dialogue Tree OnEnter and stop it OnExit. Optionaly an event can be sent for whether the dialogue ended in Success or Failure. This can be controled by using the 'Finish' Dialogue Node inside the Dialogue Tree. Use a 'CheckEvent' condition to make use of those events. The 'Instigator' Actor of the Dialogue Tree will be set to this graph agent.")]
[DropReferenceType(typeof(DialogueTree))]
[ParadoxNotion.Design.Icon("Dialogue")]
public class NestedDTState : FSMStateNested<DialogueTree>
{
[SerializeField, ExposeField, Name("Sub Tree")]
private BBParameter<DialogueTree> _nestedDLG = null;
[DimIfDefault, Tooltip("The event to send when the Dialogue Tree finished in Success.")]
public string successEvent;
[DimIfDefault, Tooltip("The event to send when the Dialogue Tree finish in Failure.")]
public string failureEvent;
public override DialogueTree subGraph { get { return _nestedDLG.value; } set { _nestedDLG.value = value; } }
public override BBParameter subGraphParameter => _nestedDLG;
//
protected override void OnEnter() {
if ( subGraph == null ) {
Finish(false);
return;
}
this.TryStartSubGraph(graphAgent, OnDialogueFinished);
}
protected override void OnUpdate() {
currentInstance.UpdateGraph(this.graph.deltaTime);
}
protected override void OnExit() {
if ( currentInstance != null ) {
currentInstance.Stop();
}
}
void OnDialogueFinished(bool success) {
if ( this.status == Status.Running ) {
if ( !string.IsNullOrEmpty(successEvent) && success ) {
SendEvent(successEvent);
}
if ( !string.IsNullOrEmpty(failureEvent) && !success ) {
SendEvent(failureEvent);
}
Finish(success);
}
}
}
}

View File

@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 9b6030e6c23c1534ca4d4210998eb4d6
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
AssetOrigin:
serializedVersion: 1
productId: 14914
packageName: NodeCanvas
packageVersion: 3.3.1
assetPath: Assets/ParadoxNotion/NodeCanvas/Modules/StateMachines/Nodes/NestedDTState.cs
uploadId: 704937

View File

@@ -0,0 +1,61 @@
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using UnityEngine;
namespace NodeCanvas.StateMachines
{
[Name("Sub FSM")]
[Description("Execute a sub FSM OnEnter, and Stop that FSM OnExit. This state is Finished only when and if the sub FSM is finished as well.")]
[DropReferenceType(typeof(FSM))]
[ParadoxNotion.Design.Icon("FSM")]
public class NestedFSMState : FSMStateNested<FSM>
{
public enum FSMExitMode
{
StopAndRestart,
PauseAndResume
}
[SerializeField, ExposeField, Name("Sub FSM")]
private BBParameter<FSM> _nestedFSM = null;
[Tooltip("What will happen to the sub FSM when this state exits.")]
public FSMExitMode exitMode;
public override FSM subGraph { get { return _nestedFSM.value; } set { _nestedFSM.value = value; } }
public override BBParameter subGraphParameter => _nestedFSM;
//
protected override void OnEnter() {
if ( subGraph == null ) {
Finish(false);
return;
}
this.TryStartSubGraph(graphAgent, Finish);
OnUpdate();
}
protected override void OnUpdate() {
currentInstance.UpdateGraph(this.graph.deltaTime);
}
protected override void OnExit() {
if ( currentInstance != null ) {
if ( this.status == Status.Running ) {
this.TryReadAndUnbindMappedVariables();
}
if ( exitMode == FSMExitMode.StopAndRestart ) {
currentInstance.Stop();
} else {
currentInstance.Pause();
}
}
}
}
}

View File

@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 6ba2d9e312e9fa145aae02b450447b07
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
AssetOrigin:
serializedVersion: 1
productId: 14914
packageName: NodeCanvas
packageVersion: 3.3.1
assetPath: Assets/ParadoxNotion/NodeCanvas/Modules/StateMachines/Nodes/NestedFSMState.cs
uploadId: 704937

View File

@@ -0,0 +1,85 @@
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using UnityEngine;
namespace NodeCanvas.StateMachines
{
[Description("Execute a number of Actions when the FSM starts/enters, if Conditions are met. This is not a state.")]
[Color("ff64cb")]
[ParadoxNotion.Design.Icon("MacroIn")]
[Name("On FSM Enter")]
public class OnFSMEnter : FSMNode, IUpdatable
{
[SerializeField] private ConditionList _conditionList;
[SerializeField] private ActionList _actionList;
public override string name => base.name.ToUpper();
public override int maxInConnections => 0;
public override int maxOutConnections => 0;
public override bool allowAsPrime => false;
///----------------------------------------------------------------------------------------------
public override void OnValidate(Graph assignedGraph) {
if ( _conditionList == null ) {
_conditionList = (ConditionList)Task.Create(typeof(ConditionList), assignedGraph);
_conditionList.checkMode = ConditionList.ConditionsCheckMode.AllTrueRequired;
}
if ( _actionList == null ) {
_actionList = (ActionList)Task.Create(typeof(ActionList), assignedGraph);
_actionList.executionMode = ActionList.ActionsExecutionMode.ActionsRunInParallel;
}
}
public override void OnGraphStarted() {
_conditionList.Enable(graphAgent, graphBlackboard);
if ( _conditionList.Check(graphAgent, graphBlackboard) ) {
status = _actionList.Execute(graphAgent, graphBlackboard);
} else { status = Status.Failure; }
}
public override void OnGraphStoped() {
_conditionList.Disable();
_actionList.EndAction(null);
}
void IUpdatable.Update() {
if ( status == Status.Running ) {
status = _actionList.Execute(graphAgent, graphBlackboard);
}
}
///----------------------------------------------------------------------------------------------
///---------------------------------------UNITY EDITOR-------------------------------------------
#if UNITY_EDITOR
protected override void OnNodeGUI() {
if ( _conditionList.conditions.Count > 0 ) {
GUILayout.BeginVertical(Styles.roundedBox);
GUILayout.Label(_conditionList.summaryInfo);
GUILayout.EndVertical();
}
GUILayout.BeginVertical(Styles.roundedBox);
GUILayout.Label(_actionList.summaryInfo);
GUILayout.EndVertical();
base.OnNodeGUI();
}
protected override void OnNodeInspectorGUI() {
EditorUtils.CoolLabel("Conditions (optional)");
_conditionList.ShowListGUI();
_conditionList.ShowNestedConditionsGUI();
EditorUtils.BoldSeparator();
EditorUtils.CoolLabel("Actions");
_actionList.ShowListGUI();
_actionList.ShowNestedActionsGUI();
}
#endif
}
}

View File

@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: b14eff6fa54e645469e5f0902dc1e8f9
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
AssetOrigin:
serializedVersion: 1
productId: 14914
packageName: NodeCanvas
packageVersion: 3.3.1
assetPath: Assets/ParadoxNotion/NodeCanvas/Modules/StateMachines/Nodes/OnFSMEnter.cs
uploadId: 704937

View File

@@ -0,0 +1,80 @@
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using UnityEngine;
namespace NodeCanvas.StateMachines
{
[Description("Execute a number of Actions when the FSM stops/exits, if Conditions are met. Note that the actions will only execute for 1 frame. This is not a state.")]
[Color("ff64cb")]
[ParadoxNotion.Design.Icon("MacroOut")]
[Name("On FSM Exit")]
public class OnFSMExit : FSMNode
{
[SerializeField] private ConditionList _conditionList;
[SerializeField] private ActionList _actionList;
public override string name => base.name.ToUpper();
public override int maxInConnections => 0;
public override int maxOutConnections => 0;
public override bool allowAsPrime => false;
///----------------------------------------------------------------------------------------------
public override void OnValidate(Graph assignedGraph) {
if ( _conditionList == null ) {
_conditionList = (ConditionList)Task.Create(typeof(ConditionList), assignedGraph);
_conditionList.checkMode = ConditionList.ConditionsCheckMode.AllTrueRequired;
}
if ( _actionList == null ) {
_actionList = (ActionList)Task.Create(typeof(ActionList), assignedGraph);
_actionList.executionMode = ActionList.ActionsExecutionMode.ActionsRunInParallel;
}
}
public override void OnGraphStarted() {
_conditionList.Enable(graphAgent, graphBlackboard);
}
public override void OnGraphStoped() {
if ( _conditionList.Check(graphAgent, graphBlackboard) ) {
_actionList.Execute(graphAgent, graphBlackboard);
}
_actionList.EndAction(null);
_conditionList.Disable();
}
///----------------------------------------------------------------------------------------------
///---------------------------------------UNITY EDITOR-------------------------------------------
#if UNITY_EDITOR
protected override void OnNodeGUI() {
if ( _conditionList.conditions.Count > 0 ) {
GUILayout.BeginVertical(Styles.roundedBox);
GUILayout.Label(_conditionList.summaryInfo);
GUILayout.EndVertical();
}
GUILayout.BeginVertical(Styles.roundedBox);
GUILayout.Label(_actionList.summaryInfo);
GUILayout.EndVertical();
base.OnNodeGUI();
}
protected override void OnNodeInspectorGUI() {
EditorUtils.CoolLabel("Conditions (optional)");
_conditionList.ShowListGUI();
_conditionList.ShowNestedConditionsGUI();
EditorUtils.BoldSeparator();
EditorUtils.CoolLabel("Actions");
_actionList.ShowListGUI();
_actionList.ShowNestedActionsGUI();
}
#endif
}
}

View File

@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: b690cf7166bc1c64984e93389fb62701
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
AssetOrigin:
serializedVersion: 1
productId: 14914
packageName: NodeCanvas
packageVersion: 3.3.1
assetPath: Assets/ParadoxNotion/NodeCanvas/Modules/StateMachines/Nodes/OnFSMExit.cs
uploadId: 704937

View File

@@ -0,0 +1,85 @@
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using UnityEngine;
namespace NodeCanvas.StateMachines
{
[Description("Execute a number of Actions repeatedly and in parallel to any other FSM state while the FSM is running. Conditions are optional. This is not a state.")]
[Color("ff64cb")]
[ParadoxNotion.Design.Icon("Repeat")]
[Name("On FSM Update")]
public class OnFSMUpdate : FSMNode, IUpdatable
{
[SerializeField] private ConditionList _conditionList;
[SerializeField] private ActionList _actionList;
public override string name => base.name.ToUpper();
public override int maxInConnections => 0;
public override int maxOutConnections => 0;
public override bool allowAsPrime => false;
///----------------------------------------------------------------------------------------------
public override void OnValidate(Graph assignedGraph) {
if ( _conditionList == null ) {
_conditionList = (ConditionList)Task.Create(typeof(ConditionList), assignedGraph);
_conditionList.checkMode = ConditionList.ConditionsCheckMode.AllTrueRequired;
}
if ( _actionList == null ) {
_actionList = (ActionList)Task.Create(typeof(ActionList), assignedGraph);
_actionList.executionMode = ActionList.ActionsExecutionMode.ActionsRunInParallel;
}
}
public override void OnGraphStarted() {
_conditionList.Enable(graphAgent, graphBlackboard);
}
public override void OnGraphStoped() {
_conditionList.Disable();
_actionList.EndAction(null);
}
void IUpdatable.Update() {
if ( _conditionList.Check(graphAgent, graphBlackboard) ) {
status = _actionList.Execute(graphAgent, graphBlackboard);
} else {
_actionList.EndAction(null);
status = Status.Failure;
}
}
///----------------------------------------------------------------------------------------------
///---------------------------------------UNITY EDITOR-------------------------------------------
#if UNITY_EDITOR
protected override void OnNodeGUI() {
if ( _conditionList.conditions.Count > 0 ) {
GUILayout.BeginVertical(Styles.roundedBox);
GUILayout.Label(_conditionList.summaryInfo);
GUILayout.EndVertical();
}
GUILayout.BeginVertical(Styles.roundedBox);
GUILayout.Label(_actionList.summaryInfo);
GUILayout.EndVertical();
base.OnNodeGUI();
}
protected override void OnNodeInspectorGUI() {
EditorUtils.CoolLabel("Conditions (optional)");
_conditionList.ShowListGUI();
_conditionList.ShowNestedConditionsGUI();
EditorUtils.BoldSeparator();
EditorUtils.CoolLabel("Actions");
_actionList.ShowListGUI();
_actionList.ShowNestedActionsGUI();
}
#endif
}
}

View File

@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 0e81c1848a517df4c9327dc6d65e75ff
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
AssetOrigin:
serializedVersion: 1
productId: 14914
packageName: NodeCanvas
packageVersion: 3.3.1
assetPath: Assets/ParadoxNotion/NodeCanvas/Modules/StateMachines/Nodes/OnFSMUpdate.cs
uploadId: 704937

View File

@@ -0,0 +1,127 @@
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using UnityEngine;
namespace NodeCanvas.StateMachines
{
[Name("Action State (Super)", 99)]
[Description("The Super Action State provides finer control on when to execute actions. This state is never Finished by it's own if there is any Actions in the OnUpdate list and thus OnFinish transitions will never be called in that case. OnExit Actions are only called for 1 frame when the state exits.")]
public class SuperActionState : FSMState
{
[SerializeField]
private ActionList _onEnterList;
[SerializeField]
private ActionList _onUpdateList;
[SerializeField]
private ActionList _onExitList;
private bool enterListFinished = false;
public override void OnValidate(Graph assignedGraph) {
if ( _onEnterList == null ) {
_onEnterList = (ActionList)Task.Create(typeof(ActionList), assignedGraph);
_onEnterList.executionMode = ActionList.ActionsExecutionMode.ActionsRunInParallel;
}
if ( _onUpdateList == null ) {
_onUpdateList = (ActionList)Task.Create(typeof(ActionList), assignedGraph);
_onUpdateList.executionMode = ActionList.ActionsExecutionMode.ActionsRunInParallel;
}
if ( _onExitList == null ) {
_onExitList = (ActionList)Task.Create(typeof(ActionList), assignedGraph);
_onExitList.executionMode = ActionList.ActionsExecutionMode.ActionsRunInParallel;
}
}
protected override void OnEnter() {
enterListFinished = false;
OnUpdate();
}
protected override void OnUpdate() {
if ( !enterListFinished ) {
var enterListStatus = _onEnterList.Execute(graphAgent, graphBlackboard);
if ( enterListStatus != Status.Running ) {
enterListFinished = true;
if ( _onUpdateList.actions.Count == 0 ) {
Finish(enterListStatus);
}
}
}
_onUpdateList.Execute(graphAgent, graphBlackboard);
}
protected override void OnExit() {
_onEnterList.EndAction(null);
_onUpdateList.EndAction(null);
_onExitList.Execute(graphAgent, graphBlackboard);
_onExitList.EndAction(null);
}
protected override void OnPause() {
_onEnterList.Pause();
_onUpdateList.Pause();
}
///----------------------------------------------------------------------------------------------
///---------------------------------------UNITY EDITOR-------------------------------------------
#if UNITY_EDITOR
[SerializeField, ParadoxNotion.Serialization.FullSerializer.fsIgnoreInBuild]
private bool foldEnter;
[SerializeField, ParadoxNotion.Serialization.FullSerializer.fsIgnoreInBuild]
private bool foldUpdate;
[SerializeField, ParadoxNotion.Serialization.FullSerializer.fsIgnoreInBuild]
private bool foldExit;
protected override void OnNodeGUI() {
GUILayout.BeginVertical(Styles.roundedBox);
GUILayout.Label(string.Format("<i>{0} OnEnter Actions</i>", _onEnterList.actions.Count), Styles.leftLabel);
GUILayout.EndVertical();
GUILayout.BeginVertical(Styles.roundedBox);
GUILayout.Label(string.Format("<i>{0} OnUpdate Actions</i>", _onUpdateList.actions.Count), Styles.leftLabel);
GUILayout.EndVertical();
GUILayout.BeginVertical(Styles.roundedBox);
GUILayout.Label(string.Format("<i>{0} OnExit Actions</i>", _onExitList.actions.Count), Styles.leftLabel);
GUILayout.EndVertical();
}
protected override void OnNodeInspectorGUI() {
ShowTransitionsInspector();
if ( _onEnterList == null || _onUpdateList == null || _onExitList == null ) {
return;
}
EditorUtils.CoolLabel("Actions");
foldEnter = UnityEditor.EditorGUILayout.Foldout(foldEnter, string.Format("OnEnter Actions ({0})", _onEnterList.actions.Count));
if ( foldEnter ) {
_onEnterList.ShowListGUI();
_onEnterList.ShowNestedActionsGUI();
}
EditorUtils.Separator();
foldUpdate = UnityEditor.EditorGUILayout.Foldout(foldUpdate, string.Format("OnUpdate Actions ({0})", _onUpdateList.actions.Count));
if ( foldUpdate ) {
_onUpdateList.ShowListGUI();
_onUpdateList.ShowNestedActionsGUI();
}
EditorUtils.Separator();
foldExit = UnityEditor.EditorGUILayout.Foldout(foldExit, string.Format("OnExit Actions ({0})", _onExitList.actions.Count));
if ( foldExit ) {
_onExitList.ShowListGUI();
_onExitList.ShowNestedActionsGUI();
}
}
#endif
}
}

View File

@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: a0aefad866fbf9c45b00499f08198f4d
timeCreated: 1447866445
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 14914
packageName: NodeCanvas
packageVersion: 3.3.1
assetPath: Assets/ParadoxNotion/NodeCanvas/Modules/StateMachines/Nodes/SuperActionState.cs
uploadId: 704937