Initial Commit
This commit is contained in:
parent
53eb92e9af
commit
270ab7d11f
15341 changed files with 700234 additions and 0 deletions
|
@ -0,0 +1,65 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
[Analyser(typeof(FlowStateTransition))]
|
||||
public class FlowStateTransitionAnalyser : NesterStateTransitionAnalyser<FlowStateTransition>
|
||||
{
|
||||
public FlowStateTransitionAnalyser(GraphReference reference, FlowStateTransition transition) : base(reference, transition) { }
|
||||
|
||||
protected override bool IsTraversed()
|
||||
{
|
||||
var graph = transition.nest.graph;
|
||||
|
||||
if (graph == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
using (var recursion = Recursion.New(1))
|
||||
{
|
||||
foreach (var trigger in graph.GetUnitsRecursive(recursion).OfType<TriggerStateTransition>())
|
||||
{
|
||||
if (trigger.Analysis<UnitAnalysis>(context).isEntered)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override IEnumerable<Warning> Warnings()
|
||||
{
|
||||
foreach (var baseWarning in base.Warnings())
|
||||
{
|
||||
yield return baseWarning;
|
||||
}
|
||||
|
||||
var graph = transition.nest.graph;
|
||||
|
||||
if (graph == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
using (var recursion = Recursion.New(1))
|
||||
{
|
||||
if (!graph.GetUnitsRecursive(recursion).OfType<IEventUnit>().Any())
|
||||
{
|
||||
yield return Warning.Caution("Transition graph is missing an event.");
|
||||
}
|
||||
}
|
||||
|
||||
using (var recursion = Recursion.New(1))
|
||||
{
|
||||
if (!graph.GetUnitsRecursive(recursion).OfType<TriggerStateTransition>().Any())
|
||||
{
|
||||
yield return Warning.Caution("Transition graph is missing a trigger unit.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1275e89e45fac4c26b82fda27d2d9647
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,106 @@
|
|||
using System.Linq;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
[Descriptor(typeof(FlowStateTransition))]
|
||||
public class FlowStateTransitionDescriptor : NesterStateTransitionDescriptor<FlowStateTransition>
|
||||
{
|
||||
public FlowStateTransitionDescriptor(FlowStateTransition transition) : base(transition) { }
|
||||
|
||||
public override string Title()
|
||||
{
|
||||
var graph = transition.nest.graph;
|
||||
|
||||
if (graph != null)
|
||||
{
|
||||
if (!StringUtility.IsNullOrWhiteSpace(graph.title))
|
||||
{
|
||||
return graph.title;
|
||||
}
|
||||
|
||||
using (var recursion = Recursion.New(1))
|
||||
{
|
||||
var events = graph.GetUnitsRecursive(recursion).OfType<IEventUnit>().ToList();
|
||||
|
||||
if (events.Count == 0)
|
||||
{
|
||||
return "(No Event)";
|
||||
}
|
||||
else if (events.Count == 1)
|
||||
{
|
||||
return events[0].Description().title;
|
||||
}
|
||||
else // if (events.Count > 1)
|
||||
{
|
||||
return "(Multiple Events)";
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return "(No Transition)";
|
||||
}
|
||||
}
|
||||
|
||||
public override string Summary()
|
||||
{
|
||||
var graph = transition.nest.graph;
|
||||
|
||||
if (graph != null)
|
||||
{
|
||||
if (!StringUtility.IsNullOrWhiteSpace(graph.summary))
|
||||
{
|
||||
return graph.summary;
|
||||
}
|
||||
|
||||
using (var recursion = Recursion.New(1))
|
||||
{
|
||||
var events = graph.GetUnitsRecursive(recursion).OfType<IEventUnit>().ToList();
|
||||
|
||||
if (events.Count == 0)
|
||||
{
|
||||
return "Open the transition graph to add an event.";
|
||||
}
|
||||
else if (events.Count == 1)
|
||||
{
|
||||
return events[0].Description().summary;
|
||||
}
|
||||
else // if (events.Count > 1)
|
||||
{
|
||||
return "Open the transition graph to see the full transition.";
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Choose a source in the graph inspector.";
|
||||
}
|
||||
}
|
||||
|
||||
public override EditorTexture Icon()
|
||||
{
|
||||
var graph = transition.nest.graph;
|
||||
|
||||
using (var recursion = Recursion.New(1))
|
||||
{
|
||||
if (graph != null)
|
||||
{
|
||||
var events = graph.GetUnitsRecursive(recursion).OfType<IEventUnit>().ToList();
|
||||
|
||||
if (events.Count == 1)
|
||||
{
|
||||
return events[0].Description().icon;
|
||||
}
|
||||
else
|
||||
{
|
||||
return typeof(IStateTransition).Icon();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return typeof(IStateTransition).Icon();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 355c8a593616d459494378fc4c6f39a2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
namespace Unity.VisualScripting
|
||||
{
|
||||
[Editor(typeof(FlowStateTransition))]
|
||||
public sealed class FlowStateTransitionEditor : NesterStateTransitionEditor
|
||||
{
|
||||
public FlowStateTransitionEditor(Metadata metadata) : base(metadata) { }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6a1240097a06b411090f9cb6c29cf64a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,44 @@
|
|||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
[Widget(typeof(FlowStateTransition))]
|
||||
public sealed class FlowStateTransitionWidget : NesterStateTransitionWidget<FlowStateTransition>, IDragAndDropHandler
|
||||
{
|
||||
public FlowStateTransitionWidget(StateCanvas canvas, FlowStateTransition transition) : base(canvas, transition) { }
|
||||
|
||||
#region Drag & Drop
|
||||
|
||||
public DragAndDropVisualMode dragAndDropVisualMode => DragAndDropVisualMode.Generic;
|
||||
|
||||
public bool AcceptsDragAndDrop()
|
||||
{
|
||||
return DragAndDropUtility.Is<ScriptGraphAsset>();
|
||||
}
|
||||
|
||||
public void PerformDragAndDrop()
|
||||
{
|
||||
UndoUtility.RecordEditedObject("Drag & Drop Macro");
|
||||
transition.nest.source = GraphSource.Macro;
|
||||
transition.nest.macro = DragAndDropUtility.Get<ScriptGraphAsset>();
|
||||
transition.nest.embed = null;
|
||||
GUI.changed = true;
|
||||
}
|
||||
|
||||
public void UpdateDragAndDrop()
|
||||
{
|
||||
}
|
||||
|
||||
public void DrawDragAndDropPreview()
|
||||
{
|
||||
GraphGUI.DrawDragAndDropPreviewLabel(new Vector2(edgePosition.x, outerPosition.yMax), "Replace with: " + DragAndDropUtility.Get<ScriptGraphAsset>().name, typeof(ScriptGraphAsset).Icon());
|
||||
}
|
||||
|
||||
public void ExitDragAndDrop()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 78c6207d792514839993cf7f713d79e2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,7 @@
|
|||
namespace Unity.VisualScripting
|
||||
{
|
||||
public interface IStateTransitionWidget : INodeWidget
|
||||
{
|
||||
Edge sourceEdge { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1b9a6728ed0414076b5bd0a744bb8b21
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,29 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
[Analyser(typeof(INesterStateTransition))]
|
||||
public class NesterStateTransitionAnalyser<TGraphNesterStateTransition> : StateTransitionAnalyser<TGraphNesterStateTransition>
|
||||
where TGraphNesterStateTransition : class, INesterStateTransition
|
||||
{
|
||||
public NesterStateTransitionAnalyser(GraphReference reference, TGraphNesterStateTransition transition) : base(reference, transition) { }
|
||||
|
||||
protected override IEnumerable<Warning> Warnings()
|
||||
{
|
||||
foreach (var baseWarning in base.Warnings())
|
||||
{
|
||||
yield return baseWarning;
|
||||
}
|
||||
|
||||
if (transition.childGraph == null)
|
||||
{
|
||||
yield return Warning.Caution("Missing transition graph.");
|
||||
}
|
||||
|
||||
if (transition.nest.hasBackgroundEmbed)
|
||||
{
|
||||
yield return Warning.Caution("Background embed graph detected.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b41eaa7e036334f5abae76f726a02edc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,21 @@
|
|||
namespace Unity.VisualScripting
|
||||
{
|
||||
[Descriptor(typeof(INesterStateTransition))]
|
||||
public class NesterStateTransitionDescriptor<TNesterStateTransition> : StateTransitionDescriptor<TNesterStateTransition>
|
||||
where TNesterStateTransition : class, INesterStateTransition
|
||||
{
|
||||
public NesterStateTransitionDescriptor(TNesterStateTransition transition) : base(transition) { }
|
||||
|
||||
[RequiresUnityAPI]
|
||||
public override string Title()
|
||||
{
|
||||
return GraphNesterDescriptor.Title(transition);
|
||||
}
|
||||
|
||||
[RequiresUnityAPI]
|
||||
public override string Summary()
|
||||
{
|
||||
return GraphNesterDescriptor.Summary(transition);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3ef8f00bbef6a4ceaaaa090c41d2cf23
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,30 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
[Editor(typeof(INesterStateTransition))]
|
||||
public class NesterStateTransitionEditor : StateTransitionEditor
|
||||
{
|
||||
public NesterStateTransitionEditor(Metadata metadata) : base(metadata) { }
|
||||
|
||||
private Metadata nestMetadata => metadata[nameof(INesterStateTransition.nest)];
|
||||
|
||||
private Metadata graphMetadata => nestMetadata[nameof(IGraphNest.graph)];
|
||||
|
||||
protected override GraphReference headerReference => reference.ChildReference((INesterStateTransition)metadata.value, false);
|
||||
|
||||
protected override Metadata headerTitleMetadata => graphMetadata[nameof(IGraph.title)];
|
||||
|
||||
protected override Metadata headerSummaryMetadata => graphMetadata[nameof(IGraph.summary)];
|
||||
|
||||
protected override float GetInspectorHeight(float width)
|
||||
{
|
||||
return LudiqGUI.GetEditorHeight(this, nestMetadata, width);
|
||||
}
|
||||
|
||||
protected override void OnInspectorGUI(Rect position)
|
||||
{
|
||||
LudiqGUI.Editor(nestMetadata, position);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 55db6890417fe448f800580347902eea
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,56 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public abstract class NesterStateTransitionWidget<TNesterStateTransition> : StateTransitionWidget<TNesterStateTransition>
|
||||
where TNesterStateTransition : class, INesterStateTransition
|
||||
{
|
||||
protected NesterStateTransitionWidget(StateCanvas canvas, TNesterStateTransition transition) : base(canvas, transition) { }
|
||||
|
||||
protected override IEnumerable<DropdownOption> contextOptions
|
||||
{
|
||||
get
|
||||
{
|
||||
var childReference = reference.ChildReference(transition, false);
|
||||
|
||||
if (childReference != null)
|
||||
{
|
||||
yield return new DropdownOption((Action)(() => window.reference = childReference), "Open");
|
||||
yield return new DropdownOption((Action)(() => GraphWindow.OpenTab(childReference)), "Open in new window");
|
||||
}
|
||||
|
||||
foreach (var baseOption in base.contextOptions)
|
||||
{
|
||||
yield return baseOption;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDoubleClick()
|
||||
{
|
||||
if (transition.graph.zoom == 1)
|
||||
{
|
||||
var childReference = reference.ChildReference(transition, false);
|
||||
|
||||
if (childReference != null)
|
||||
{
|
||||
if (e.ctrlOrCmd)
|
||||
{
|
||||
GraphWindow.OpenTab(childReference);
|
||||
}
|
||||
else
|
||||
{
|
||||
window.reference = childReference;
|
||||
}
|
||||
}
|
||||
|
||||
e.Use();
|
||||
}
|
||||
else
|
||||
{
|
||||
base.OnDoubleClick();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 292c8b7edc601474ba17d63cd957dbff
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,27 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public abstract class StateTransitionAnalyser<TStateTransition> : Analyser<TStateTransition, StateTransitionAnalysis>
|
||||
where TStateTransition : IStateTransition
|
||||
{
|
||||
protected StateTransitionAnalyser(GraphReference reference, TStateTransition target) : base(reference, target) { }
|
||||
|
||||
public TStateTransition transition => target;
|
||||
|
||||
[Assigns]
|
||||
protected virtual bool IsTraversed()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
[Assigns]
|
||||
protected virtual IEnumerable<Warning> Warnings()
|
||||
{
|
||||
if (!IsTraversed())
|
||||
{
|
||||
yield return Warning.Info("Transition is never traversed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4666c372b28594071897c42f3fd2f47e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
namespace Unity.VisualScripting
|
||||
{
|
||||
public sealed class StateTransitionDescription : GraphElementDescription
|
||||
{
|
||||
public string label { get; set; }
|
||||
public string tooltip { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2cf1e35e2ea20404cb31e0153aec76c7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,41 @@
|
|||
namespace Unity.VisualScripting
|
||||
{
|
||||
public abstract class StateTransitionDescriptor<TStateTransition> : Descriptor<TStateTransition, StateTransitionDescription>
|
||||
where TStateTransition : class, IStateTransition
|
||||
{
|
||||
protected StateTransitionDescriptor(TStateTransition target) : base(target) { }
|
||||
|
||||
public TStateTransition transition => target;
|
||||
|
||||
[Assigns]
|
||||
public override string Title()
|
||||
{
|
||||
return "Transition";
|
||||
}
|
||||
|
||||
[Assigns]
|
||||
public override string Summary()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
[Assigns]
|
||||
public virtual string Label()
|
||||
{
|
||||
return Title();
|
||||
}
|
||||
|
||||
[Assigns]
|
||||
public virtual string Tooltip()
|
||||
{
|
||||
return Summary();
|
||||
}
|
||||
|
||||
[Assigns]
|
||||
[RequiresUnityAPI]
|
||||
public override EditorTexture Icon()
|
||||
{
|
||||
return typeof(IStateTransition).Icon();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 82e797f2fea594d6f89e0146757d0ed6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,12 @@
|
|||
namespace Unity.VisualScripting
|
||||
{
|
||||
[Editor(typeof(IStateTransition))]
|
||||
public class StateTransitionEditor : GraphElementEditor<StateGraphContext>
|
||||
{
|
||||
public StateTransitionEditor(Metadata metadata) : base(metadata) { }
|
||||
|
||||
private IStateTransition transition => (IStateTransition)element;
|
||||
|
||||
protected new StateTransitionDescription description => (StateTransitionDescription)base.description;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9d4e73ed999c34cb3ae93178fcb08712
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,692 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public abstract class StateTransitionWidget<TStateTransition> : NodeWidget<StateCanvas, TStateTransition>, IStateTransitionWidget
|
||||
where TStateTransition : class, IStateTransition
|
||||
{
|
||||
protected StateTransitionWidget(StateCanvas canvas, TStateTransition transition) : base(canvas, transition) { }
|
||||
|
||||
|
||||
#region Model
|
||||
|
||||
protected TStateTransition transition => element;
|
||||
|
||||
protected IStateTransitionDebugData transitionDebugData => GetDebugData<IStateTransitionDebugData>();
|
||||
|
||||
private StateTransitionDescription description;
|
||||
|
||||
private StateTransitionAnalysis analysis => transition.Analysis<StateTransitionAnalysis>(context);
|
||||
|
||||
protected override void CacheDescription()
|
||||
{
|
||||
description = transition.Description<StateTransitionDescription>();
|
||||
|
||||
label.text = description.label;
|
||||
label.image = description.icon?[IconSize.Small];
|
||||
label.tooltip = description.tooltip;
|
||||
|
||||
if (!revealLabel)
|
||||
{
|
||||
label.tooltip = label.text + ": " + label.tooltip;
|
||||
}
|
||||
|
||||
Reposition();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lifecycle
|
||||
|
||||
public override void BeforeFrame()
|
||||
{
|
||||
base.BeforeFrame();
|
||||
|
||||
if (showDroplets)
|
||||
{
|
||||
GraphGUI.UpdateDroplets(canvas, droplets, transitionDebugData.lastBranchFrame, ref lastBranchTime, ref dropTime);
|
||||
}
|
||||
|
||||
if (currentInnerWidth != targetInnerWidth)
|
||||
{
|
||||
Reposition();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Contents
|
||||
|
||||
private GUIContent label { get; } = new GUIContent();
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Positioning
|
||||
|
||||
private readonly List<IStateTransition> siblingStateTransitions = new List<IStateTransition>();
|
||||
|
||||
private Rect sourcePosition;
|
||||
|
||||
private Rect destinationPosition;
|
||||
|
||||
private Edge sourceEdge;
|
||||
|
||||
private Edge entryEdge;
|
||||
|
||||
private Edge exitEdge;
|
||||
|
||||
private Edge destinationEdge;
|
||||
|
||||
private Vector2 sourceEdgeCenter;
|
||||
|
||||
private Vector2 entryEdgeCenter;
|
||||
|
||||
private Vector2 exitEdgeCenter;
|
||||
|
||||
private Vector2 destinationEdgeCenter;
|
||||
|
||||
private Vector2 middle;
|
||||
|
||||
private Rect _position;
|
||||
|
||||
private Rect _clippingPosition;
|
||||
|
||||
private float targetInnerWidth;
|
||||
|
||||
private float currentInnerWidth;
|
||||
|
||||
private bool revealInitialized;
|
||||
|
||||
private float minBend
|
||||
{
|
||||
get
|
||||
{
|
||||
if (transition.source != transition.destination)
|
||||
{
|
||||
return 15;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (middle.y - canvas.Widget(transition.source).position.center.y) / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private float relativeBend => 1 / 4f;
|
||||
|
||||
Edge IStateTransitionWidget.sourceEdge => sourceEdge;
|
||||
|
||||
public override IEnumerable<IWidget> positionDependencies
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return canvas.Widget(transition.source);
|
||||
yield return canvas.Widget(transition.destination);
|
||||
}
|
||||
}
|
||||
|
||||
public override IEnumerable<IWidget> positionDependers
|
||||
{
|
||||
get
|
||||
{
|
||||
// Return all sibling transitions. This is an asymetrical dependency / depender
|
||||
// relation (because the siblings are not included in the dependers) to force
|
||||
// repositioning of siblings while avoiding stack overflow.
|
||||
|
||||
foreach (var graphTransition in canvas.graph.transitions)
|
||||
{
|
||||
var current = transition == graphTransition;
|
||||
|
||||
var analog =
|
||||
transition.source == graphTransition.source &&
|
||||
transition.destination == graphTransition.destination;
|
||||
|
||||
var inverted =
|
||||
transition.source == graphTransition.destination &&
|
||||
transition.destination == graphTransition.source;
|
||||
|
||||
if (!current && (analog || inverted))
|
||||
{
|
||||
var widget = canvas.Widget(graphTransition);
|
||||
|
||||
if (widget.isPositionValid) // Avoid stack overflow
|
||||
{
|
||||
yield return widget;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Rect iconPosition { get; private set; }
|
||||
|
||||
public Rect clipPosition { get; private set; }
|
||||
|
||||
public Rect labelInnerPosition { get; private set; }
|
||||
|
||||
public override Rect position
|
||||
{
|
||||
get { return _position; }
|
||||
set { }
|
||||
}
|
||||
|
||||
public override Rect clippingPosition => _clippingPosition;
|
||||
|
||||
public override void CachePositionFirstPass()
|
||||
{
|
||||
// Calculate the size immediately, because other transitions will rely on it for positioning
|
||||
|
||||
targetInnerWidth = Styles.eventIcon.fixedWidth;
|
||||
|
||||
var labelWidth = Styles.label.CalcSize(label).x;
|
||||
var labelHeight = EditorGUIUtility.singleLineHeight;
|
||||
|
||||
if (revealLabel)
|
||||
{
|
||||
targetInnerWidth += Styles.spaceAroundIcon;
|
||||
targetInnerWidth += labelWidth;
|
||||
}
|
||||
|
||||
if (!revealInitialized)
|
||||
{
|
||||
currentInnerWidth = targetInnerWidth;
|
||||
revealInitialized = true;
|
||||
}
|
||||
|
||||
currentInnerWidth = Mathf.Lerp(currentInnerWidth, targetInnerWidth, canvas.repaintDeltaTime * Styles.revealSpeed);
|
||||
|
||||
if (Mathf.Abs(targetInnerWidth - currentInnerWidth) < 1)
|
||||
{
|
||||
currentInnerWidth = targetInnerWidth;
|
||||
}
|
||||
|
||||
var innerWidth = currentInnerWidth;
|
||||
var innerHeight = labelHeight;
|
||||
|
||||
var edgeSize = InnerToEdgePosition(new Rect(0, 0, innerWidth, innerHeight)).size;
|
||||
var edgeWidth = edgeSize.x;
|
||||
var edgeHeight = edgeSize.y;
|
||||
|
||||
_position.width = edgeWidth;
|
||||
_position.height = edgeHeight;
|
||||
}
|
||||
|
||||
public override void CachePosition()
|
||||
{
|
||||
var innerWidth = innerPosition.width;
|
||||
var innerHeight = innerPosition.height;
|
||||
var edgeWidth = edgePosition.width;
|
||||
var edgeHeight = edgePosition.height;
|
||||
var labelWidth = Styles.label.CalcSize(label).x;
|
||||
var labelHeight = EditorGUIUtility.singleLineHeight;
|
||||
|
||||
sourcePosition = canvas.Widget(transition.source).position;
|
||||
destinationPosition = canvas.Widget(transition.destination).position;
|
||||
|
||||
Vector2 sourceClosestPoint;
|
||||
Vector2 destinationClosestPoint;
|
||||
LudiqGUIUtility.ClosestPoints(sourcePosition, destinationPosition, out sourceClosestPoint, out destinationClosestPoint);
|
||||
|
||||
if (transition.destination != transition.source)
|
||||
{
|
||||
GraphGUI.GetConnectionEdge
|
||||
(
|
||||
sourceClosestPoint,
|
||||
destinationClosestPoint,
|
||||
out sourceEdge,
|
||||
out destinationEdge
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
sourceEdge = Edge.Right;
|
||||
destinationEdge = Edge.Left;
|
||||
}
|
||||
|
||||
sourceEdgeCenter = sourcePosition.GetEdgeCenter(sourceEdge);
|
||||
destinationEdgeCenter = destinationPosition.GetEdgeCenter(destinationEdge);
|
||||
|
||||
siblingStateTransitions.Clear();
|
||||
|
||||
var siblingIndex = 0;
|
||||
|
||||
// Assign one common axis for transition for all siblings,
|
||||
// regardless of their inversion. The axis is arbitrarily
|
||||
// chosen as the axis for the first transition.
|
||||
var assignedTransitionAxis = false;
|
||||
var transitionAxis = Vector2.zero;
|
||||
|
||||
foreach (var graphTransition in canvas.graph.transitions)
|
||||
{
|
||||
var current = transition == graphTransition;
|
||||
|
||||
var analog =
|
||||
transition.source == graphTransition.source &&
|
||||
transition.destination == graphTransition.destination;
|
||||
|
||||
var inverted =
|
||||
transition.source == graphTransition.destination &&
|
||||
transition.destination == graphTransition.source;
|
||||
|
||||
if (current)
|
||||
{
|
||||
siblingIndex = siblingStateTransitions.Count;
|
||||
}
|
||||
|
||||
if (current || analog || inverted)
|
||||
{
|
||||
if (!assignedTransitionAxis)
|
||||
{
|
||||
var siblingStateTransitionDrawer = canvas.Widget<IStateTransitionWidget>(graphTransition);
|
||||
|
||||
transitionAxis = siblingStateTransitionDrawer.sourceEdge.Normal();
|
||||
|
||||
assignedTransitionAxis = true;
|
||||
}
|
||||
|
||||
siblingStateTransitions.Add(graphTransition);
|
||||
}
|
||||
}
|
||||
|
||||
// Fix the edge case where the source and destination perfectly overlap
|
||||
|
||||
if (transitionAxis == Vector2.zero)
|
||||
{
|
||||
transitionAxis = Vector2.right;
|
||||
}
|
||||
|
||||
// Calculate the spread axis and origin for the set of siblings
|
||||
|
||||
var spreadAxis = transitionAxis.Perpendicular1().Abs();
|
||||
var spreadOrigin = (sourceEdgeCenter + destinationEdgeCenter) / 2;
|
||||
|
||||
if (transition.source == transition.destination)
|
||||
{
|
||||
spreadAxis = Vector2.up;
|
||||
spreadOrigin = sourcePosition.GetEdgeCenter(Edge.Bottom) - Vector2.down * 10;
|
||||
}
|
||||
|
||||
if (BoltCore.Configuration.developerMode && BoltCore.Configuration.debug)
|
||||
{
|
||||
Handles.BeginGUI();
|
||||
Handles.color = Color.yellow;
|
||||
Handles.DrawLine(spreadOrigin + spreadAxis * -1000, spreadOrigin + spreadAxis * 1000);
|
||||
Handles.EndGUI();
|
||||
}
|
||||
|
||||
// Calculate the offset of the current sibling by iterating over its predecessors
|
||||
|
||||
var spreadOffset = 0f;
|
||||
var previousSpreadSize = 0f;
|
||||
|
||||
for (var i = 0; i <= siblingIndex; i++)
|
||||
{
|
||||
var siblingSize = canvas.Widget<IStateTransitionWidget>(siblingStateTransitions[i]).outerPosition.size;
|
||||
var siblingSizeProjection = GraphGUI.SizeProjection(siblingSize, spreadOrigin, spreadAxis);
|
||||
spreadOffset += previousSpreadSize / 2 + siblingSizeProjection / 2;
|
||||
previousSpreadSize = siblingSizeProjection;
|
||||
}
|
||||
|
||||
if (transition.source != transition.destination)
|
||||
{
|
||||
// Calculate the total spread size to center the sibling set
|
||||
|
||||
var totalSpreadSize = 0f;
|
||||
|
||||
for (var i = 0; i < siblingStateTransitions.Count; i++)
|
||||
{
|
||||
var siblingSize = canvas.Widget<IStateTransitionWidget>(siblingStateTransitions[i]).outerPosition.size;
|
||||
var siblingSizeProjection = GraphGUI.SizeProjection(siblingSize, spreadOrigin, spreadAxis);
|
||||
totalSpreadSize += siblingSizeProjection;
|
||||
}
|
||||
|
||||
spreadOffset -= totalSpreadSize / 2;
|
||||
}
|
||||
|
||||
// Finally, calculate the positions
|
||||
|
||||
middle = spreadOrigin + spreadOffset * spreadAxis;
|
||||
|
||||
var edgeX = middle.x - edgeWidth / 2;
|
||||
var edgeY = middle.y - edgeHeight / 2;
|
||||
|
||||
_position = new Rect
|
||||
(
|
||||
edgeX,
|
||||
edgeY,
|
||||
edgeWidth,
|
||||
edgeHeight
|
||||
).PixelPerfect();
|
||||
|
||||
var innerX = innerPosition.x;
|
||||
var innerY = innerPosition.y;
|
||||
|
||||
_clippingPosition = _position.Encompass(sourceEdgeCenter).Encompass(destinationEdgeCenter);
|
||||
|
||||
if (transition.source != transition.destination)
|
||||
{
|
||||
entryEdge = destinationEdge;
|
||||
exitEdge = sourceEdge;
|
||||
}
|
||||
else
|
||||
{
|
||||
entryEdge = sourceEdge;
|
||||
exitEdge = destinationEdge;
|
||||
}
|
||||
|
||||
entryEdgeCenter = edgePosition.GetEdgeCenter(entryEdge);
|
||||
exitEdgeCenter = edgePosition.GetEdgeCenter(exitEdge);
|
||||
|
||||
var x = innerX;
|
||||
|
||||
iconPosition = new Rect
|
||||
(
|
||||
x,
|
||||
innerY,
|
||||
Styles.eventIcon.fixedWidth,
|
||||
Styles.eventIcon.fixedHeight
|
||||
).PixelPerfect();
|
||||
|
||||
x += iconPosition.width;
|
||||
|
||||
var clipWidth = innerWidth - (x - innerX);
|
||||
|
||||
clipPosition = new Rect
|
||||
(
|
||||
x,
|
||||
edgeY,
|
||||
clipWidth,
|
||||
edgeHeight
|
||||
).PixelPerfect();
|
||||
|
||||
labelInnerPosition = new Rect
|
||||
(
|
||||
Styles.spaceAroundIcon,
|
||||
innerY - edgeY,
|
||||
labelWidth,
|
||||
labelHeight
|
||||
).PixelPerfect();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Drawing
|
||||
|
||||
protected virtual NodeColorMix baseColor => NodeColor.Gray;
|
||||
|
||||
protected override NodeColorMix color
|
||||
{
|
||||
get
|
||||
{
|
||||
if (transitionDebugData.runtimeException != null)
|
||||
{
|
||||
return NodeColor.Red;
|
||||
}
|
||||
|
||||
var color = baseColor;
|
||||
|
||||
if (analysis.warnings.Count > 0)
|
||||
{
|
||||
var mostSevereWarning = Warning.MostSevereLevel(analysis.warnings);
|
||||
|
||||
switch (mostSevereWarning)
|
||||
{
|
||||
case WarningLevel.Error:
|
||||
color = NodeColor.Red;
|
||||
|
||||
break;
|
||||
|
||||
case WarningLevel.Severe:
|
||||
color = NodeColor.Orange;
|
||||
|
||||
break;
|
||||
|
||||
case WarningLevel.Caution:
|
||||
color = NodeColor.Yellow;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (EditorApplication.isPlaying)
|
||||
{
|
||||
if (EditorApplication.isPaused)
|
||||
{
|
||||
if (EditorTimeBinding.frame == transitionDebugData.lastBranchFrame)
|
||||
{
|
||||
color = NodeColor.Blue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
color.blue = Mathf.Lerp(1, 0, (EditorTimeBinding.time - transitionDebugData.lastBranchTime) / StateWidget<IState>.Styles.enterFadeDuration);
|
||||
}
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
}
|
||||
|
||||
protected override NodeShape shape => NodeShape.Hex;
|
||||
|
||||
private bool revealLabel
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (BoltState.Configuration.transitionsReveal)
|
||||
{
|
||||
case StateRevealCondition.Always:
|
||||
|
||||
return true;
|
||||
case StateRevealCondition.Never:
|
||||
|
||||
return false;
|
||||
case StateRevealCondition.OnHover:
|
||||
|
||||
return isMouseOver;
|
||||
case StateRevealCondition.OnHoverWithAlt:
|
||||
|
||||
return isMouseOver && e.alt;
|
||||
case StateRevealCondition.WhenSelected:
|
||||
|
||||
return selection.Contains(transition);
|
||||
case StateRevealCondition.OnHoverOrSelected:
|
||||
|
||||
return isMouseOver || selection.Contains(transition);
|
||||
case StateRevealCondition.OnHoverWithAltOrSelected:
|
||||
|
||||
return isMouseOver && e.alt || selection.Contains(transition);
|
||||
default:
|
||||
|
||||
throw new UnexpectedEnumValueException<StateRevealCondition>(BoltState.Configuration.transitionsReveal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool revealedLabel;
|
||||
|
||||
private void CheckReveal()
|
||||
{
|
||||
var revealLabel = this.revealLabel;
|
||||
|
||||
if (revealLabel != revealedLabel)
|
||||
{
|
||||
Reposition();
|
||||
}
|
||||
|
||||
revealedLabel = revealLabel;
|
||||
}
|
||||
|
||||
protected override bool dim
|
||||
{
|
||||
get
|
||||
{
|
||||
var dim = BoltCore.Configuration.dimInactiveNodes && !(transition.source.Analysis<StateAnalysis>(context).isEntered && analysis.isTraversed);
|
||||
|
||||
if (isMouseOver || isSelected)
|
||||
{
|
||||
dim = false;
|
||||
}
|
||||
|
||||
return dim;
|
||||
}
|
||||
}
|
||||
|
||||
public override void DrawForeground()
|
||||
{
|
||||
BeginDim();
|
||||
|
||||
base.DrawForeground();
|
||||
|
||||
GUI.Label(iconPosition, label, Styles.eventIcon);
|
||||
|
||||
GUI.BeginClip(clipPosition);
|
||||
|
||||
GUI.Label(labelInnerPosition, label, invertForeground ? Styles.labelInverted : Styles.label);
|
||||
|
||||
GUI.EndClip();
|
||||
|
||||
EndDim();
|
||||
|
||||
CheckReveal();
|
||||
}
|
||||
|
||||
public override void DrawBackground()
|
||||
{
|
||||
BeginDim();
|
||||
|
||||
base.DrawBackground();
|
||||
|
||||
DrawConnection();
|
||||
|
||||
if (showDroplets)
|
||||
{
|
||||
DrawDroplets();
|
||||
}
|
||||
|
||||
EndDim();
|
||||
}
|
||||
|
||||
private void DrawConnection()
|
||||
{
|
||||
GraphGUI.DrawConnectionArrow(Color.white, sourceEdgeCenter, entryEdgeCenter, sourceEdge, entryEdge, relativeBend, minBend);
|
||||
|
||||
if (BoltState.Configuration.transitionsEndArrow)
|
||||
{
|
||||
GraphGUI.DrawConnectionArrow(Color.white, exitEdgeCenter, destinationEdgeCenter, exitEdge, destinationEdge, relativeBend, minBend);
|
||||
}
|
||||
else
|
||||
{
|
||||
GraphGUI.DrawConnection(Color.white, exitEdgeCenter, destinationEdgeCenter, exitEdge, destinationEdge, null, Vector2.zero, relativeBend, minBend);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Selecting
|
||||
|
||||
public override bool canSelect => true;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Dragging
|
||||
|
||||
public override bool canDrag => false;
|
||||
|
||||
protected override bool snapToGrid => false;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Deleting
|
||||
|
||||
public override bool canDelete => true;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Droplets
|
||||
|
||||
private readonly List<float> droplets = new List<float>();
|
||||
|
||||
private float dropTime;
|
||||
|
||||
private float lastBranchTime;
|
||||
|
||||
protected virtual bool showDroplets => BoltState.Configuration.animateTransitions;
|
||||
|
||||
protected virtual Vector2 GetDropletSize()
|
||||
{
|
||||
return BoltFlow.Icons.valuePortConnected?[12].Size() ?? 12 * Vector2.one;
|
||||
}
|
||||
|
||||
protected virtual void DrawDroplet(Rect position)
|
||||
{
|
||||
GUI.DrawTexture(position, BoltFlow.Icons.valuePortConnected?[12]);
|
||||
}
|
||||
|
||||
private void DrawDroplets()
|
||||
{
|
||||
foreach (var droplet in droplets)
|
||||
{
|
||||
Vector2 position;
|
||||
|
||||
if (droplet < 0.5f)
|
||||
{
|
||||
var t = droplet / 0.5f;
|
||||
position = GraphGUI.GetPointOnConnection(t, sourceEdgeCenter, entryEdgeCenter, sourceEdge, entryEdge, relativeBend, minBend);
|
||||
}
|
||||
else
|
||||
{
|
||||
var t = (droplet - 0.5f) / 0.5f;
|
||||
position = GraphGUI.GetPointOnConnection(t, exitEdgeCenter, destinationEdgeCenter, exitEdge, destinationEdge, relativeBend, minBend);
|
||||
}
|
||||
|
||||
var size = GetDropletSize();
|
||||
|
||||
using (LudiqGUI.color.Override(Color.white))
|
||||
{
|
||||
DrawDroplet(new Rect(position.x - size.x / 2, position.y - size.y / 2, size.x, size.y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
public static class Styles
|
||||
{
|
||||
static Styles()
|
||||
{
|
||||
label = new GUIStyle(BoltCore.Styles.nodeLabel);
|
||||
label.alignment = TextAnchor.MiddleCenter;
|
||||
label.imagePosition = ImagePosition.TextOnly;
|
||||
|
||||
labelInverted = new GUIStyle(label);
|
||||
labelInverted.normal.textColor = ColorPalette.unityBackgroundDark;
|
||||
|
||||
eventIcon = new GUIStyle();
|
||||
eventIcon.imagePosition = ImagePosition.ImageOnly;
|
||||
eventIcon.fixedHeight = 16;
|
||||
eventIcon.fixedWidth = 16;
|
||||
}
|
||||
|
||||
public static readonly GUIStyle label;
|
||||
|
||||
public static readonly GUIStyle labelInverted;
|
||||
|
||||
public static readonly GUIStyle eventIcon;
|
||||
|
||||
public static readonly float spaceAroundIcon = 5;
|
||||
|
||||
public static readonly float revealSpeed = 15;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3d4c55a931854453da3c5a0466ef9fb8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,10 @@
|
|||
namespace Unity.VisualScripting
|
||||
{
|
||||
[Widget(typeof(TriggerStateTransition))]
|
||||
public sealed class TriggerStateTransitionWidget : UnitWidget<TriggerStateTransition>
|
||||
{
|
||||
public TriggerStateTransitionWidget(FlowCanvas canvas, TriggerStateTransition unit) : base(canvas, unit) { }
|
||||
|
||||
protected override NodeColorMix baseColor => NodeColorMix.TealReadable;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b189625584c90451ebec9bd6a0e5e334
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue