Created unity project

This commit is contained in:
2024-12-07 20:55:50 +01:00
parent 539250d964
commit 54fe327198
13758 changed files with 865324 additions and 0 deletions

View File

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

View File

@@ -0,0 +1,14 @@
namespace Unity.VisualScripting
{
[Plugin(BoltProduct.ID)]
internal class Acknowledgement_NCalc : PluginAcknowledgement
{
public Acknowledgement_NCalc(Plugin plugin) : base(plugin) { }
public override string title => "NCalc";
public override string author => "Sébastien Ros";
public override string url => "https://ncalc.codeplex.com/";
public override string licenseName => "MIT";
public override string licenseText => CommonLicenses.MIT;
}
}

View File

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

View File

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

View File

@@ -0,0 +1,21 @@
using UnityEditor;
namespace Unity.VisualScripting.Analytics
{
class FlowMacroSavedEvent : UnityEditor.AssetModificationProcessor
{
static string[] OnWillSaveAssets(string[] paths)
{
foreach (string path in paths)
{
var assetType = AssetDatabase.GetMainAssetTypeAtPath(path);
if (assetType == typeof(ScriptGraphAsset))
{
UsageAnalytics.CollectAndSend();
break;
}
}
return paths;
}
}
}

View File

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

View File

@@ -0,0 +1,58 @@
using System;
using System.Linq;
namespace Unity.VisualScripting
{
public static class BoltFlowNameUtility
{
[Obsolete("This method is obsolete. Please use the new UnitTitle(unitType, short, includeStatus) instead.")]
public static string UnitTitle(Type unitType, bool @short)
{
if (@short)
{
var shortTitle = unitType.GetAttribute<UnitShortTitleAttribute>()?.title;
if (shortTitle != null)
{
return shortTitle;
}
}
var title = unitType.GetAttribute<UnitTitleAttribute>()?.title;
if (title != null)
{
return title;
}
return unitType.HumanName();
}
public static string UnitTitle(Type unitType, bool @short, bool includeStatus)
{
var suffix = string.Empty;
if (includeStatus && Attribute.IsDefined(unitType, typeof(ObsoleteAttribute)))
suffix = " (Deprecated)";
if (@short)
{
var shortTitle = unitType.GetAttribute<UnitShortTitleAttribute>()?.title;
if (shortTitle != null)
{
return $"{shortTitle} {suffix}";
}
}
var title = unitType.GetAttribute<UnitTitleAttribute>()?.title;
return title != null ? $"{title} {suffix}" : $"{unitType.HumanName()} {suffix}";
}
public static string UnitPreviousTitle(Type unitType)
{
var title = unitType.GetAttribute<RenamedFromAttribute>()?.previousName.Split('.').Last();
return title ?? string.Empty;
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,39 @@
using UnityEngine;
namespace Unity.VisualScripting
{
[Widget(typeof(ControlConnection))]
public sealed class ControlConnectionWidget : UnitConnectionWidget<ControlConnection>
{
public ControlConnectionWidget(FlowCanvas canvas, ControlConnection connection) : base(canvas, connection) { }
#region Drawing
public override Color color => Color.white;
protected override bool colorIfActive => !BoltFlow.Configuration.animateControlConnections || !BoltFlow.Configuration.animateValueConnections;
#endregion
#region Droplets
protected override bool showDroplets => BoltFlow.Configuration.animateControlConnections;
protected override Vector2 GetDropletSize()
{
return BoltFlow.Icons.valuePortConnected?[12].Size() ?? 12 * Vector2.one;
}
protected override void DrawDroplet(Rect position)
{
if (BoltFlow.Icons.valuePortConnected != null)
{
GUI.DrawTexture(position, BoltFlow.Icons.valuePortConnected[12]);
}
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,9 @@
using UnityEngine;
namespace Unity.VisualScripting
{
public interface IUnitConnectionWidget : IGraphElementWidget
{
Color color { get; }
}
}

View File

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

View File

@@ -0,0 +1,28 @@
using UnityEngine;
namespace Unity.VisualScripting
{
[Widget(typeof(InvalidConnection))]
public sealed class InvalidConnectionWidget : UnitConnectionWidget<InvalidConnection>
{
public InvalidConnectionWidget(FlowCanvas canvas, InvalidConnection connection) : base(canvas, connection) { }
#region Drawing
public override Color color => UnitConnectionStyles.invalidColor;
#endregion
#region Droplets
protected override bool showDroplets => false;
protected override Vector2 GetDropletSize() => Vector2.zero;
protected override void DrawDroplet(Rect position) { }
#endregion
}
}

View File

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

View File

@@ -0,0 +1,19 @@
using UnityEngine;
namespace Unity.VisualScripting
{
public static class UnitConnectionStyles
{
public static readonly Color activeColor = new Color(0.37f, 0.66f, 0.95f);
public static readonly Color highlightColor = new Color(1, 0.95f, 0f);
public static readonly Color invalidColor = new Color(1, 0, 0);
public static readonly Color disconnectColor = new Color(0.95f, 0.1f, 0.1f);
public static readonly float minBend = 15;
public static readonly float relativeBend = 1 / 4f;
}
}

View File

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

View File

@@ -0,0 +1,247 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Unity.VisualScripting
{
public abstract class UnitConnectionWidget<TConnection> : GraphElementWidget<FlowCanvas, TConnection>, IUnitConnectionWidget
where TConnection : class, IUnitConnection
{
protected UnitConnectionWidget(FlowCanvas canvas, TConnection connection) : base(canvas, connection) { }
#region Model
protected TConnection connection => element;
protected IUnitConnectionDebugData ConnectionDebugData => GetDebugData<IUnitConnectionDebugData>();
#endregion
#region Lifecycle
public override void BeforeFrame()
{
base.BeforeFrame();
if (showDroplets)
{
GraphGUI.UpdateDroplets(canvas, droplets, ConnectionDebugData.lastInvokeFrame, ref lastInvokeTime, ref dropTime);
}
}
#endregion
#region Positioning
public override IEnumerable<IWidget> positionDependencies
{
get
{
yield return canvas.Widget(connection.source);
yield return canvas.Widget(connection.destination);
}
}
protected override bool snapToGrid => false;
public Rect sourceHandlePosition { get; private set; }
public Rect destinationHandlePosition { get; private set; }
public Vector2 sourceHandleEdgeCenter { get; private set; }
public Vector2 destinationHandleEdgeCenter { get; private set; }
public Vector2 middlePosition;
private Rect _position;
private Rect _clippingPosition;
public override Rect position
{
get { return _position; }
set { }
}
public override Rect clippingPosition => _clippingPosition;
public override void CachePosition()
{
base.CachePosition();
sourceHandlePosition = canvas.Widget<IUnitPortWidget>(connection.source).handlePosition;
destinationHandlePosition = canvas.Widget<IUnitPortWidget>(connection.destination).handlePosition;
sourceHandleEdgeCenter = sourceHandlePosition.GetEdgeCenter(Edge.Right);
destinationHandleEdgeCenter = destinationHandlePosition.GetEdgeCenter(Edge.Left);
middlePosition = (sourceHandlePosition.center + destinationHandlePosition.center) / 2;
_position = new Rect
(
middlePosition.x,
middlePosition.y,
0,
0
);
_clippingPosition = _position.Encompass(sourceHandleEdgeCenter).Encompass(destinationHandleEdgeCenter);
}
#endregion
#region Drawing
protected virtual bool colorIfActive => true;
public abstract Color color { get; }
protected override bool dim
{
get
{
var dim = BoltCore.Configuration.dimInactiveNodes && !connection.destination.unit.Analysis<UnitAnalysis>(context).isEntered;
if (BoltCore.Configuration.dimIncompatibleNodes && canvas.isCreatingConnection)
{
dim = true;
}
return dim;
}
}
public override void DrawBackground()
{
base.DrawBackground();
BeginDim();
DrawConnection();
if (showDroplets)
{
DrawDroplets();
}
EndDim();
}
protected virtual void DrawConnection()
{
var color = this.color;
var sourceWidget = canvas.Widget<IUnitPortWidget>(connection.source);
var destinationWidget = canvas.Widget<IUnitPortWidget>(connection.destination);
var highlight = !canvas.isCreatingConnection && (sourceWidget.isMouseOver || destinationWidget.isMouseOver);
var willDisconnect = sourceWidget.willDisconnect || destinationWidget.willDisconnect;
if (willDisconnect)
{
color = UnitConnectionStyles.disconnectColor;
}
else if (highlight)
{
color = UnitConnectionStyles.highlightColor;
}
else if (colorIfActive)
{
if (EditorApplication.isPaused)
{
if (EditorTimeBinding.frame == ConnectionDebugData.lastInvokeFrame)
{
color = UnitConnectionStyles.activeColor;
}
}
else
{
color = Color.Lerp(UnitConnectionStyles.activeColor, color, (EditorTimeBinding.time - ConnectionDebugData.lastInvokeTime) / UnitWidget<IUnit>.Styles.invokeFadeDuration);
}
}
var thickness = 3;
GraphGUI.DrawConnection(color, sourceHandleEdgeCenter, destinationHandleEdgeCenter, Edge.Right, Edge.Left, null, Vector2.zero, UnitConnectionStyles.relativeBend, UnitConnectionStyles.minBend, thickness);
}
#endregion
#region Selecting
public override bool canSelect => false;
#endregion
#region Dragging
public override bool canDrag => 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 lastInvokeTime;
private const float handleAlignmentMargin = 0.1f;
protected virtual bool showDroplets => true;
protected abstract Vector2 GetDropletSize();
protected abstract void DrawDroplet(Rect position);
protected virtual void DrawDroplets()
{
foreach (var droplet in droplets)
{
Vector2 position;
if (droplet < handleAlignmentMargin)
{
var t = droplet / handleAlignmentMargin;
position = Vector2.Lerp(sourceHandlePosition.center, sourceHandleEdgeCenter, t);
}
else if (droplet > 1 - handleAlignmentMargin)
{
var t = (droplet - (1 - handleAlignmentMargin)) / handleAlignmentMargin;
position = Vector2.Lerp(destinationHandleEdgeCenter, destinationHandlePosition.center, t);
}
else
{
var t = (droplet - handleAlignmentMargin) / (1 - 2 * handleAlignmentMargin);
position = GraphGUI.GetPointOnConnection(t, sourceHandleEdgeCenter, destinationHandleEdgeCenter, Edge.Right, Edge.Left, UnitConnectionStyles.relativeBend, UnitConnectionStyles.minBend);
}
var size = GetDropletSize();
using (LudiqGUI.color.Override(GUI.color * color))
{
DrawDroplet(new Rect(position.x - size.x / 2, position.y - size.y / 2, size.x, size.y));
}
}
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,150 @@
using System;
using UnityEditor;
using UnityEngine;
namespace Unity.VisualScripting
{
[Widget(typeof(ValueConnection))]
public sealed class ValueConnectionWidget : UnitConnectionWidget<ValueConnection>
{
public ValueConnectionWidget(FlowCanvas canvas, ValueConnection connection) : base(canvas, connection) { }
private new ValueConnection.DebugData ConnectionDebugData => GetDebugData<ValueConnection.DebugData>();
#region Drawing
public override Color color => DetermineColor(connection.source.type, connection.destination.type);
protected override bool colorIfActive => !BoltFlow.Configuration.animateControlConnections || !BoltFlow.Configuration.animateValueConnections;
public override void DrawForeground()
{
base.DrawForeground();
if (BoltFlow.Configuration.showConnectionValues)
{
var showLastValue = EditorApplication.isPlaying && ConnectionDebugData.assignedLastValue;
var showPredictedvalue = BoltFlow.Configuration.predictConnectionValues && !EditorApplication.isPlaying && Flow.CanPredict(connection.source, reference);
if (showLastValue || showPredictedvalue)
{
var previousIconSize = EditorGUIUtility.GetIconSize();
EditorGUIUtility.SetIconSize(new Vector2(IconSize.Small, IconSize.Small));
object value;
if (showLastValue)
{
value = ConnectionDebugData.lastValue;
}
else // if (showPredictedvalue)
{
value = Flow.Predict(connection.source, reference);
}
var label = new GUIContent(value.ToShortString(), Icons.Type(value?.GetType())?[IconSize.Small]);
var labelSize = Styles.prediction.CalcSize(label);
var labelPosition = new Rect(position.position - labelSize / 2, labelSize);
BeginDim();
GUI.Label(labelPosition, label, Styles.prediction);
EndDim();
EditorGUIUtility.SetIconSize(previousIconSize);
}
}
}
public static Color DetermineColor(Type source, Type destination)
{
if (destination == typeof(object))
{
return DetermineColor(source);
}
return DetermineColor(destination);
}
public static Color DetermineColor(Type type)
{
if (type == null)
{
return new Color(0.8f, 0.8f, 0.8f);
}
if (type == typeof(string))
{
return new Color(1.0f, 0.62f, 0.35f);
}
if (type == typeof(bool))
{
return new Color(0.86f, 0.55f, 0.92f);
}
if (type == typeof(char))
{
return new Color(1.0f, 0.90f, 0.40f);
}
if (type.IsEnum)
{
return new Color(1.0f, 0.63f, 0.66f);
}
if (type.IsNumeric())
{
return new Color(0.45f, 0.78f, 1f);
}
if (type.IsNumericConstruct())
{
return new Color(0.45f, 1.00f, 0.82f);
}
return new Color(0.60f, 0.88f, 0.00f);
}
#endregion
#region Droplets
protected override bool showDroplets => BoltFlow.Configuration.animateValueConnections;
protected override Vector2 GetDropletSize()
{
return BoltFlow.Icons.valuePortConnected?[12].Size() ?? 12 * Vector3.one;
}
protected override void DrawDroplet(Rect position)
{
if (BoltFlow.Icons.valuePortConnected != null)
{
GUI.DrawTexture(position, BoltFlow.Icons.valuePortConnected?[12]);
}
}
#endregion
private static class Styles
{
static Styles()
{
prediction = new GUIStyle(EditorStyles.label);
prediction.normal.textColor = Color.white;
prediction.fontSize = 9;
prediction.normal.background = new Color(0, 0, 0, 0.25f).GetPixel();
prediction.padding = new RectOffset(4, 6, 3, 3);
prediction.margin = new RectOffset(0, 0, 0, 0);
prediction.alignment = TextAnchor.MiddleCenter;
}
public static readonly GUIStyle prediction;
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,8 @@
namespace Unity.VisualScripting
{
[Descriptor(typeof(FlowGraph))]
public sealed class FlowGraphDescriptor : GraphDescriptor<FlowGraph, GraphDescription>
{
public FlowGraphDescriptor(FlowGraph target) : base(target) { }
}
}

View File

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

View File

@@ -0,0 +1,8 @@
namespace Unity.VisualScripting
{
[Descriptor(typeof(ScriptMachine))]
public sealed class FlowMachineDescriptor : MachineDescriptor<ScriptMachine, MachineDescription>
{
public FlowMachineDescriptor(ScriptMachine target) : base(target) { }
}
}

View File

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

View File

@@ -0,0 +1,8 @@
namespace Unity.VisualScripting
{
[Descriptor(typeof(ScriptGraphAsset))]
public sealed class FlowMacroDescriptor : MacroDescriptor<ScriptGraphAsset, MacroDescription>
{
public FlowMacroDescriptor(ScriptGraphAsset target) : base(target) { }
}
}

View File

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

View File

@@ -0,0 +1,23 @@
namespace Unity.VisualScripting
{
public interface IUnitDescriptor : IDescriptor
{
IUnit unit { get; }
new UnitDescription description { get; }
string Title();
string ShortTitle();
string Surtitle();
string Subtitle();
string Summary();
EditorTexture Icon();
void DescribePort(IUnitPort port, UnitPortDescription description);
}
}

View File

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

View File

@@ -0,0 +1,324 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Unity.VisualScripting
{
[Analyser(typeof(IUnit))]
public class UnitAnalyser<TUnit> : Analyser<TUnit, UnitAnalysis>
where TUnit : class, IUnit
{
public UnitAnalyser(GraphReference reference, TUnit target) : base(reference, target) { }
public TUnit unit => target;
[Assigns]
protected bool IsEntered()
{
using (var recursion = Recursion.New(1))
{
return IsEntered(unit, recursion);
}
}
private static bool IsEntered(IUnit unit, Recursion recursion)
{
if (unit.isControlRoot)
{
return true;
}
foreach (var controlInput in unit.controlInputs)
{
if (!controlInput.isPredictable || controlInput.couldBeEntered)
{
return true;
}
}
foreach (var valueOutput in unit.valueOutputs)
{
if (!recursion?.TryEnter(valueOutput) ?? false)
{
continue;
}
var valueOutputEntered = valueOutput.validConnections.Any(c => IsEntered(c.destination.unit, recursion));
recursion?.Exit(valueOutput);
if (valueOutputEntered)
{
return true;
}
}
return false;
}
private string PortLabel(IUnitPort port)
{
return port.Description<UnitPortDescription>().label;
}
[Assigns]
protected virtual IEnumerable<Warning> Warnings()
{
var isEntered = IsEntered();
if (!unit.isDefined)
{
if (unit.definitionException != null)
{
yield return Warning.Exception(unit.definitionException);
}
else if (!unit.canDefine)
{
yield return Warning.Caution("Node is not properly configured.");
}
}
else if (unit is MissingType)
{
var formerType = $"{(unit as MissingType)?.formerType}";
formerType = string.IsNullOrEmpty(formerType) ? string.Empty : $"'{formerType}'";
yield return new ActionButtonWarning(
WarningLevel.Error,
$"The source script for this node type can't be found. Did you remove its script?\n" +
$"Replace the node or add the {formerType} script file back to your project files.",
"Replace Node",
() =>
{ UnitWidgetHelper.ReplaceUnit(unit, reference, context, context.selection, new EventWrapper(unit)); }
);
yield break;
}
if (!isEntered)
{
yield return Warning.Info("Node is never entered.");
}
// Obsolete attribute is not inherited, so traverse the chain manually
var obsoleteAttribute = unit.GetType().AndHierarchy().FirstOrDefault(t => t.HasAttribute<ObsoleteAttribute>())?.GetAttribute<ObsoleteAttribute>();
if (obsoleteAttribute != null)
{
var unitName = BoltFlowNameUtility.UnitTitle(unit.GetType(), true, false);
if (obsoleteAttribute.Message != null)
{
Debug.LogWarning($"\"{unitName}\" node is deprecated: {obsoleteAttribute.Message}");
yield return Warning.Caution($"Deprecated: {obsoleteAttribute.Message}");
}
else
{
Debug.LogWarning($"\"{unitName}\" node is deprecated.");
yield return Warning.Caution("This node is deprecated.");
}
}
if (unit.isDefined)
{
foreach (var invalidInput in unit.invalidInputs)
{
yield return Warning.Caution($"{PortLabel(invalidInput)} is not used by this unit.");
}
foreach (var invalidOutput in unit.invalidOutputs)
{
yield return Warning.Caution($"{PortLabel(invalidOutput)} is not provided by this unit.");
}
foreach (var validPort in unit.validPorts)
{
if (validPort.hasInvalidConnection)
{
yield return Warning.Caution($"{PortLabel(validPort)} has an invalid connection.");
}
}
#if UNITY_IOS || UNITY_ANDROID || UNITY_TVOS
if (unit is IMouseEventUnit)
{
var graphName = string.IsNullOrEmpty(unit.graph.title) ? "A ScriptGraph" : $"The ScriptGraph {unit.graph.title}";
var unitName = BoltFlowNameUtility.UnitTitle(unit.GetType(), true, false);
Debug.LogWarning($"{graphName} contains a {unitName} node. Presence of MouseEvent nodes might impact performance on handheld devices.");
yield return Warning.Caution("Presence of MouseEvent nodes might impact performance on handheld devices.");
}
#endif
}
foreach (var controlInput in unit.controlInputs)
{
if (!controlInput.hasValidConnection)
{
continue;
}
foreach (var relation in controlInput.relations)
{
if (relation.source is ValueInput)
{
var valueInput = (ValueInput)relation.source;
foreach (var warning in ValueInputWarnings(valueInput))
{
yield return warning;
}
}
}
}
foreach (var controlOutput in unit.controlOutputs)
{
if (!controlOutput.hasValidConnection)
{
continue;
}
var controlInputs = controlOutput.relations.Select(r => r.source).OfType<ControlInput>();
var isTriggered = !controlInputs.Any() || controlInputs.Any(ci => !ci.isPredictable || ci.couldBeEntered);
foreach (var relation in controlOutput.relations)
{
if (relation.source is ValueInput)
{
var valueInput = (ValueInput)relation.source;
foreach (var warning in ValueInputWarnings(valueInput))
{
yield return warning;
}
}
}
if (isEntered && !isTriggered)
{
yield return Warning.Caution($"{PortLabel(controlOutput)} is connected, but it is never triggered.");
}
}
foreach (var valueOutput in unit.valueOutputs)
{
if (!valueOutput.hasValidConnection)
{
continue;
}
foreach (var relation in valueOutput.relations)
{
if (relation.source is ControlInput)
{
var controlInput = (ControlInput)relation.source;
if (isEntered && controlInput.isPredictable && !controlInput.couldBeEntered)
{
yield return Warning.Severe($"{PortLabel(controlInput)} is required, but it is never entered.");
}
}
else if (relation.source is ValueInput)
{
var valueInput = (ValueInput)relation.source;
foreach (var warning in ValueInputWarnings(valueInput))
{
yield return warning;
}
}
}
}
}
private IEnumerable<Warning> ValueInputWarnings(ValueInput valueInput)
{
// We can disable null reference check if no self is available
// and the port requires an owner, for example in macros.
var trustFutureOwner = valueInput.nullMeansSelf && reference.self == null;
var checkForNullReference = BoltFlow.Configuration.predictPotentialNullReferences && !valueInput.allowsNull && !trustFutureOwner;
var checkForMissingComponent = BoltFlow.Configuration.predictPotentialMissingComponents && typeof(Component).IsAssignableFrom(valueInput.type);
// Note that we cannot directly check the input's predicted value, because it
// will return false for safeguard specifically because it might be missing requirements.
// Therefore, we first check the connected value, then the default value.
// If the port is connected to a predictable output, use the connected value to perform checks.
if (valueInput.hasValidConnection)
{
var valueOutput = valueInput.validConnectedPorts.Single();
if (Flow.CanPredict(valueOutput, reference))
{
if (checkForNullReference)
{
if (Flow.Predict(valueOutput, reference) == null)
{
yield return Warning.Severe($"{PortLabel(valueInput)} cannot be null.");
}
}
if (checkForMissingComponent)
{
var connectedPredictedValue = Flow.Predict(valueOutput, reference);
// This check is necessary, because the predicted value could be
// incompatible as connections with non-guaranteed conversions are allowed.
if (ConversionUtility.CanConvert(connectedPredictedValue, typeof(GameObject), true))
{
var gameObject = ConversionUtility.Convert<GameObject>(connectedPredictedValue);
if (gameObject != null)
{
var component = (Component)ConversionUtility.Convert(gameObject, valueInput.type);
if (component == null)
{
yield return Warning.Caution($"{PortLabel(valueInput)} is missing a {valueInput.type.DisplayName()} component.");
}
}
}
}
}
}
// If the port isn't connected but has a default value, use the default value to perform checks.
else if (valueInput.hasDefaultValue)
{
if (checkForNullReference)
{
if (Flow.Predict(valueInput, reference) == null)
{
yield return Warning.Severe($"{PortLabel(valueInput)} cannot be null.");
}
}
if (checkForMissingComponent)
{
var unconnectedPredictedValue = Flow.Predict(valueInput, reference);
if (ConversionUtility.CanConvert(unconnectedPredictedValue, typeof(GameObject), true))
{
var gameObject = ConversionUtility.Convert<GameObject>(unconnectedPredictedValue);
if (gameObject != null)
{
var component = (Component)ConversionUtility.Convert(gameObject, valueInput.type);
if (component == null)
{
yield return Warning.Caution($"{PortLabel(valueInput)} is missing a {valueInput.type.DisplayName()} component.");
}
}
}
}
}
// The value isn't connected and has no default value,
// therefore it is certain to be missing at runtime.
else
{
yield return Warning.Severe($"{PortLabel(valueInput)} is missing.");
}
}
}
}

View File

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

View File

@@ -0,0 +1,7 @@
namespace Unity.VisualScripting
{
public sealed class UnitAnalysis : GraphElementAnalysis
{
public bool isEntered { get; set; }
}
}

View File

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

View File

@@ -0,0 +1,10 @@
namespace Unity.VisualScripting
{
public sealed class UnitDescription : GraphElementDescription
{
public string shortTitle { get; set; }
public string surtitle { get; set; }
public string subtitle { get; set; }
public EditorTexture[] icons { get; set; }
}
}

View File

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

View File

@@ -0,0 +1,391 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityObject = UnityEngine.Object;
namespace Unity.VisualScripting
{
[Descriptor(typeof(IUnit))]
public class UnitDescriptor<TUnit> : Descriptor<TUnit, UnitDescription>, IUnitDescriptor
where TUnit : class, IUnit
{
public UnitDescriptor(TUnit target) : base(target)
{
unitType = unit.GetType();
}
protected Type unitType { get; }
public TUnit unit => target;
IUnit IUnitDescriptor.unit => unit;
private enum State
{
Defined,
NotDefined,
FailedToDefine
}
private State state
{
get
{
if (unit.isDefined)
{
return State.Defined;
}
else if (unit.failedToDefine)
{
return State.FailedToDefine;
}
else
{
return State.NotDefined;
}
}
}
#region Reflected Description
static UnitDescriptor()
{
XmlDocumentation.loadComplete += FreeReflectedDescriptions;
}
public static void FreeReflectedDescriptions()
{
reflectedDescriptions.Clear();
reflectedInputDescriptions.Clear();
reflectedOutputDescriptions.Clear();
}
protected UnitDescription reflectedDescription
{
get
{
if (!reflectedDescriptions.TryGetValue(unitType, out var reflectedDescription))
{
reflectedDescription = FetchReflectedDescription(unitType);
reflectedDescriptions.Add(unitType, reflectedDescription);
}
return reflectedDescription;
}
}
protected UnitPortDescription ReflectedPortDescription(IUnitPort port)
{
if (port is IUnitInvalidPort)
{
return null;
}
if (port is IUnitInputPort)
{
if (!reflectedInputDescriptions.TryGetValue(unitType, out var _reflectedInputDescriptions))
{
_reflectedInputDescriptions = FetchReflectedPortDescriptions<IUnitInputPort>(unitType);
reflectedInputDescriptions.Add(unitType, _reflectedInputDescriptions);
}
if (_reflectedInputDescriptions.TryGetValue(port.key, out var portDescription))
{
return portDescription;
}
}
else if (port is IUnitOutputPort)
{
if (!reflectedOutputDescriptions.TryGetValue(unitType, out var _reflectedOutputDescriptions))
{
_reflectedOutputDescriptions = FetchReflectedPortDescriptions<IUnitOutputPort>(unitType);
reflectedOutputDescriptions.Add(unitType, _reflectedOutputDescriptions);
}
if (_reflectedOutputDescriptions.TryGetValue(port.key, out var portDescription))
{
return portDescription;
}
}
return null;
}
private static readonly Dictionary<Type, UnitDescription> reflectedDescriptions = new Dictionary<Type, UnitDescription>();
private static readonly Dictionary<Type, Dictionary<string, UnitPortDescription>> reflectedInputDescriptions = new Dictionary<Type, Dictionary<string, UnitPortDescription>>();
private static readonly Dictionary<Type, Dictionary<string, UnitPortDescription>> reflectedOutputDescriptions = new Dictionary<Type, Dictionary<string, UnitPortDescription>>();
private static UnitDescription FetchReflectedDescription(Type unitType)
{
var oldName = BoltFlowNameUtility.UnitPreviousTitle(unitType);
var prefix = string.IsNullOrEmpty(oldName) ? string.Empty : $"(Previously named {oldName}) ";
return new UnitDescription()
{
title = BoltFlowNameUtility.UnitTitle(unitType, false, true),
shortTitle = BoltFlowNameUtility.UnitTitle(unitType, true, true),
surtitle = unitType.GetAttribute<UnitSurtitleAttribute>()?.surtitle,
subtitle = unitType.GetAttribute<UnitSubtitleAttribute>()?.subtitle,
summary = prefix + unitType.Summary()
};
}
private static Dictionary<string, UnitPortDescription> FetchReflectedPortDescriptions<T>(Type unitType) where T : IUnitPort
{
var descriptions = new Dictionary<string, UnitPortDescription>();
foreach (var portMember in unitType.GetMembers().Where(member => typeof(T).IsAssignableFrom(member.GetAccessorType())))
{
var key = portMember.GetAttribute<PortKeyAttribute>()?.key ?? portMember.Name;
if (descriptions.ContainsKey(key))
{
Debug.LogWarning("Duplicate reflected port description for: " + key);
continue;
}
descriptions.Add(key, FetchReflectedPortDescription(portMember));
}
return descriptions;
}
private static UnitPortDescription FetchReflectedPortDescription(MemberInfo portMember)
{
return new UnitPortDescription()
{
label = portMember.GetAttribute<PortLabelAttribute>()?.label ?? portMember.HumanName(),
showLabel = !(portMember.HasAttribute<PortLabelHiddenAttribute>() || (portMember.GetAttribute<PortLabelAttribute>()?.hidden ?? false)),
summary = portMember.Summary(),
getMetadata = (unitMetadata) => unitMetadata[portMember.Name]
};
}
#endregion
#region Description
[Assigns]
public sealed override string Title()
{
switch (state)
{
case State.Defined: return DefinedTitle();
case State.NotDefined: return DefaultTitle();
case State.FailedToDefine: return ErrorTitle(unit.definitionException);
default: throw new UnexpectedEnumValueException<State>(state);
}
}
[Assigns]
public string ShortTitle()
{
switch (state)
{
case State.Defined: return DefinedShortTitle();
case State.NotDefined: return DefaultShortTitle();
case State.FailedToDefine: return ErrorShortTitle(unit.definitionException);
default: throw new UnexpectedEnumValueException<State>(state);
}
}
[Assigns]
public string Surtitle()
{
switch (state)
{
case State.Defined: return DefinedSurtitle();
case State.NotDefined: return DefaultSurtitle();
case State.FailedToDefine: return ErrorSurtitle(unit.definitionException);
default: throw new UnexpectedEnumValueException<State>(state);
}
}
[Assigns]
public string Subtitle()
{
switch (state)
{
case State.Defined: return DefinedSubtitle();
case State.NotDefined: return DefaultSubtitle();
case State.FailedToDefine: return ErrorSubtitle(unit.definitionException);
default: throw new UnexpectedEnumValueException<State>(state);
}
}
[Assigns]
public sealed override string Summary()
{
switch (state)
{
case State.Defined: return DefinedSummary();
case State.NotDefined: return DefaultSummary();
case State.FailedToDefine: return ErrorSummary(unit.definitionException);
default: throw new UnexpectedEnumValueException<State>(state);
}
}
[Assigns]
[RequiresUnityAPI]
public sealed override EditorTexture Icon()
{
switch (state)
{
case State.Defined: return DefinedIcon();
case State.NotDefined: return DefaultIcon();
case State.FailedToDefine: return ErrorIcon(unit.definitionException);
default: throw new UnexpectedEnumValueException<State>(state);
}
}
[Assigns]
[RequiresUnityAPI]
public IEnumerable<EditorTexture> Icons()
{
switch (state)
{
case State.Defined: return DefinedIcons();
case State.NotDefined: return DefaultIcons();
case State.FailedToDefine: return ErrorIcons(unit.definitionException);
default: throw new UnexpectedEnumValueException<State>(state);
}
}
protected virtual string DefinedTitle()
{
return reflectedDescription.title;
}
protected virtual string DefaultTitle()
{
return reflectedDescription.title;
}
protected virtual string ErrorTitle(Exception exception)
{
return reflectedDescription.title;
}
protected virtual string DefinedShortTitle()
{
return reflectedDescription.shortTitle;
}
protected virtual string DefaultShortTitle()
{
return reflectedDescription.shortTitle;
}
protected virtual string ErrorShortTitle(Exception exception)
{
return ErrorTitle(exception);
}
protected virtual string DefinedSurtitle()
{
return reflectedDescription.surtitle;
}
protected virtual string DefaultSurtitle()
{
return reflectedDescription.surtitle;
}
protected virtual string ErrorSurtitle(Exception exception)
{
return null;
}
protected virtual string DefinedSubtitle()
{
return reflectedDescription.subtitle;
}
protected virtual string DefaultSubtitle()
{
return reflectedDescription.subtitle;
}
protected virtual string ErrorSubtitle(Exception exception)
{
return null;
}
protected virtual string DefinedSummary()
{
return reflectedDescription.summary;
}
protected virtual string DefaultSummary()
{
return reflectedDescription.summary;
}
protected virtual string ErrorSummary(Exception exception)
{
return $"This node failed to define.\n\n{exception.DisplayName()}: {exception.Message}";
}
protected virtual EditorTexture DefinedIcon()
{
return unit.GetType().Icon();
}
protected virtual EditorTexture DefaultIcon()
{
return unit.GetType().Icon();
}
protected virtual EditorTexture ErrorIcon(Exception exception)
{
return BoltCore.Icons.errorState;
}
protected virtual IEnumerable<EditorTexture> DefinedIcons()
{
return Enumerable.Empty<EditorTexture>();
}
protected virtual IEnumerable<EditorTexture> DefaultIcons()
{
return Enumerable.Empty<EditorTexture>();
}
protected virtual IEnumerable<EditorTexture> ErrorIcons(Exception exception)
{
return Enumerable.Empty<EditorTexture>();
}
public void DescribePort(IUnitPort port, UnitPortDescription description)
{
description.getMetadata = (unitMetadata) => unitMetadata.StaticObject(port);
// Only defined nodes can have specific ports
if (state == State.Defined)
{
DefinedPort(port, description);
}
}
protected virtual void DefinedPort(IUnitPort port, UnitPortDescription description)
{
var reflectedPortDescription = ReflectedPortDescription(port);
if (reflectedPortDescription != null)
{
description.CopyFrom(reflectedPortDescription);
}
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,79 @@
using System;
namespace Unity.VisualScripting
{
public sealed class UnitPortDescription : IDescription
{
private string _label;
private bool _isLabelVisible = true;
internal IUnitPort portType;
public EditorTexture icon
{
get
{
if (_icon == null || !_icon.IsValid())
{
_icon = GetIcon(portType);
}
return _icon;
}
set => _icon = value;
}
private EditorTexture _icon;
public string fallbackLabel { get; set; }
public string label
{
get => _label ?? fallbackLabel;
set => _label = value;
}
public bool showLabel
{
get => !BoltFlow.Configuration.hidePortLabels || _isLabelVisible;
set => _isLabelVisible = value;
}
string IDescription.title => label;
public string summary { get; set; }
public Func<Metadata, Metadata> getMetadata { get; set; }
public void CopyFrom(UnitPortDescription other)
{
_label = other._label;
_isLabelVisible = other._isLabelVisible;
summary = other.summary;
portType = other.portType ?? portType;
getMetadata = other.getMetadata ?? getMetadata;
}
private static EditorTexture GetIcon(IUnitPort portType)
{
if (portType is IUnitControlPort)
{
return typeof(Flow).Icon();
}
else if (portType is IUnitValuePort)
{
return Icons.Type(((IUnitValuePort)portType).type);
}
else if (portType is IUnitInvalidPort)
{
return BoltCore.Resources.icons.errorState;
}
else
{
// throw new NotSupportedException();
return null;
}
}
}
}

View File

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

View File

@@ -0,0 +1,44 @@
using System;
namespace Unity.VisualScripting
{
[Descriptor(typeof(IUnitPort))]
public sealed class UnitPortDescriptor : IDescriptor
{
public UnitPortDescriptor(IUnitPort target)
{
Ensure.That(nameof(target)).IsNotNull(target);
this.target = target;
description.portType = target;
}
public IUnitPort target { get; }
object IDescriptor.target => target;
public UnitPortDescription description { get; private set; } = new UnitPortDescription();
IDescription IDescriptor.description => description;
public bool isDirty { get; set; } = true;
public void Validate()
{
if (isDirty)
{
isDirty = false;
description.fallbackLabel = target.key.Filter(symbols: false, punctuation: false).Prettify();
description.portType = target;
target.unit?.Descriptor<IUnitDescriptor>().DescribePort(target, description);
// No DescriptionAssignment is run, so we'll just always assume that the description changes.
DescriptorProvider.instance.TriggerDescriptionChange(target);
}
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,25 @@
namespace Unity.VisualScripting
{
[Descriptor(typeof(CustomEvent))]
public class CustomEventDescriptor : EventUnitDescriptor<CustomEvent>
{
public CustomEventDescriptor(CustomEvent @event) : base(@event) { }
protected override string DefinedSubtitle()
{
return null;
}
protected override void DefinedPort(IUnitPort port, UnitPortDescription description)
{
base.DefinedPort(port, description);
var index = unit.argumentPorts.IndexOf(port as ValueOutput);
if (index >= 0)
{
description.label = "Arg. " + index;
}
}
}
}

View File

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

View File

@@ -0,0 +1,24 @@
using System.Collections.Generic;
namespace Unity.VisualScripting
{
[Descriptor(typeof(IEventUnit))]
public class EventUnitDescriptor<TEvent> : UnitDescriptor<TEvent>
where TEvent : class, IEventUnit
{
public EventUnitDescriptor(TEvent @event) : base(@event) { }
protected override string DefinedSubtitle()
{
return "Event";
}
protected override IEnumerable<EditorTexture> DefinedIcons()
{
if (unit.coroutine)
{
yield return BoltFlow.Icons.coroutine;
}
}
}
}

View File

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

View File

@@ -0,0 +1,10 @@
namespace Unity.VisualScripting
{
[Widget(typeof(IEventUnit))]
public sealed class EventUnitWidget : UnitWidget<IEventUnit>
{
public EventUnitWidget(FlowCanvas canvas, IEventUnit unit) : base(canvas, unit) { }
protected override NodeColorMix baseColor => NodeColor.Green;
}
}

View File

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

View File

@@ -0,0 +1,13 @@
using UnityEditor;
namespace Unity.VisualScripting
{
[CustomEditor(typeof(GlobalMessageListener), true)]
public class GlobalMessageListenerEditor : Editor
{
public override void OnInspectorGUI()
{
EditorGUILayout.HelpBox("This component is automatically added to relay Unity messages to Visual Scripting.", MessageType.Info);
}
}
}

View File

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

View File

@@ -0,0 +1,13 @@
using UnityEditor;
namespace Unity.VisualScripting
{
[CustomEditor(typeof(MessageListener), true)]
public class MessageListenerEditor : Editor
{
public override void OnInspectorGUI()
{
EditorGUILayout.HelpBox("This component is automatically added to relay Unity messages to Visual Scripting.", MessageType.Info);
}
}
}

View File

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

View File

@@ -0,0 +1,20 @@
namespace Unity.VisualScripting
{
[Descriptor(typeof(TriggerCustomEvent))]
public class TriggerCustomEventDescriptor : UnitDescriptor<TriggerCustomEvent>
{
public TriggerCustomEventDescriptor(TriggerCustomEvent trigger) : base(trigger) { }
protected override void DefinedPort(IUnitPort port, UnitPortDescription description)
{
base.DefinedPort(port, description);
var index = unit.arguments.IndexOf(port as ValueInput);
if (index >= 0)
{
description.label = "Arg. " + index;
}
}
}
}

View File

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

View File

@@ -0,0 +1,495 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityObject = UnityEngine.Object;
namespace Unity.VisualScripting
{
[Canvas(typeof(FlowGraph))]
public sealed class FlowCanvas : VisualScriptingCanvas<FlowGraph>
{
public FlowCanvas(FlowGraph graph) : base(graph) { }
#region Clipboard
public override void ShrinkCopyGroup(HashSet<IGraphElement> copyGroup)
{
copyGroup.RemoveWhere(element =>
{
if (element is IUnitConnection)
{
var connection = (IUnitConnection)element;
if (!copyGroup.Contains(connection.source.unit) ||
!copyGroup.Contains(connection.destination.unit))
{
return true;
}
}
return false;
});
}
#endregion
#region Window
public override void OnToolbarGUI()
{
showRelations = GUILayout.Toggle(showRelations, "Relations", LudiqStyles.toolbarButton);
EditorGUI.BeginChangeCheck();
BoltFlow.Configuration.showConnectionValues = GUILayout.Toggle(BoltFlow.Configuration.showConnectionValues, "Values", LudiqStyles.toolbarButton);
BoltCore.Configuration.dimInactiveNodes = GUILayout.Toggle(BoltCore.Configuration.dimInactiveNodes, "Dim", LudiqStyles.toolbarButton);
if (EditorGUI.EndChangeCheck())
{
BoltFlow.Configuration.Save();
BoltCore.Configuration.Save();
}
base.OnToolbarGUI();
}
#endregion
#region View
protected override bool shouldEdgePan => base.shouldEdgePan || isCreatingConnection;
public const float inspectorZoomThreshold = 0.7f;
#endregion
#region Lifecycle
public override void Close()
{
base.Close();
CancelConnection();
}
protected override void HandleHighPriorityInput()
{
if (isCreatingConnection)
{
if (e.IsMouseDown(MouseButton.Left))
{
connectionEnd = mousePosition;
NewUnitContextual();
e.Use();
}
else if (e.IsFree(EventType.KeyDown) && e.keyCode == KeyCode.Escape)
{
CancelConnection();
e.Use();
}
}
base.HandleHighPriorityInput();
}
private void CompleteContextualConnection(IUnitPort source, IUnitPort destination)
{
source.ValidlyConnectTo(destination);
Cache();
var unitPosition = this.Widget<IUnitWidget>(destination.unit).position.position;
var portPosition = this.Widget<IUnitPortWidget>(destination).handlePosition.center.PixelPerfect();
var offset = portPosition - unitPosition;
destination.unit.position -= offset;
this.Widget(destination.unit).Reposition();
connectionSource = null;
GUI.changed = true;
}
public void NewUnitContextual()
{
var filter = UnitOptionFilter.Any;
filter.GraphHashCode = graph.GetHashCode();
if (connectionSource is ValueInput)
{
var valueInput = (ValueInput)connectionSource;
filter.CompatibleOutputType = valueInput.type;
filter.Expose = false;
filter.NoConnection = false;
NewUnit(mousePosition, GetNewUnitOptions(filter), (unit) => CompleteContextualConnection(valueInput, unit.CompatibleValueOutput(valueInput.type)));
}
else if (connectionSource is ValueOutput)
{
var valueOutput = (ValueOutput)connectionSource;
filter.CompatibleInputType = valueOutput.type;
filter.NoConnection = false;
NewUnit(mousePosition, GetNewUnitOptions(filter), (unit) => CompleteContextualConnection(valueOutput, unit.CompatibleValueInput(valueOutput.type)));
}
else if (connectionSource is ControlInput)
{
var controlInput = (ControlInput)connectionSource;
filter.NoControlOutput = false;
filter.NoConnection = false;
NewUnit(mousePosition, GetNewUnitOptions(filter), (unit) => CompleteContextualConnection(controlInput, unit.controlOutputs.First()));
}
else if (connectionSource is ControlOutput)
{
var controlOutput = (ControlOutput)connectionSource;
filter.NoControlInput = false;
filter.NoConnection = false;
NewUnit(mousePosition, GetNewUnitOptions(filter), (unit) => CompleteContextualConnection(controlOutput, unit.controlInputs.First()));
}
}
#endregion
#region Context
protected override void OnContext()
{
if (isCreatingConnection)
{
CancelConnection();
}
else
{
// Checking for Alt seems to lose focus, for some reason maybe
// unrelated to Bolt. Shift or other modifiers seem to work though.
if (base.GetContextOptions().Any() && (!BoltFlow.Configuration.skipContextMenu || e.shift))
{
base.OnContext();
}
else
{
NewUnit(mousePosition);
}
}
}
protected override IEnumerable<DropdownOption> GetContextOptions()
{
yield return new DropdownOption((Action<Vector2>)(NewUnit), "Add Node...");
yield return new DropdownOption((Action<Vector2>)(NewSticky), "Create Sticky Note");
foreach (var baseOption in base.GetContextOptions())
{
yield return baseOption;
}
}
public void AddUnit(IUnit unit, Vector2 position)
{
UndoUtility.RecordEditedObject("Create Node");
unit.guid = Guid.NewGuid();
unit.position = position.PixelPerfect();
graph.units.Add(unit);
selection.Select(unit);
GUI.changed = true;
}
private UnitOptionTree GetNewUnitOptions(UnitOptionFilter filter)
{
var options = new UnitOptionTree(new GUIContent("Node"));
options.filter = filter;
options.reference = reference;
if (filter.CompatibleOutputType == typeof(object))
{
options.surfaceCommonTypeLiterals = true;
}
return options;
}
private void NewSticky(Vector2 position)
{
UndoUtility.RecordEditedObject("Create Sticky Note");
var stickyNote = new StickyNote() { position = new Rect(position, new Vector2(100, 100)) };
graph.elements.Add(stickyNote);
selection.Select(stickyNote);
GUI.changed = true;
}
private void NewUnit(Vector2 position)
{
var filter = UnitOptionFilter.Any;
filter.GraphHashCode = graph.GetHashCode();
NewUnit(position, GetNewUnitOptions(filter));
}
private void NewUnit(Vector2 unitPosition, UnitOptionTree options, Action<IUnit> then = null)
{
delayCall += () =>
{
var activatorPosition = new Rect(e.mousePosition, new Vector2(200, 1));
var context = this.context;
LudiqGUI.FuzzyDropdown
(
activatorPosition,
options,
null,
delegate (object _option)
{
context.BeginEdit();
if (_option is IUnitOption)
{
var option = (IUnitOption)_option;
var unit = option.InstantiateUnit();
AddUnit(unit, unitPosition);
option.PreconfigureUnit(unit);
then?.Invoke(unit);
GUI.changed = true;
}
else
{
if ((Type)_option == typeof(StickyNote))
{
NewSticky(unitPosition);
}
}
context.EndEdit();
}
);
};
}
#endregion
#region Drag & Drop
private bool CanDetermineDraggedInput(UnityObject uo)
{
if (uo.IsSceneBound())
{
if (reference.self == uo.GameObject())
{
// Because we'll be able to assign it to Self
return true;
}
if (reference.serializedObject.IsSceneBound())
{
// Because we'll be able to use a direct scene reference
return true;
}
return false;
}
else
{
return true;
}
}
public override bool AcceptsDragAndDrop()
{
if (DragAndDropUtility.Is<ScriptGraphAsset>())
{
return FlowDragAndDropUtility.AcceptsScript(graph);
}
return DragAndDropUtility.Is<UnityObject>() && !DragAndDropUtility.Is<IMacro>() && CanDetermineDraggedInput(DragAndDropUtility.Get<UnityObject>())
|| EditorVariablesUtility.isDraggingVariable;
}
public override void PerformDragAndDrop()
{
if (DragAndDropUtility.Is<ScriptGraphAsset>())
{
var flowMacro = DragAndDropUtility.Get<ScriptGraphAsset>();
var superUnit = new SubgraphUnit(flowMacro);
AddUnit(superUnit, DragAndDropUtility.position);
}
else if (DragAndDropUtility.Is<UnityObject>())
{
var uo = DragAndDropUtility.Get<UnityObject>();
var type = uo.GetType();
var filter = UnitOptionFilter.Any;
filter.Literals = false;
filter.Expose = false;
var options = GetNewUnitOptions(filter);
var root = new List<object>();
if (!uo.IsSceneBound() || reference.serializedObject.IsSceneBound())
{
if (uo == reference.self)
{
root.Add(new UnitOption<This>(new This()));
}
root.Add(new LiteralOption(new Literal(type, uo)));
}
if (uo is MonoScript script)
{
var scriptType = script.GetClass();
if (scriptType != null)
{
root.Add(scriptType);
}
}
else
{
root.Add(type);
}
if (uo is GameObject)
{
root.AddRange(uo.GetComponents<Component>().Select(c => c.GetType()));
}
options.rootOverride = root.ToArray();
NewUnit(DragAndDropUtility.position, options, (unit) =>
{
// Try to assign a correct input
var compatibleInput = unit.CompatibleValueInput(type);
if (compatibleInput == null)
{
return;
}
if (uo.IsSceneBound())
{
if (reference.self == uo.GameObject())
{
// The component is owned by the same game object as the graph.
if (compatibleInput.nullMeansSelf)
{
compatibleInput.SetDefaultValue(null);
}
else
{
var self = new This();
self.position = unit.position + new Vector2(-150, 19);
graph.units.Add(self);
self.self.ConnectToValid(compatibleInput);
}
}
else if (reference.serializedObject.IsSceneBound())
{
// The component is from another object from the same scene
compatibleInput.SetDefaultValue(uo.ConvertTo(compatibleInput.type));
}
else
{
throw new NotSupportedException("Cannot determine compatible input from dragged Unity object.");
}
}
else
{
compatibleInput.SetDefaultValue(uo.ConvertTo(compatibleInput.type));
}
});
}
else if (EditorVariablesUtility.isDraggingVariable)
{
var kind = EditorVariablesUtility.kind;
var declaration = EditorVariablesUtility.declaration;
UnifiedVariableUnit unit;
if (e.alt)
{
unit = new SetVariable();
}
else if (e.shift)
{
unit = new IsVariableDefined();
}
else
{
unit = new GetVariable();
}
unit.kind = kind;
AddUnit(unit, DragAndDropUtility.position);
unit.name.SetDefaultValue(declaration.name);
}
}
public override void DrawDragAndDropPreview()
{
if (DragAndDropUtility.Is<ScriptGraphAsset>())
{
GraphGUI.DrawDragAndDropPreviewLabel(DragAndDropUtility.offsetedPosition, DragAndDropUtility.Get<ScriptGraphAsset>().name, typeof(ScriptGraphAsset).Icon());
}
else if (DragAndDropUtility.Is<GameObject>())
{
var gameObject = DragAndDropUtility.Get<GameObject>();
GraphGUI.DrawDragAndDropPreviewLabel(DragAndDropUtility.offsetedPosition, gameObject.name + "...", gameObject.Icon());
}
else if (DragAndDropUtility.Is<UnityObject>())
{
var obj = DragAndDropUtility.Get<UnityObject>();
var type = obj.GetType();
GraphGUI.DrawDragAndDropPreviewLabel(DragAndDropUtility.offsetedPosition, type.HumanName() + "...", type.Icon());
}
else if (EditorVariablesUtility.isDraggingVariable)
{
var kind = EditorVariablesUtility.kind;
var name = EditorVariablesUtility.declaration.name;
string label;
if (e.alt)
{
label = $"Set {name}";
}
else if (e.shift)
{
label = $"Check if {name} is defined";
}
else
{
label = $"Get {name}";
}
GraphGUI.DrawDragAndDropPreviewLabel(DragAndDropUtility.offsetedPosition, label, BoltCore.Icons.VariableKind(kind));
}
}
#endregion
#region Drawing
public bool showRelations { get; set; }
#endregion
#region Connection Creation
public IUnitPort connectionSource { get; set; }
public Vector2 connectionEnd { get; set; }
public bool isCreatingConnection => connectionSource != null &&
connectionSource.unit != null; // Make sure the port didn't get destroyed: https://support.ludiq.io/communities/5/topics/4034-x
public void CancelConnection()
{
connectionSource = null;
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,13 @@
using UnityEditor;
namespace Unity.VisualScripting
{
public static class FlowDragAndDropUtility
{
public static bool AcceptsScript(IGraph graph)
{
// Can't drag a graph into itself
return DragAndDrop.objectReferences[0] is ScriptGraphAsset graphAsset && graph != graphAsset.graph;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7ef5652404544c8cab2b662e003e8766
timeCreated: 1612898755

View File

@@ -0,0 +1,26 @@
namespace Unity.VisualScripting
{
[InitializeAfterPlugins]
public static class FlowEditorBindings
{
static FlowEditorBindings()
{
Flow.isInspectedBinding = IsInspected;
}
private static bool IsInspected(GraphPointer pointer)
{
Ensure.That(nameof(pointer)).IsNotNull(pointer);
foreach (var graphWindow in GraphWindow.tabsNoAlloc)
{
if (graphWindow.reference?.InstanceEquals(pointer) ?? false)
{
return true;
}
}
return false;
}
}
}

View File

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

View File

@@ -0,0 +1,18 @@
using System.Collections.Generic;
namespace Unity.VisualScripting
{
[GraphContext(typeof(FlowGraph))]
public class FlowGraphContext : GraphContext<FlowGraph, FlowCanvas>
{
public FlowGraphContext(GraphReference reference) : base(reference) { }
public override string windowTitle => "Script Graph";
protected override IEnumerable<ISidebarPanelContent> SidebarPanels()
{
yield return new GraphInspectorPanel(this);
yield return new VariablesPanel(this);
}
}
}

View File

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

View File

@@ -0,0 +1,116 @@
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace Unity.VisualScripting
{
[Editor(typeof(FlowGraph))]
public class FlowGraphEditor : GraphEditor
{
public FlowGraphEditor(Metadata metadata) : base(metadata) { }
private new FlowGraph graph => (FlowGraph)base.graph;
private Metadata controlInputDefinitionsMetadata => metadata[nameof(FlowGraph.controlInputDefinitions)];
private Metadata controlOutputDefinitionsMetadata => metadata[nameof(FlowGraph.controlOutputDefinitions)];
private Metadata valueInputDefinitionsMetadata => metadata[nameof(FlowGraph.valueInputDefinitions)];
private Metadata valueOutputDefinitionsMetadata => metadata[nameof(FlowGraph.valueOutputDefinitions)];
private IEnumerable<Warning> warnings => UnitPortDefinitionUtility.Warnings((FlowGraph)metadata.value);
protected override float GetHeight(float width, GUIContent label)
{
var height = 0f;
height += GetHeaderHeight(width);
height += GetControlInputDefinitionsHeight(width);
height += EditorGUIUtility.standardVerticalSpacing;
height += GetControlOutputDefinitionsHeight(width);
height += EditorGUIUtility.standardVerticalSpacing;
height += GetValueInputDefinitionsHeight(width);
height += EditorGUIUtility.standardVerticalSpacing;
height += GetValueOutputDefinitionsHeight(width);
if (warnings.Any())
{
height += EditorGUIUtility.standardVerticalSpacing;
foreach (var warning in warnings)
{
height += warning.GetHeight(width);
}
}
return height;
}
protected override void OnGUI(Rect position, GUIContent label)
{
BeginLabeledBlock(metadata, position, label);
OnHeaderGUI(position);
EditorGUI.BeginChangeCheck();
LudiqGUI.Inspector(controlInputDefinitionsMetadata, position.VerticalSection(ref y, GetControlInputDefinitionsHeight(position.width)));
y += EditorGUIUtility.standardVerticalSpacing;
LudiqGUI.Inspector(controlOutputDefinitionsMetadata, position.VerticalSection(ref y, GetControlOutputDefinitionsHeight(position.width)));
y += EditorGUIUtility.standardVerticalSpacing;
LudiqGUI.Inspector(valueInputDefinitionsMetadata, position.VerticalSection(ref y, GetValueInputDefinitionsHeight(position.width)));
y += EditorGUIUtility.standardVerticalSpacing;
LudiqGUI.Inspector(valueOutputDefinitionsMetadata, position.VerticalSection(ref y, GetValueOutputDefinitionsHeight(position.width)));
if (EditorGUI.EndChangeCheck())
{
graph.PortDefinitionsChanged();
}
if (warnings.Any())
{
y += EditorGUIUtility.standardVerticalSpacing;
foreach (var warning in warnings)
{
y--;
warning.OnGUI(position.VerticalSection(ref y, warning.GetHeight(position.width) + 1));
}
}
EndBlock(metadata);
}
private float GetControlInputDefinitionsHeight(float width)
{
return LudiqGUI.GetInspectorHeight(this, controlInputDefinitionsMetadata, width);
}
private float GetControlOutputDefinitionsHeight(float width)
{
return LudiqGUI.GetInspectorHeight(this, controlOutputDefinitionsMetadata, width);
}
private float GetValueInputDefinitionsHeight(float width)
{
return LudiqGUI.GetInspectorHeight(this, valueInputDefinitionsMetadata, width);
}
private float GetValueOutputDefinitionsHeight(float width)
{
return LudiqGUI.GetInspectorHeight(this, valueOutputDefinitionsMetadata, width);
}
}
}

View File

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

View File

@@ -0,0 +1,61 @@
#if VISUAL_SCRIPT_INTERNAL
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Unity.VisualScripting;
using UnityEditor;
using UnityEngine;
public class FlowGraphUnitUISample : RuntimeFlowGraph
{
[MenuItem("Tools/Visual Scripting/Internal/Create Node UI Samples", priority = LudiqProduct.DeveloperToolsMenuPriority + 403)]
public static void CreateUnitUISamples()
{
(new FlowGraphUnitUISample()).CreateGraphUISample();
}
private void CreateGraphUISample()
{
CreateGraph();
IEnumerable<Type> GetEventUnitTypes() => AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes().Where(t => typeof(IUnit).IsAssignableFrom(t))).Where(t => t.IsClass && !t.IsAbstract);
Vector2 position = Vector2.zero;
int index = 0;
foreach (var unitType in GetEventUnitTypes())
{
try
{
string name = unitType.Assembly.GetName().Name;
string space = unitType.FullName;
var unit = Activator.CreateInstance(name, space);
IUnit b = (IUnit)unit.Unwrap();
b.position = position;
if (index % 10 == 0)
{
position.x = 0;
position.y += 180;
}
position.x += 180;
AddUnit(b, position);
index++;
}
catch (Exception e)
{
Debug.LogException(e);
}
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,8 @@
namespace Unity.VisualScripting
{
[Editor(typeof(ScriptMachine))]
public class FlowMachineEditor : MachineEditor
{
public FlowMachineEditor(Metadata metadata) : base(metadata) { }
}
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,40 @@
namespace Unity.VisualScripting
{
[Descriptor(typeof(CreateStruct))]
public class CreateStructDescriptor : UnitDescriptor<CreateStruct>
{
public CreateStructDescriptor(CreateStruct unit) : base(unit) { }
protected override string DefinedTitle()
{
if (BoltCore.Configuration.humanNaming)
{
return $"Create {unit.type.HumanName()}";
}
else
{
return $"new {unit.type.CSharpName()}";
}
}
protected override string DefinedShortTitle()
{
return BoltCore.Configuration.humanNaming ? "Create" : "new";
}
protected override string DefinedSurtitle()
{
return unit.type.DisplayName();
}
protected override string DefinedSummary()
{
return unit.type.Summary();
}
protected override EditorTexture DefinedIcon()
{
return unit.type.Icon();
}
}
}

View File

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

View File

@@ -0,0 +1,75 @@
using System;
namespace Unity.VisualScripting
{
[FuzzyOption(typeof(CreateStruct))]
public class CreateStructOption : UnitOption<CreateStruct>
{
public CreateStructOption() : base() { }
public CreateStructOption(CreateStruct unit) : base(unit) { }
public Type structType { get; private set; }
protected override void FillFromUnit()
{
structType = unit.type;
base.FillFromUnit();
}
protected override string Label(bool human)
{
if (human)
{
return $"Create {structType.HumanName()} ()";
}
else
{
return $"new {structType.CSharpName()} ()";
}
}
protected override string Haystack(bool human)
{
if (human)
{
return $"{structType.HumanName()}: Create {structType.HumanName()}";
}
else
{
return $"new {structType.CSharpName()}";
}
}
public override string SearchResultLabel(string query)
{
return base.SearchResultLabel(query) + " ()";
}
protected override int Order()
{
return 0;
}
protected override string FavoriteKey()
{
return $"{structType.FullName}@create";
}
public override void Deserialize(UnitOptionRow row)
{
base.Deserialize(row);
structType = Codebase.DeserializeType(row.tag1);
}
public override UnitOptionRow Serialize()
{
var row = base.Serialize();
row.tag1 = Codebase.SerializeType(structType);
return row;
}
}
}

View File

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

View File

@@ -0,0 +1,39 @@
namespace Unity.VisualScripting
{
[Descriptor(typeof(Expose))]
public class ExposeDescriptor : UnitDescriptor<Expose>
{
public ExposeDescriptor(Expose unit) : base(unit) { }
protected override string DefinedTitle()
{
return $"Expose {unit.type.DisplayName()}";
}
protected override string DefinedSurtitle()
{
return "Expose";
}
protected override string DefinedShortTitle()
{
return unit.type.DisplayName();
}
protected override EditorTexture DefinedIcon()
{
return unit.type.Icon();
}
protected override void DefinedPort(IUnitPort port, UnitPortDescription description)
{
base.DefinedPort(port, description);
if (port is ValueOutput && unit.members.TryGetValue((ValueOutput)port, out Member member))
{
description.label = member.info.HumanName();
description.summary = member.info.Summary();
}
}
}
}

View File

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

View File

@@ -0,0 +1,55 @@
using System;
namespace Unity.VisualScripting
{
[FuzzyOption(typeof(Expose))]
public class ExposeOption : UnitOption<Expose>
{
public ExposeOption() : base() { }
public ExposeOption(Expose unit) : base(unit)
{
sourceScriptGuids = UnitBase.GetScriptGuids(unit.type).ToHashSet();
}
public Type exposedType { get; private set; }
protected override string FavoriteKey()
{
return $"{exposedType.FullName}@expose";
}
protected override string Label(bool human)
{
return $"Expose {unit.type.SelectedName(human)}";
}
protected override bool ShowValueOutputsInFooter()
{
return false;
}
protected override void FillFromUnit()
{
exposedType = unit.type;
base.FillFromUnit();
}
public override void Deserialize(UnitOptionRow row)
{
base.Deserialize(row);
exposedType = Codebase.DeserializeType(row.tag1);
}
public override UnitOptionRow Serialize()
{
var row = base.Serialize();
row.tag1 = Codebase.SerializeType(exposedType);
return row;
}
}
}

View File

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

View File

@@ -0,0 +1,20 @@
namespace Unity.VisualScripting
{
[Descriptor(typeof(GetMember))]
public class GetMemberDescriptor : MemberUnitDescriptor<GetMember>
{
public GetMemberDescriptor(GetMember unit) : base(unit) { }
protected override ActionDirection direction => ActionDirection.Get;
protected override void DefinedPort(IUnitPort port, UnitPortDescription description)
{
base.DefinedPort(port, description);
if (port == unit.value)
{
description.summary = unit.member.info.Summary();
}
}
}
}

View File

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

View File

@@ -0,0 +1,12 @@
namespace Unity.VisualScripting
{
[FuzzyOption(typeof(GetMember))]
public class GetMemberOption : MemberUnitOption<GetMember>
{
public GetMemberOption() : base() { }
public GetMemberOption(GetMember unit) : base(unit) { }
protected override ActionDirection direction => ActionDirection.Get;
}
}

View File

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

View File

@@ -0,0 +1,77 @@
using System.Linq;
namespace Unity.VisualScripting
{
[Descriptor(typeof(InvokeMember))]
public class InvokeMemberDescriptor : MemberUnitDescriptor<InvokeMember>
{
public InvokeMemberDescriptor(InvokeMember unit) : base(unit) { }
protected override ActionDirection direction => ActionDirection.Any;
protected override string DefinedShortTitle()
{
if (member.isConstructor)
{
return BoltCore.Configuration.humanNaming ? "Create" : "new";
}
return base.DefinedShortTitle();
}
protected override void DefinedPort(IUnitPort port, UnitPortDescription description)
{
base.DefinedPort(port, description);
var documentation = member.info.Documentation();
if (port == unit.enter)
{
description.label = "Invoke";
description.summary = "The entry point to invoke the method.";
if (member.isGettable)
{
description.summary += " You can still get the return value without connecting this port.";
}
}
else if (port == unit.exit)
{
description.summary = "The action to call once the method has been invoked.";
}
else if (port == unit.result)
{
if (member.isGettable)
{
description.summary = documentation?.returns;
}
if (unit.supportsChaining && unit.chainable)
{
description.showLabel = true;
}
}
else if (port == unit.targetOutput)
{
if (member.isGettable)
{
description.showLabel = true;
}
}
else if (port is ValueInput && unit.inputParameters.ContainsValue((ValueInput)port))
{
var parameter = member.GetParameterInfos().Single(p => "%" + p.Name == port.key);
description.label = parameter.DisplayName();
description.summary = documentation?.ParameterSummary(parameter);
}
else if (port is ValueOutput && unit.outputParameters.ContainsValue((ValueOutput)port))
{
var parameter = member.GetParameterInfos().Single(p => "&" + p.Name == port.key);
description.label = parameter.DisplayName();
description.summary = documentation?.ParameterSummary(parameter);
}
}
}
}

View File

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

View File

@@ -0,0 +1,34 @@
namespace Unity.VisualScripting
{
[FuzzyOption(typeof(InvokeMember))]
public class InvokeMemberOption : MemberUnitOption<InvokeMember>
{
public InvokeMemberOption() : base() { }
public InvokeMemberOption(InvokeMember unit) : base(unit) { }
protected override ActionDirection direction => ActionDirection.Any;
public override string SearchResultLabel(string query)
{
return base.SearchResultLabel(query) + $" ({unit.member.methodBase.DisplayParameterString(unit.member.targetType)})";
}
protected override string Label(bool human)
{
return base.Label(human) + $" ({unit.member.methodBase.SelectedParameterString(unit.member.targetType, human)})";
}
protected override string Haystack(bool human)
{
if (!human && member.isConstructor)
{
return base.Label(human);
}
else
{
return $"{targetType.SelectedName(human)}{(human ? ": " : ".")}{base.Label(human)}";
}
}
}
}

View File

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

View File

@@ -0,0 +1,28 @@
namespace Unity.VisualScripting
{
[Descriptor(typeof(Literal))]
public class LiteralDescriptor : UnitDescriptor<Literal>
{
public LiteralDescriptor(Literal unit) : base(unit) { }
protected override string DefinedTitle()
{
return unit.type.DisplayName() + " Literal";
}
protected override string DefinedShortTitle()
{
return unit.type.DisplayName();
}
protected override string DefinedSummary()
{
return unit.type.Summary();
}
protected override EditorTexture DefinedIcon()
{
return unit.type.Icon();
}
}
}

View File

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

View File

@@ -0,0 +1,55 @@
using System;
using UnityEngine;
namespace Unity.VisualScripting
{
[Inspector(typeof(Literal))]
public sealed class LiteralInspector : Inspector
{
public LiteralInspector(Metadata metadata) : base(metadata) { }
private Metadata typeMetadata => metadata[nameof(Literal.type)];
private Metadata valueMetadata => metadata[nameof(Literal.value)];
private Metadata typedValueMetadata => valueMetadata.Cast((Type)typeMetadata.value);
private bool hasType => typeMetadata.value != null;
protected override float GetHeight(float width, GUIContent label)
{
if (hasType)
{
return LudiqGUI.GetInspectorHeight(this, typedValueMetadata, width, label);
}
else
{
return LudiqGUI.GetInspectorHeight(this, typeMetadata, width, label);
}
}
protected override bool cacheHeight => false;
protected override void OnGUI(Rect position, GUIContent label)
{
if (hasType)
{
LudiqGUI.Inspector(typedValueMetadata, position, label);
}
else
{
LudiqGUI.Inspector(typeMetadata, position, label);
}
}
public override float GetAdaptiveWidth()
{
if (hasType)
{
return typedValueMetadata.Inspector().GetAdaptiveWidth();
}
else
{
return typeMetadata.Inspector().GetAdaptiveWidth();
}
}
}
}

View File

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

View File

@@ -0,0 +1,65 @@
using System;
using UnityObject = UnityEngine.Object;
namespace Unity.VisualScripting
{
[FuzzyOption(typeof(Literal))]
public class LiteralOption : UnitOption<Literal>
{
public LiteralOption() : base() { }
public LiteralOption(Literal unit) : base(unit)
{
sourceScriptGuids = UnitBase.GetScriptGuids(unit.type).ToHashSet();
}
public Type literalType { get; private set; }
protected override void FillFromUnit()
{
literalType = unit.type;
base.FillFromUnit();
}
protected override string Label(bool human)
{
if (unit.value is UnityObject uo && !uo.IsUnityNull())
{
return UnityAPI.Await(() => uo.name);
}
return unit.type.SelectedName(human) + " Literal";
}
protected override EditorTexture Icon()
{
if (unit.value is UnityObject uo && !uo.IsUnityNull())
{
return uo.Icon();
}
return base.Icon();
}
protected override string FavoriteKey()
{
return $"{literalType.FullName}@literal";
}
public override void Deserialize(UnitOptionRow row)
{
base.Deserialize(row);
literalType = Codebase.DeserializeType(row.tag1);
}
public override UnitOptionRow Serialize()
{
var row = base.Serialize();
row.tag1 = Codebase.SerializeType(literalType);
return row;
}
}
}

View File

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

View File

@@ -0,0 +1,56 @@
using UnityEditor;
using UnityEngine;
namespace Unity.VisualScripting
{
[Widget(typeof(Literal))]
public sealed class LiteralWidget : UnitWidget<Literal>
{
public LiteralWidget(FlowCanvas canvas, Literal unit) : base(canvas, unit) { }
protected override bool showHeaderAddon => unit.isDefined;
public override bool foregroundRequiresInput => true;
protected override float GetHeaderAddonWidth()
{
var adaptiveWidthAttribute = unit.type.GetAttribute<InspectorAdaptiveWidthAttribute>();
return Mathf.Min(metadata.Inspector().GetAdaptiveWidth(), adaptiveWidthAttribute?.width ?? Styles.maxSettingsWidth);
}
protected override float GetHeaderAddonHeight(float width)
{
return LudiqGUI.GetInspectorHeight(null, metadata, width, GUIContent.none);
}
public override void BeforeFrame()
{
base.BeforeFrame();
if (showHeaderAddon &&
GetHeaderAddonWidth() != headerAddonPosition.width ||
GetHeaderAddonHeight(headerAddonPosition.width) != headerAddonPosition.height)
{
Reposition();
}
}
protected override void DrawHeaderAddon()
{
using (LudiqGUIUtility.labelWidth.Override(75)) // For reflected inspectors / custom property drawers
using (Inspector.adaptiveWidth.Override(true))
{
EditorGUI.BeginChangeCheck();
LudiqGUI.Inspector(metadata, headerAddonPosition, GUIContent.none);
if (EditorGUI.EndChangeCheck())
{
unit.EnsureDefined();
Reposition();
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Unity.VisualScripting
{
[Analyser(typeof(MemberUnit))]
public class MemberUnitAnalyser : UnitAnalyser<MemberUnit>
{
public MemberUnitAnalyser(GraphReference reference, MemberUnit target) : base(reference, target) { }
protected override IEnumerable<Warning> Warnings()
{
foreach (var baseWarning in base.Warnings())
{
yield return baseWarning;
}
if (target.member != null && target.member.isReflected)
{
var obsoleteAttribute = target.member.info.GetAttribute<ObsoleteAttribute>();
if (obsoleteAttribute != null)
{
if (obsoleteAttribute.Message != null)
{
Debug.LogWarning($"\"{target.member.name}\" node member is deprecated: {obsoleteAttribute.Message}");
yield return Warning.Caution("Deprecated: " + obsoleteAttribute.Message);
}
else
{
Debug.LogWarning($"\"{target.member.name}\" node member is deprecated.");
yield return Warning.Caution($"Member {target.member.name} is deprecated.");
}
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,106 @@
using System;
using System.Linq;
using Unity.VisualScripting.AssemblyQualifiedNameParser;
namespace Unity.VisualScripting
{
public abstract class MemberUnitDescriptor<TMemberUnit> : UnitDescriptor<TMemberUnit> where TMemberUnit : MemberUnit
{
protected MemberUnitDescriptor(TMemberUnit unit) : base(unit)
{
}
protected Member member => unit.member;
protected abstract ActionDirection direction { get; }
private string Name()
{
return unit.member.info.DisplayName(direction);
}
protected override string DefinedTitle()
{
return Name();
}
protected override string ErrorSurtitle(Exception exception)
{
if (member?.targetType != null)
{
return member.targetType.DisplayName();
}
else if (member?.targetTypeName != null)
{
try
{
var parsedName = new ParsedAssemblyQualifiedName(member.targetTypeName).TypeName.Split('.').Last();
if (BoltCore.Configuration.humanNaming)
{
return parsedName.Prettify();
}
else
{
return parsedName;
}
}
catch
{
return "Malformed Type Name";
}
}
else
{
return "Missing Type";
}
}
protected override string ErrorTitle(Exception exception)
{
if (!string.IsNullOrEmpty(member?.name))
{
if (BoltCore.Configuration.humanNaming)
{
return member.name.Prettify();
}
else
{
return member.name;
}
}
return base.ErrorTitle(exception);
}
protected override string DefinedShortTitle()
{
return Name();
}
protected override EditorTexture DefinedIcon()
{
return member.targetType.Icon();
}
protected override EditorTexture ErrorIcon(Exception exception)
{
if (member.targetType != null)
{
return member.targetType.Icon();
}
return base.ErrorIcon(exception);
}
protected override string DefinedSurtitle()
{
return member.targetType.DisplayName();
}
protected override string DefinedSummary()
{
return member.info.Summary();
}
}
}

View File

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

View File

@@ -0,0 +1,130 @@
using System;
using UnityEngine;
namespace Unity.VisualScripting
{
public interface IMemberUnitOption : IUnitOption
{
Type targetType { get; }
Member member { get; }
Member pseudoDeclarer { get; }
}
public abstract class MemberUnitOption<TMemberUnit> : UnitOption<TMemberUnit>, IMemberUnitOption where TMemberUnit : MemberUnit
{
protected MemberUnitOption() : base() { }
protected MemberUnitOption(TMemberUnit unit) : base(unit)
{
sourceScriptGuids = UnitBase.GetScriptGuids(unit.member.targetType).ToHashSet();
}
private Member _member;
private Member _pseudoDeclarer;
public Member member
{
get => _member ?? unit.member;
set => _member = value;
}
public Member pseudoDeclarer
{
get => _pseudoDeclarer ?? member.ToPseudoDeclarer();
set => _pseudoDeclarer = value;
}
public bool isPseudoInherited => member == pseudoDeclarer;
protected abstract ActionDirection direction { get; }
public Type targetType { get; private set; }
protected override GUIStyle Style()
{
if (unit.member.isPseudoInherited)
{
return FuzzyWindow.Styles.optionWithIconDim;
}
return base.Style();
}
protected override string Label(bool human)
{
return unit.member.info.SelectedName(human, direction);
}
protected override string FavoriteKey()
{
return $"{member.ToUniqueString()}@{direction.ToString().ToLower()}";
}
protected override int Order()
{
if (member.isConstructor)
{
return 0;
}
return base.Order();
}
protected override string Haystack(bool human)
{
return $"{targetType.SelectedName(human)}{(human ? ": " : ".")}{Label(human)}";
}
protected override void FillFromUnit()
{
targetType = unit.member.targetType;
member = unit.member;
pseudoDeclarer = member.ToPseudoDeclarer();
base.FillFromUnit();
}
public override void Deserialize(UnitOptionRow row)
{
base.Deserialize(row);
targetType = Codebase.DeserializeType(row.tag1);
if (!string.IsNullOrEmpty(row.tag2))
{
member = Codebase.DeserializeMember(row.tag2);
}
if (!string.IsNullOrEmpty(row.tag3))
{
pseudoDeclarer = Codebase.DeserializeMember(row.tag3);
}
}
public override UnitOptionRow Serialize()
{
var row = base.Serialize();
row.tag1 = Codebase.SerializeType(targetType);
row.tag2 = Codebase.SerializeMember(member);
row.tag3 = Codebase.SerializeMember(pseudoDeclarer);
return row;
}
public override void OnPopulate()
{
// Members are late-reflected to speed up loading and search
// We only reflect them when we're just about to populate their node
// By doing it in OnPopulate instead of on-demand later, we ensure
// any error will be gracefully catched and shown as a warning by
// the fuzzy window
member.EnsureReflected();
pseudoDeclarer.EnsureReflected();
base.OnPopulate();
}
}
}

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