Initial Commit

This commit is contained in:
Sebastian Cabrera 2021-08-02 05:44:37 -04:00
parent 53eb92e9af
commit 270ab7d11f
15341 changed files with 700234 additions and 0 deletions

View file

@ -0,0 +1,43 @@
namespace Unity.VisualScripting
{
/// <summary>
/// A special state that can trigger transitions to other states,
/// no matter which state is currently active. This state cannot receive
/// transitions.
/// </summary>
public sealed class AnyState : State
{
[DoNotSerialize]
public override bool canBeDestination => false;
public AnyState() : base()
{
isStart = true;
}
public override void OnExit(Flow flow, StateExitReason reason)
{
// Don't exit this state from branching.
if (reason == StateExitReason.Branch)
{
return;
}
base.OnExit(flow, reason);
}
public override void OnBranchTo(Flow flow, IState destination)
{
// Before entering the destination destination state,
// exit all other connected states.
foreach (var outgoingTransition in outgoingTransitionsNoAlloc)
{
if (outgoingTransition.destination != destination)
{
outgoingTransition.destination.OnExit(flow, StateExitReason.AnyBranch);
}
}
}
}
}

View file

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

View file

@ -0,0 +1,94 @@
using System.ComponentModel;
using UnityEngine;
namespace Unity.VisualScripting
{
[SerializationVersion("A")]
[TypeIcon(typeof(FlowGraph))]
[DisplayName("Script State")]
public sealed class FlowState : NesterState<FlowGraph, ScriptGraphAsset>, IGraphEventListener
{
public FlowState() { }
public FlowState(ScriptGraphAsset macro) : base(macro) { }
#region Lifecycle
protected override void OnEnterImplementation(Flow flow)
{
if (flow.stack.TryEnterParentElement(this))
{
nest.graph.StartListening(flow.stack);
flow.stack.TriggerEventHandler(hook => hook == StateEventHooks.OnEnterState, new EmptyEventArgs(), parent => parent is SuperUnit, false);
flow.stack.ExitParentElement();
}
}
protected override void OnExitImplementation(Flow flow)
{
if (flow.stack.TryEnterParentElement(this))
{
flow.stack.TriggerEventHandler(hook => hook == StateEventHooks.OnExitState, new EmptyEventArgs(), parent => parent is SuperUnit, false);
nest.graph.StopListening(flow.stack);
flow.stack.ExitParentElement();
}
}
public void StartListening(GraphStack stack)
{
if (stack.TryEnterParentElement(this))
{
nest.graph.StartListening(stack);
stack.ExitParentElement();
}
}
public void StopListening(GraphStack stack)
{
if (stack.TryEnterParentElement(this))
{
nest.graph.StopListening(stack);
stack.ExitParentElement();
}
}
public bool IsListening(GraphPointer pointer)
{
return pointer.GetElementData<Data>(this).isActive;
}
#endregion
#region Factory
public override FlowGraph DefaultGraph()
{
return GraphWithEnterUpdateExit();
}
public static FlowState WithEnterUpdateExit()
{
var flowState = new FlowState();
flowState.nest.source = GraphSource.Embed;
flowState.nest.embed = GraphWithEnterUpdateExit();
return flowState;
}
public static FlowGraph GraphWithEnterUpdateExit()
{
return new FlowGraph
{
units =
{
new OnEnterState { position = new Vector2(-205, -215) },
new Update { position = new Vector2(-161, -38) },
new OnExitState { position = new Vector2(-205, 145) }
}
};
}
#endregion
}
}

View file

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

View file

@ -0,0 +1,95 @@
using System;
using UnityEngine;
namespace Unity.VisualScripting
{
[SerializationVersion("A")]
public sealed class FlowStateTransition : NesterStateTransition<FlowGraph, ScriptGraphAsset>, IGraphEventListener
{
public FlowStateTransition() : base() { }
public FlowStateTransition(IState source, IState destination) : base(source, destination)
{
if (!source.canBeSource)
{
throw new InvalidOperationException("Source state cannot emit transitions.");
}
if (!destination.canBeDestination)
{
throw new InvalidOperationException("Destination state cannot receive transitions.");
}
}
public static FlowStateTransition WithDefaultTrigger(IState source, IState destination)
{
var flowStateTransition = new FlowStateTransition(source, destination);
flowStateTransition.nest.source = GraphSource.Embed;
flowStateTransition.nest.embed = GraphWithDefaultTrigger();
return flowStateTransition;
}
public static FlowGraph GraphWithDefaultTrigger()
{
return new FlowGraph()
{
units =
{
new TriggerStateTransition() { position = new Vector2(100, -50) }
}
};
}
#region Lifecycle
public override void OnEnter(Flow flow)
{
if (flow.stack.TryEnterParentElement(this))
{
flow.stack.TriggerEventHandler(hook => hook == StateEventHooks.OnEnterState, new EmptyEventArgs(), parent => parent is SuperUnit, false);
flow.stack.ExitParentElement();
}
}
public override void OnExit(Flow flow)
{
if (flow.stack.TryEnterParentElement(this))
{
flow.stack.TriggerEventHandler(hook => hook == StateEventHooks.OnExitState, new EmptyEventArgs(), parent => parent is SuperUnit, false);
nest.graph.StopListening(flow.stack);
flow.stack.ExitParentElement();
}
}
public void StartListening(GraphStack stack)
{
if (stack.TryEnterParentElement(this))
{
nest.graph.StartListening(stack);
stack.ExitParentElement();
}
}
public void StopListening(GraphStack stack)
{
if (stack.TryEnterParentElement(this))
{
nest.graph.StopListening(stack);
stack.ExitParentElement();
}
}
public bool IsListening(GraphPointer pointer)
{
return pointer.GetElementData<State.Data>(source).isActive;
}
#endregion
public override FlowGraph DefaultGraph()
{
return GraphWithDefaultTrigger();
}
}
}

View file

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

View file

@ -0,0 +1,4 @@
namespace Unity.VisualScripting
{
public interface INesterState : IState, IGraphNesterElement { }
}

View file

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

View file

@ -0,0 +1,4 @@
namespace Unity.VisualScripting
{
public interface INesterStateTransition : IStateTransition, IGraphNesterElement { }
}

View file

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

View file

@ -0,0 +1,36 @@
using System.Collections.Generic;
using UnityEngine;
namespace Unity.VisualScripting
{
public interface IState : IGraphElementWithDebugData, IGraphElementWithData
{
new StateGraph graph { get; }
bool isStart { get; set; }
bool canBeSource { get; }
bool canBeDestination { get; }
void OnBranchTo(Flow flow, IState destination);
IEnumerable<IStateTransition> outgoingTransitions { get; }
IEnumerable<IStateTransition> incomingTransitions { get; }
IEnumerable<IStateTransition> transitions { get; }
void OnEnter(Flow flow, StateEnterReason reason);
void OnExit(Flow flow, StateExitReason reason);
#region Widget
Vector2 position { get; set; }
float width { get; set; }
#endregion
}
}

View file

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

View file

@ -0,0 +1,9 @@
namespace Unity.VisualScripting
{
public interface IStateDebugData : IGraphElementDebugData
{
int lastEnterFrame { get; }
float lastExitTime { get; }
}
}

View file

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

View file

@ -0,0 +1,11 @@
namespace Unity.VisualScripting
{
public interface IStateTransition : IGraphElementWithDebugData, IConnection<IState, IState>
{
void Branch(Flow flow);
void OnEnter(Flow flow);
void OnExit(Flow flow);
}
}

View file

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

View file

@ -0,0 +1,9 @@
namespace Unity.VisualScripting
{
public interface IStateTransitionDebugData : IGraphElementDebugData
{
int lastBranchFrame { get; }
float lastBranchTime { get; }
}
}

View file

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

View file

@ -0,0 +1,58 @@
using System.Collections.Generic;
using UnityObject = UnityEngine.Object;
namespace Unity.VisualScripting
{
public abstract class NesterState<TGraph, TMacro> : State, INesterState
where TGraph : class, IGraph, new()
where TMacro : Macro<TGraph>
{
protected NesterState()
{
nest.nester = this;
}
protected NesterState(TMacro macro)
{
nest.nester = this;
nest.macro = macro;
nest.source = GraphSource.Macro;
}
[Serialize]
public GraphNest<TGraph, TMacro> nest { get; private set; } = new GraphNest<TGraph, TMacro>();
[DoNotSerialize]
IGraphNest IGraphNester.nest => nest;
[DoNotSerialize]
IGraph IGraphParent.childGraph => nest.graph;
[DoNotSerialize]
bool IGraphParent.isSerializationRoot => nest.source == GraphSource.Macro;
[DoNotSerialize]
UnityObject IGraphParent.serializedObject => nest.macro;
[DoNotSerialize]
public override IEnumerable<ISerializationDependency> deserializationDependencies => nest.deserializationDependencies;
protected void CopyFrom(NesterState<TGraph, TMacro> source)
{
base.CopyFrom(source);
nest = source.nest;
}
[DoNotSerialize]
public override IEnumerable<object> aotStubs => LinqUtility.Concat<object>(base.aotStubs, nest.aotStubs);
public abstract TGraph DefaultGraph();
IGraph IGraphParent.DefaultGraph() => DefaultGraph();
void IGraphNester.InstantiateNest() => InstantiateNest();
void IGraphNester.UninstantiateNest() => UninstantiateNest();
}
}

View file

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

View file

@ -0,0 +1,56 @@
using System.Collections.Generic;
using UnityObject = UnityEngine.Object;
namespace Unity.VisualScripting
{
public abstract class NesterStateTransition<TGraph, TMacro> : StateTransition, INesterStateTransition
where TGraph : class, IGraph, new()
where TMacro : Macro<TGraph>
{
protected NesterStateTransition()
{
nest.nester = this;
}
protected NesterStateTransition(IState source, IState destination) : base(source, destination)
{
nest.nester = this;
}
[Serialize]
public GraphNest<TGraph, TMacro> nest { get; private set; } = new GraphNest<TGraph, TMacro>();
[DoNotSerialize]
IGraphNest IGraphNester.nest => nest;
[DoNotSerialize]
IGraph IGraphParent.childGraph => nest.graph;
[DoNotSerialize]
bool IGraphParent.isSerializationRoot => nest.source == GraphSource.Macro;
[DoNotSerialize]
UnityObject IGraphParent.serializedObject => nest.macro;
[DoNotSerialize]
public override IEnumerable<ISerializationDependency> deserializationDependencies => nest.deserializationDependencies;
[DoNotSerialize]
public override IEnumerable<object> aotStubs => LinqUtility.Concat<object>(base.aotStubs, nest.aotStubs);
protected void CopyFrom(NesterStateTransition<TGraph, TMacro> source)
{
base.CopyFrom(source);
nest = source.nest;
}
public abstract TGraph DefaultGraph();
IGraph IGraphParent.DefaultGraph() => DefaultGraph();
void IGraphNester.InstantiateNest() => InstantiateNest();
void IGraphNester.UninstantiateNest() => UninstantiateNest();
}
}

View file

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

View file

@ -0,0 +1,11 @@
namespace Unity.VisualScripting
{
/// <summary>
/// Called in flow graphs nested in state graphs when the parent state node is entered.
/// </summary>
[UnitCategory("Events/State")]
public class OnEnterState : ManualEventUnit<EmptyEventArgs>
{
protected override string hookName => StateEventHooks.OnEnterState;
}
}

View file

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

View file

@ -0,0 +1,11 @@
namespace Unity.VisualScripting
{
/// <summary>
/// Called in flow graphs nested in state graphs before the parent state node is exited.
/// </summary>
[UnitCategory("Events/State")]
public class OnExitState : ManualEventUnit<EmptyEventArgs>
{
protected override string hookName => StateEventHooks.OnExitState;
}
}

View file

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

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a8356fd10cc2a4a52bb860a13dd47a52
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,41 @@
#if DISTRIBUTE_ASSEMBLIES
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Visual Scripting State (Runtime)")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Unity")]
[assembly: AssemblyProduct("Visual Scripting")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("3a1cfa72-fea6-4963-83a5-bcf1deee18c5")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
#endif

View file

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

View file

@ -0,0 +1,220 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Unity.VisualScripting
{
public abstract class State : GraphElement<StateGraph>, IState
{
public class Data : IGraphElementData
{
public bool isActive;
public bool hasEntered;
}
public class DebugData : IStateDebugData
{
public int lastEnterFrame { get; set; }
public float lastExitTime { get; set; }
public Exception runtimeException { get; set; }
}
public IGraphElementData CreateData()
{
return new Data();
}
public IGraphElementDebugData CreateDebugData()
{
return new DebugData();
}
[Serialize]
public bool isStart { get; set; }
[DoNotSerialize]
public virtual bool canBeSource => true;
[DoNotSerialize]
public virtual bool canBeDestination => true;
public override void BeforeRemove()
{
base.BeforeRemove();
Disconnect();
}
public override void Instantiate(GraphReference instance)
{
base.Instantiate(instance);
var data = instance.GetElementData<Data>(this);
if (this is IGraphEventListener listener && data.isActive)
{
listener.StartListening(instance);
}
else if (isStart && !data.hasEntered && graph.IsListening(instance))
{
using (var flow = Flow.New(instance))
{
OnEnter(flow, StateEnterReason.Start);
}
}
}
public override void Uninstantiate(GraphReference instance)
{
if (this is IGraphEventListener listener)
{
listener.StopListening(instance);
}
base.Uninstantiate(instance);
}
#region Poutine
protected void CopyFrom(State source)
{
base.CopyFrom(source);
isStart = source.isStart;
width = source.width;
}
#endregion
#region Transitions
public IEnumerable<IStateTransition> outgoingTransitions => graph?.transitions.WithSource(this) ?? Enumerable.Empty<IStateTransition>();
public IEnumerable<IStateTransition> incomingTransitions => graph?.transitions.WithDestination(this) ?? Enumerable.Empty<IStateTransition>();
protected List<IStateTransition> outgoingTransitionsNoAlloc => graph?.transitions.WithSourceNoAlloc(this) ?? Empty<IStateTransition>.list;
public IEnumerable<IStateTransition> transitions => LinqUtility.Concat<IStateTransition>(outgoingTransitions, incomingTransitions);
public void Disconnect()
{
foreach (var transition in transitions.ToArray())
{
graph.transitions.Remove(transition);
}
}
#endregion
#region Lifecycle
public virtual void OnEnter(Flow flow, StateEnterReason reason)
{
var data = flow.stack.GetElementData<Data>(this);
if (data.isActive) // Prevent re-entry from Any State
{
return;
}
data.isActive = true;
data.hasEntered = true;
foreach (var transition in outgoingTransitionsNoAlloc)
{
// Start listening for the transition's events
// before entering the state in case OnEnterState
// actually instantly triggers a transition via event
// http://support.ludiq.io/topics/261-event-timing-issue/
(transition as IGraphEventListener)?.StartListening(flow.stack);
}
if (flow.enableDebug)
{
var editorData = flow.stack.GetElementDebugData<DebugData>(this);
editorData.lastEnterFrame = EditorTimeBinding.frame;
}
OnEnterImplementation(flow);
foreach (var transition in outgoingTransitionsNoAlloc)
{
try
{
transition.OnEnter(flow);
}
catch (Exception ex)
{
transition.HandleException(flow.stack, ex);
throw;
}
}
}
public virtual void OnExit(Flow flow, StateExitReason reason)
{
var data = flow.stack.GetElementData<Data>(this);
if (!data.isActive)
{
return;
}
OnExitImplementation(flow);
data.isActive = false;
if (flow.enableDebug)
{
var editorData = flow.stack.GetElementDebugData<DebugData>(this);
editorData.lastExitTime = EditorTimeBinding.time;
}
foreach (var transition in outgoingTransitionsNoAlloc)
{
try
{
transition.OnExit(flow);
}
catch (Exception ex)
{
transition.HandleException(flow.stack, ex);
throw;
}
}
}
protected virtual void OnEnterImplementation(Flow flow) { }
protected virtual void UpdateImplementation(Flow flow) { }
protected virtual void FixedUpdateImplementation(Flow flow) { }
protected virtual void LateUpdateImplementation(Flow flow) { }
protected virtual void OnExitImplementation(Flow flow) { }
public virtual void OnBranchTo(Flow flow, IState destination) { }
#endregion
#region Widget
public const float DefaultWidth = 170;
[Serialize]
public Vector2 position { get; set; }
[Serialize]
public float width { get; set; } = DefaultWidth;
#endregion
}
}

View file

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

View file

@ -0,0 +1,9 @@
namespace Unity.VisualScripting
{
public enum StateEnterReason
{
Start,
Branch,
Forced
}
}

View file

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

View file

@ -0,0 +1,8 @@
namespace Unity.VisualScripting
{
public static class StateEventHooks
{
public const string OnEnterState = nameof(OnEnterState);
public const string OnExitState = nameof(OnExitState);
}
}

View file

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

View file

@ -0,0 +1,10 @@
namespace Unity.VisualScripting
{
public enum StateExitReason
{
Stop,
Branch,
AnyBranch,
Forced
}
}

View file

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

View file

@ -0,0 +1,160 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Unity.VisualScripting
{
[SerializationVersion("A")]
public sealed class StateGraph : Graph, IGraphEventListener
{
public StateGraph()
{
states = new GraphElementCollection<IState>(this);
transitions = new GraphConnectionCollection<IStateTransition, IState, IState>(this);
groups = new GraphElementCollection<GraphGroup>(this);
elements.Include(states);
elements.Include(transitions);
elements.Include(groups);
}
public override IGraphData CreateData()
{
return new StateGraphData(this);
}
public void StartListening(GraphStack stack)
{
stack.GetGraphData<StateGraphData>().isListening = true;
var activeStates = GetActiveStatesNoAlloc(stack);
foreach (var state in activeStates)
{
(state as IGraphEventListener)?.StartListening(stack);
}
activeStates.Free();
}
public void StopListening(GraphStack stack)
{
var activeStates = GetActiveStatesNoAlloc(stack);
foreach (var state in activeStates)
{
(state as IGraphEventListener)?.StopListening(stack);
}
activeStates.Free();
stack.GetGraphData<StateGraphData>().isListening = false;
}
public bool IsListening(GraphPointer pointer)
{
return pointer.GetGraphData<StateGraphData>().isListening;
}
#region Elements
[DoNotSerialize]
public GraphElementCollection<IState> states { get; internal set; }
[DoNotSerialize]
public GraphConnectionCollection<IStateTransition, IState, IState> transitions { get; internal set; }
[DoNotSerialize]
public GraphElementCollection<GraphGroup> groups { get; internal set; }
#endregion
#region Lifecycle
// Active state detection happens twice:
//
// 1. Before the enumeration, because any state
// that becomes active during an update shouldn't
// be updated until the next update
//
// 2. Inside the update method, because a state
// that was active during enumeration and no longer
// is shouldn't be updated.
private HashSet<IState> GetActiveStatesNoAlloc(GraphPointer pointer)
{
var activeStates = HashSetPool<IState>.New();
foreach (var state in states)
{
var stateData = pointer.GetElementData<State.Data>(state);
if (stateData.isActive)
{
activeStates.Add(state);
}
}
return activeStates;
}
public void Start(Flow flow)
{
flow.stack.GetGraphData<StateGraphData>().isListening = true;
foreach (var state in states.Where(s => s.isStart))
{
try
{
state.OnEnter(flow, StateEnterReason.Start);
}
catch (Exception ex)
{
state.HandleException(flow.stack, ex);
throw;
}
}
}
public void Stop(Flow flow)
{
var activeStates = GetActiveStatesNoAlloc(flow.stack);
foreach (var state in activeStates)
{
try
{
state.OnExit(flow, StateExitReason.Stop);
}
catch (Exception ex)
{
state.HandleException(flow.stack, ex);
throw;
}
}
activeStates.Free();
flow.stack.GetGraphData<StateGraphData>().isListening = false;
}
#endregion
public static StateGraph WithStart()
{
var stateGraph = new StateGraph();
var startState = FlowState.WithEnterUpdateExit();
startState.isStart = true;
startState.nest.embed.title = "Start";
startState.position = new Vector2(-86, -15);
stateGraph.states.Add(startState);
return stateGraph;
}
}
}

View file

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

View file

@ -0,0 +1,20 @@
using UnityEngine;
namespace Unity.VisualScripting
{
[CreateAssetMenu(menuName = "Visual Scripting/State Graph", fileName = "New State Graph", order = 81)]
[HelpURL("https://docs.unity3d.com/Packages/com.unity.visualscripting@latest/index.html?subfolder=/manual/vs-state.html")]
public sealed class StateGraphAsset : Macro<StateGraph>
{
[ContextMenu("Show Data...")]
protected override void ShowData()
{
base.ShowData();
}
public override StateGraph DefaultGraph()
{
return StateGraph.WithStart();
}
}
}

View file

@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 530e556d4407947dcae7102ff497587d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon:
fileID: 2800000
guid: b28af3520c9f270429f383116bd0d368
type: 3
userData: ''
assetBundleName: ''
assetBundleVariant: ''
...

View file

@ -0,0 +1,11 @@
namespace Unity.VisualScripting
{
public sealed class StateGraphData : GraphData<StateGraph>, IGraphEventListenerData
{
public bool isListening { get; set; }
public StateGraphData(StateGraph definition) : base(definition)
{
}
}
}

View file

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

View file

@ -0,0 +1,74 @@
using UnityEngine;
namespace Unity.VisualScripting
{
[AddComponentMenu("Visual Scripting/State Machine")]
[RequireComponent(typeof(Variables))]
[DisableAnnotation]
[HelpURL("https://docs.unity3d.com/Packages/com.unity.visualscripting@latest/index.html?subfolder=/manual/vs-graphs-machines-macros.html")]
public sealed class StateMachine : EventMachine<StateGraph, StateGraphAsset>
{
protected override void OnEnable()
{
if (hasGraph)
{
using (var flow = Flow.New(reference))
{
graph.Start(flow);
}
}
base.OnEnable();
}
protected override void OnInstantiateWhileEnabled()
{
if (hasGraph)
{
using (var flow = Flow.New(reference))
{
graph.Start(flow);
}
}
base.OnInstantiateWhileEnabled();
}
protected override void OnUninstantiateWhileEnabled()
{
base.OnUninstantiateWhileEnabled();
if (hasGraph)
{
using (var flow = Flow.New(reference))
{
graph.Stop(flow);
}
}
}
protected override void OnDisable()
{
base.OnDisable();
if (hasGraph)
{
using (var flow = Flow.New(reference))
{
graph.Stop(flow);
}
}
}
[ContextMenu("Show Data...")]
protected override void ShowData()
{
base.ShowData();
}
public override StateGraph DefaultGraph()
{
return StateGraph.WithStart();
}
}
}

View file

@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: efe58df89b5b74a97a123b5ed2b70422
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon:
fileID: 2800000
guid: 78beaf4c6e26bf745b5bc7dfe5d007be
type: 3
userData: ''
assetBundleName: ''
assetBundleVariant: ''
...

View file

@ -0,0 +1,106 @@
using System;
namespace Unity.VisualScripting
{
public abstract class StateTransition : GraphElement<StateGraph>, IStateTransition
{
public class DebugData : IStateTransitionDebugData
{
public Exception runtimeException { get; set; }
public int lastBranchFrame { get; set; }
public float lastBranchTime { get; set; }
}
protected StateTransition() { }
protected StateTransition(IState source, IState destination)
{
Ensure.That(nameof(source)).IsNotNull(source);
Ensure.That(nameof(destination)).IsNotNull(destination);
if (source.graph != destination.graph)
{
throw new NotSupportedException("Cannot create transitions across state graphs.");
}
this.source = source;
this.destination = destination;
}
public IGraphElementDebugData CreateDebugData()
{
return new DebugData();
}
public override int dependencyOrder => 1;
[Serialize]
public IState source { get; internal set; }
[Serialize]
public IState destination { get; internal set; }
public override void Instantiate(GraphReference instance)
{
base.Instantiate(instance);
if (this is IGraphEventListener listener && instance.GetElementData<State.Data>(source).isActive)
{
listener.StartListening(instance);
}
}
public override void Uninstantiate(GraphReference instance)
{
if (this is IGraphEventListener listener)
{
listener.StopListening(instance);
}
base.Uninstantiate(instance);
}
#region Lifecycle
public void Branch(Flow flow)
{
if (flow.enableDebug)
{
var editorData = flow.stack.GetElementDebugData<DebugData>(this);
editorData.lastBranchFrame = EditorTimeBinding.frame;
editorData.lastBranchTime = EditorTimeBinding.time;
}
try
{
source.OnExit(flow, StateExitReason.Branch);
}
catch (Exception ex)
{
source.HandleException(flow.stack, ex);
throw;
}
source.OnBranchTo(flow, destination);
try
{
destination.OnEnter(flow, StateEnterReason.Branch);
}
catch (Exception ex)
{
destination.HandleException(flow.stack, ex);
throw;
}
}
public abstract void OnEnter(Flow flow);
public abstract void OnExit(Flow flow);
#endregion
}
}

View file

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

View file

@ -0,0 +1,76 @@
namespace Unity.VisualScripting
{
[TypeIcon(typeof(StateGraph))]
[UnitCategory("Nesting")]
public sealed class StateUnit : NesterUnit<StateGraph, StateGraphAsset>
{
public StateUnit() : base() { }
public StateUnit(StateGraphAsset macro) : base(macro) { }
/// <summary>
/// The entry point to start the state graph.
/// </summary>
[DoNotSerialize]
public ControlInput start { get; private set; }
/// <summary>
/// The entry point to stop the state graph.
/// </summary>
[DoNotSerialize]
public ControlInput stop { get; private set; }
/// <summary>
/// The action to execute after the state graph has been started.
/// </summary>
[DoNotSerialize]
public ControlOutput started { get; private set; }
/// <summary>
/// The action to execute after the state graph has been stopped.
/// </summary>
[DoNotSerialize]
public ControlOutput stopped { get; private set; }
public static StateUnit WithStart()
{
var stateUnit = new StateUnit();
stateUnit.nest.source = GraphSource.Embed;
stateUnit.nest.embed = StateGraph.WithStart();
return stateUnit;
}
protected override void Definition()
{
start = ControlInput(nameof(start), Start);
stop = ControlInput(nameof(stop), Stop);
started = ControlOutput(nameof(started));
stopped = ControlOutput(nameof(stopped));
Succession(start, started);
Succession(stop, stopped);
}
private ControlOutput Start(Flow flow)
{
flow.stack.EnterParentElement(this);
nest.graph.Start(flow);
flow.stack.ExitParentElement();
return started;
}
private ControlOutput Stop(Flow flow)
{
flow.stack.EnterParentElement(this);
nest.graph.Stop(flow);
flow.stack.ExitParentElement();
return stopped;
}
public override StateGraph DefaultGraph()
{
return StateGraph.WithStart();
}
}
}

View file

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

View file

@ -0,0 +1,69 @@
namespace Unity.VisualScripting
{
[TypeIcon(typeof(StateGraph))]
public sealed class SuperState : NesterState<StateGraph, StateGraphAsset>, IGraphEventListener
{
public SuperState() : base() { }
public SuperState(StateGraphAsset macro) : base(macro) { }
public static SuperState WithStart()
{
var superState = new SuperState();
superState.nest.source = GraphSource.Embed;
superState.nest.embed = StateGraph.WithStart();
return superState;
}
#region Lifecycle
protected override void OnEnterImplementation(Flow flow)
{
if (flow.stack.TryEnterParentElement(this))
{
nest.graph.Start(flow);
flow.stack.ExitParentElement();
}
}
protected override void OnExitImplementation(Flow flow)
{
if (flow.stack.TryEnterParentElement(this))
{
nest.graph.Stop(flow);
flow.stack.ExitParentElement();
}
}
public void StartListening(GraphStack stack)
{
if (stack.TryEnterParentElement(this))
{
nest.graph.StartListening(stack);
stack.ExitParentElement();
}
}
public void StopListening(GraphStack stack)
{
if (stack.TryEnterParentElement(this))
{
nest.graph.StopListening(stack);
stack.ExitParentElement();
}
}
public bool IsListening(GraphPointer pointer)
{
return pointer.GetElementData<Data>(this).isActive;
}
#endregion
public override StateGraph DefaultGraph()
{
return StateGraph.WithStart();
}
}
}

View file

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

View file

@ -0,0 +1,35 @@
namespace Unity.VisualScripting
{
/// <summary>
/// Triggers the transition in the parent state graph.
/// </summary>
[UnitSurtitle("State")]
[UnitCategory("Nesting")]
[UnitShortTitle("Trigger Transition")]
[TypeIcon(typeof(IStateTransition))]
public sealed class TriggerStateTransition : Unit
{
/// <summary>
/// The moment at which the parent state transition should be triggered.
/// </summary>
[DoNotSerialize]
[PortLabelHidden]
public ControlInput trigger { get; private set; }
protected override void Definition()
{
trigger = ControlInput(nameof(trigger), Trigger);
}
private ControlOutput Trigger(Flow flow)
{
var stateTransition = flow.stack.GetParent<INesterStateTransition>();
flow.stack.ExitParentElement();
stateTransition.Branch(flow);
return null;
}
}
}

View file

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

View file

@ -0,0 +1,11 @@
{
"name": "Unity.VisualScripting.State",
"references": [
"Unity.VisualScripting.Core",
"Unity.VisualScripting.Flow"
],
"optionalUnityReferences": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 315d634a9ac6d460a9515f5a56be6311
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: