This commit is contained in:
2025-01-17 13:10:42 +01:00
commit 4536213c91
15115 changed files with 1442174 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 SubgraphUnit, 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 SubgraphUnit, 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 SubgraphUnit, 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 SubgraphUnit, 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,8 @@
fileFormatVersion: 2
guid: e7f4f3642c70f4650b86c2d023e47c3b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -0,0 +1,22 @@
using System;
using JetBrains.Annotations;
using UnityEngine;
namespace Unity.VisualScripting
{
/// <summary>
/// Check if a GameObject or StateMachine has a StateGraph
/// </summary>
[TypeIcon(typeof(StateGraph))]
[UnitCategory("Graphs/Graph Nodes")]
public sealed class HasStateGraph : HasGraph<StateGraph, StateGraphAsset, StateMachine>
{
/// <summary>
/// The type of object that handles the graph.
/// </summary>
[Serialize, Inspectable, UnitHeaderInspectable, UsedImplicitly]
public StateGraphContainerType containerType { get; set; }
protected override bool isGameObject => containerType == StateGraphContainerType.GameObject;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fbd7b60c4f4a7497584ee8214f088e6e
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,60 @@
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;
}
public override IEnumerable<object> GetAotStubs(HashSet<object> visited)
{
return LinqUtility.Concat<object>(base.GetAotStubs(visited), nest.GetAotStubs(visited));
}
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,58 @@
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;
public override IEnumerable<object> GetAotStubs(HashSet<object> visited)
{
return LinqUtility.Concat<object>(base.GetAotStubs(visited), nest.GetAotStubs(visited));
}
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,48 @@
using System.Runtime.CompilerServices;
#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
[assembly: InternalsVisibleTo("Unity.VisualScripting.State.Editor")]
[assembly: InternalsVisibleTo("Unity.VisualScripting.SettingsProvider.Editor")]
[assembly: InternalsVisibleTo("Unity.VisualScripting.Tests.Editor")]
[assembly: InternalsVisibleTo("Unity.VisualScripting.Tests")]

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,235 @@
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
#region Analytics
public override AnalyticsIdentifier GetAnalyticsIdentifier()
{
var aid = new AnalyticsIdentifier
{
Identifier = GetType().FullName,
Namespace = GetType().Namespace,
};
aid.Hashcode = aid.Identifier.GetHashCode();
return aid;
}
#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,164 @@
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);
sticky = new GraphElementCollection<StickyNote>(this);
elements.Include(states);
elements.Include(transitions);
elements.Include(groups);
elements.Include(sticky);
}
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; }
[DoNotSerialize]
public GraphElementCollection<StickyNote> sticky { get; private 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,21 @@
using UnityEngine;
namespace Unity.VisualScripting
{
[TypeIcon(typeof(StateGraph))]
[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-graphs-intro.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: f02ed452c3e0d1e448429a89e1773fd7
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]
[VisualScriptingHelpURL(typeof(StateMachine))]
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: f144232ea2c6046438f6cda6c1bad1af
type: 3
userData: ''
assetBundleName: ''
assetBundleVariant: ''
...

View File

@@ -0,0 +1,115 @@
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
#region Analytics
public override AnalyticsIdentifier GetAnalyticsIdentifier()
{
return null;
}
#endregion
}
}

Some files were not shown because too many files have changed in this diff Show More