935 lines
23 KiB
C#
935 lines
23 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using BlockSpace.Voxels;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
|
|
public class Node : MonoBehaviour
|
|
{
|
|
private const float PortHighlightScale = 1.25f;
|
|
|
|
private const float PortPulseScale = 0.22f;
|
|
|
|
private const float PortPulseDuration = 0.16f;
|
|
|
|
private const float SelectedScale = 1.035f;
|
|
|
|
private static readonly Color SelectedTextColor = new Color(0.55f, 0.92f, 1f, 1f);
|
|
|
|
[Header("Identity")]
|
|
[SerializeField]
|
|
private int nodeId;
|
|
|
|
[Header("Definition")]
|
|
public NodeDefinition Definition;
|
|
|
|
[Header("Runtime")]
|
|
public List<NodePort> InputPorts = new List<NodePort>();
|
|
|
|
public List<NodePort> OutputPorts = new List<NodePort>();
|
|
|
|
private NodeBehavior behavior;
|
|
|
|
private NodeGraphManager graphManager;
|
|
|
|
private bool selected;
|
|
|
|
private bool visibleInEditMode = true;
|
|
|
|
private readonly List<NodeSerializedConnection> pendingSerializedConnections = new List<NodeSerializedConnection>();
|
|
|
|
private readonly Dictionary<string, BSValue> pendingInputValues = new Dictionary<string, BSValue>(StringComparer.Ordinal);
|
|
|
|
private readonly Dictionary<string, BSValue> pendingOutputValues = new Dictionary<string, BSValue>(StringComparer.Ordinal);
|
|
|
|
private readonly List<TMP_Text> cachedPortTexts = new List<TMP_Text>();
|
|
|
|
private readonly Dictionary<TMP_Text, Color> defaultTextColors = new Dictionary<TMP_Text, Color>();
|
|
|
|
private readonly List<Renderer> cachedRenderers = new List<Renderer>();
|
|
|
|
private Vector3 defaultScale = Vector3.one;
|
|
|
|
[Header("Node Name")]
|
|
public TMP_Text NodeNameText;
|
|
|
|
[Header("Boards")]
|
|
public GameObject SmallBoard;
|
|
|
|
public GameObject MediumBoard;
|
|
|
|
public GameObject LargeBoard;
|
|
|
|
public GameObject ExtraLargeBoard;
|
|
|
|
[Header("Input Port Parents")]
|
|
public Transform InputRunPortParent;
|
|
|
|
public Transform InputValuePortParent;
|
|
|
|
[Header("Output Port Parents")]
|
|
public Transform OutputRunPortParent;
|
|
|
|
public Transform OutputValuePortParent;
|
|
|
|
[Header("Input Text Parent")]
|
|
public Transform InputTextParent;
|
|
|
|
[Header("Output Text Parent")]
|
|
public Transform OutputTextParent;
|
|
|
|
[Header("Port Prefabs")]
|
|
public GameObject InputRunPortPrefab;
|
|
|
|
public GameObject InputValuePortPrefab;
|
|
|
|
public GameObject OutputRunPortPrefab;
|
|
|
|
public GameObject OutputValuePortPrefab;
|
|
|
|
[Header("Text Prefab")]
|
|
public TMP_Text PortTextPrefab;
|
|
|
|
[Header("Port Layout")]
|
|
public float FirstPortY;
|
|
|
|
public float PortYOffset = -1.25f;
|
|
|
|
[Header("Text Layout")]
|
|
public float FirstTextY = 0.0869f;
|
|
|
|
public float TextYOffset = -0.0618f;
|
|
|
|
private static readonly int BaseColorId = Shader.PropertyToID("_BaseColor");
|
|
|
|
private static readonly int ColorId = Shader.PropertyToID("_Color");
|
|
|
|
private static readonly int TintColorId = Shader.PropertyToID("_TintColor");
|
|
|
|
public NodeBoardSize BoardSize { get; private set; }
|
|
|
|
public int NodeId => nodeId;
|
|
|
|
private void Awake()
|
|
{
|
|
defaultScale = base.transform.localScale;
|
|
CaptureSerializedRuntimeState();
|
|
ReinitializeFromDefinition();
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
NodeGraphManager.Instance?.RegisterNode(this);
|
|
}
|
|
|
|
private void LateUpdate()
|
|
{
|
|
UpdatePortVisualState();
|
|
UpdatePortTintState();
|
|
}
|
|
|
|
public void AssignGraph(NodeGraphManager manager)
|
|
{
|
|
graphManager = manager;
|
|
for (int i = 0; i < InputPorts.Count; i++)
|
|
{
|
|
InputPorts[i].Owner = this;
|
|
}
|
|
for (int j = 0; j < OutputPorts.Count; j++)
|
|
{
|
|
OutputPorts[j].Owner = this;
|
|
}
|
|
}
|
|
|
|
public void SetNodeId(int id)
|
|
{
|
|
nodeId = Mathf.Max(0, id);
|
|
}
|
|
|
|
public void ReinitializeFromDefinition()
|
|
{
|
|
if (Definition == null)
|
|
{
|
|
InputPorts.Clear();
|
|
OutputPorts.Clear();
|
|
ClearVisualCaches();
|
|
return;
|
|
}
|
|
BuildPorts();
|
|
BoardSize = Definition.GetBoardSize();
|
|
BuildVisuals();
|
|
CreateBehavior();
|
|
ApplySelectionVisual();
|
|
ApplyVisibility();
|
|
}
|
|
|
|
public bool TryGetPort(string portName, PortDirection direction, out NodePort port)
|
|
{
|
|
List<NodePort> list = ((direction == PortDirection.Input) ? InputPorts : OutputPorts);
|
|
for (int i = 0; i < list.Count; i++)
|
|
{
|
|
NodePort nodePort = list[i];
|
|
if (nodePort != null && string.Equals(nodePort.Name, portName, StringComparison.Ordinal))
|
|
{
|
|
port = nodePort;
|
|
return true;
|
|
}
|
|
}
|
|
port = null;
|
|
return false;
|
|
}
|
|
|
|
public bool TryGetPort(string portName, PortDirection direction, PortKind kind, out NodePort port)
|
|
{
|
|
if (TryGetPort(portName, direction, out port) && port.Kind == kind)
|
|
{
|
|
return true;
|
|
}
|
|
port = null;
|
|
return false;
|
|
}
|
|
|
|
public BSValue GetInputValue(string portName)
|
|
{
|
|
if (!TryGetPort(portName, PortDirection.Input, PortKind.Value, out var port))
|
|
{
|
|
return BSValue.None;
|
|
}
|
|
return port.Value;
|
|
}
|
|
|
|
public BSValue GetOutputValue(string portName)
|
|
{
|
|
if (!TryGetPort(portName, PortDirection.Output, PortKind.Value, out var port))
|
|
{
|
|
return BSValue.None;
|
|
}
|
|
return port.Value;
|
|
}
|
|
|
|
public bool TryGetInputGroup(string portName, out VoxelGroup group)
|
|
{
|
|
group = null;
|
|
if (TryGetPort(portName, PortDirection.Input, PortKind.Value, out var port))
|
|
{
|
|
return port.Value.TryResolveGroup(out group);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public VoxelGroup GetInputGroup(string portName)
|
|
{
|
|
TryGetInputGroup(portName, out var group);
|
|
return group;
|
|
}
|
|
|
|
public void SetInputValue(string portName, BSValue value)
|
|
{
|
|
if (TryGetPort(portName, PortDirection.Input, PortKind.Value, out var port))
|
|
{
|
|
if (graphManager != null)
|
|
{
|
|
graphManager.AssignInputValue(this, port, value, broadcast: true);
|
|
}
|
|
else
|
|
{
|
|
SetInputValueLocal(port, value, pulsePort: true);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SetOutputValue(string portName, BSValue value)
|
|
{
|
|
if (TryGetPort(portName, PortDirection.Output, PortKind.Value, out var port))
|
|
{
|
|
if (graphManager != null)
|
|
{
|
|
graphManager.ApplyOutputValue(this, port, value, broadcast: false);
|
|
}
|
|
else
|
|
{
|
|
SetOutputValueLocal(port, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SetOutputValueLocalOnly(string portName, BSValue value)
|
|
{
|
|
if (TryGetPort(portName, PortDirection.Output, PortKind.Value, out var port))
|
|
{
|
|
if (graphManager != null)
|
|
{
|
|
graphManager.ApplyOutputValue(this, port, value, broadcast: false);
|
|
}
|
|
else
|
|
{
|
|
SetOutputValueLocal(port, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void TriggerRun(string outputPortName)
|
|
{
|
|
if (TryGetPort(outputPortName, PortDirection.Output, PortKind.Run, out var port))
|
|
{
|
|
if (graphManager != null)
|
|
{
|
|
graphManager.TriggerOutputRun(this, port, broadcast: false);
|
|
}
|
|
else
|
|
{
|
|
TriggerRunLocal(port);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void ReceiveRun(string inputPortName)
|
|
{
|
|
if (TryGetPort(inputPortName, PortDirection.Input, PortKind.Run, out var port))
|
|
{
|
|
PulsePort(port);
|
|
behavior?.OnRun(inputPortName);
|
|
}
|
|
}
|
|
|
|
public void TickBehavior()
|
|
{
|
|
behavior?.OnGraphUpdate();
|
|
}
|
|
|
|
public bool Connect(string outputPortName, Node targetNode, string inputPortName)
|
|
{
|
|
if (graphManager == null || targetNode == null)
|
|
{
|
|
return false;
|
|
}
|
|
return graphManager.Connect(this, outputPortName, targetNode, inputPortName, broadcast: true);
|
|
}
|
|
|
|
public void Disconnect(string outputPortName, Node targetNode, string inputPortName)
|
|
{
|
|
if (!(graphManager == null) && !(targetNode == null))
|
|
{
|
|
graphManager.Disconnect(this, outputPortName, targetNode, inputPortName, broadcast: true);
|
|
}
|
|
}
|
|
|
|
public void SetSelected(bool isSelected)
|
|
{
|
|
if (selected != isSelected)
|
|
{
|
|
selected = isSelected;
|
|
ApplySelectionVisual();
|
|
}
|
|
}
|
|
|
|
public void SetVisibleInEditMode(bool visible)
|
|
{
|
|
if (visibleInEditMode != visible)
|
|
{
|
|
visibleInEditMode = visible;
|
|
ApplyVisibility();
|
|
}
|
|
}
|
|
|
|
public Bounds GetWorldBounds()
|
|
{
|
|
if (cachedRenderers.Count == 0)
|
|
{
|
|
CacheVisuals();
|
|
}
|
|
if (cachedRenderers.Count == 0)
|
|
{
|
|
return new Bounds(base.transform.position, Vector3.one * 0.2f);
|
|
}
|
|
Bounds result = new Bounds(cachedRenderers[0].bounds.center, cachedRenderers[0].bounds.size);
|
|
for (int i = 1; i < cachedRenderers.Count; i++)
|
|
{
|
|
if (cachedRenderers[i] != null)
|
|
{
|
|
result.Encapsulate(cachedRenderers[i].bounds);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public Vector3 GetPortWorldPosition(NodePort port)
|
|
{
|
|
if (port != null && port.VisualTransform != null)
|
|
{
|
|
return port.VisualTransform.position;
|
|
}
|
|
return base.transform.position;
|
|
}
|
|
|
|
public Vector3 GetPortWorldDirection(NodePort port)
|
|
{
|
|
if (port == null)
|
|
{
|
|
return base.transform.forward;
|
|
}
|
|
if (port.VisualTransform != null)
|
|
{
|
|
if (port.Direction != PortDirection.Output)
|
|
{
|
|
return -port.VisualTransform.right;
|
|
}
|
|
return port.VisualTransform.right;
|
|
}
|
|
if (port.Direction != PortDirection.Output)
|
|
{
|
|
return -base.transform.right;
|
|
}
|
|
return base.transform.right;
|
|
}
|
|
|
|
internal void SetInputValueLocal(NodePort port, BSValue value, bool pulsePort)
|
|
{
|
|
if (port != null && port.Direction == PortDirection.Input && port.Kind == PortKind.Value)
|
|
{
|
|
BSValue bSValue = value.Normalize();
|
|
bool num = !BSValueEquals(port.Value, bSValue);
|
|
port.Value = bSValue;
|
|
UpdatePortLabel(port);
|
|
if (pulsePort)
|
|
{
|
|
PulsePort(port);
|
|
}
|
|
if (num)
|
|
{
|
|
behavior?.OnInputValueChanged(port.Name, port.Value);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void SetOutputValueLocal(NodePort port, BSValue value)
|
|
{
|
|
if (port == null || port.Direction != PortDirection.Output || port.Kind != PortKind.Value)
|
|
{
|
|
return;
|
|
}
|
|
port.Value = value.Normalize();
|
|
UpdatePortLabel(port);
|
|
PulsePort(port);
|
|
for (int i = 0; i < port.Connections.Count; i++)
|
|
{
|
|
NodePort nodePort = port.Connections[i];
|
|
if (nodePort != null && !(nodePort.Owner == null))
|
|
{
|
|
nodePort.Owner.SetInputValueLocal(nodePort, port.Value, pulsePort: true);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void TriggerRunLocal(NodePort port)
|
|
{
|
|
if (port == null || port.Direction != PortDirection.Output || port.Kind != PortKind.Run)
|
|
{
|
|
return;
|
|
}
|
|
PulsePort(port);
|
|
for (int i = 0; i < port.Connections.Count; i++)
|
|
{
|
|
NodePort nodePort = port.Connections[i];
|
|
if (nodePort != null && !(nodePort.Owner == null))
|
|
{
|
|
nodePort.Owner.ReceiveRun(nodePort.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void PulsePort(NodePort port)
|
|
{
|
|
if (port != null)
|
|
{
|
|
port.PulseUntil = Mathf.Max(port.PulseUntil, Time.unscaledTime + 0.16f);
|
|
}
|
|
}
|
|
|
|
internal void SetPortHighlight(NodePort port, bool highlighted)
|
|
{
|
|
if (port != null)
|
|
{
|
|
port.Highlighted = highlighted;
|
|
}
|
|
}
|
|
|
|
internal List<NodeSerializedConnection> ConsumePendingSerializedConnections()
|
|
{
|
|
List<NodeSerializedConnection> result = new List<NodeSerializedConnection>(pendingSerializedConnections);
|
|
pendingSerializedConnections.Clear();
|
|
return result;
|
|
}
|
|
|
|
private void CaptureSerializedRuntimeState()
|
|
{
|
|
pendingSerializedConnections.Clear();
|
|
pendingInputValues.Clear();
|
|
pendingOutputValues.Clear();
|
|
CapturePortState(InputPorts, pendingInputValues, captureConnections: false);
|
|
CapturePortState(OutputPorts, pendingOutputValues, captureConnections: true);
|
|
}
|
|
|
|
private void CapturePortState(List<NodePort> ports, Dictionary<string, BSValue> valueStore, bool captureConnections)
|
|
{
|
|
if (ports == null)
|
|
{
|
|
return;
|
|
}
|
|
for (int i = 0; i < ports.Count; i++)
|
|
{
|
|
NodePort nodePort = ports[i];
|
|
if (nodePort == null || string.IsNullOrEmpty(nodePort.Name))
|
|
{
|
|
continue;
|
|
}
|
|
valueStore[nodePort.Name] = nodePort.Value.Normalize();
|
|
if (!captureConnections || nodePort.Direction != PortDirection.Output || nodePort.Connections == null)
|
|
{
|
|
continue;
|
|
}
|
|
for (int j = 0; j < nodePort.Connections.Count; j++)
|
|
{
|
|
NodePort nodePort2 = nodePort.Connections[j];
|
|
if (nodePort2 != null && !(nodePort2.Owner == null) && !string.IsNullOrEmpty(nodePort2.Name))
|
|
{
|
|
pendingSerializedConnections.Add(new NodeSerializedConnection
|
|
{
|
|
outputPortName = nodePort.Name,
|
|
inputNode = nodePort2.Owner,
|
|
inputPortName = nodePort2.Name
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void BuildPorts()
|
|
{
|
|
InputPorts.Clear();
|
|
OutputPorts.Clear();
|
|
for (int i = 0; i < Definition.InputPorts.Count; i++)
|
|
{
|
|
NodePortDefinition nodePortDefinition = Definition.InputPorts[i];
|
|
pendingInputValues.TryGetValue(nodePortDefinition.PortName, out var value);
|
|
InputPorts.Add(new NodePort
|
|
{
|
|
Index = i,
|
|
Name = nodePortDefinition.PortName,
|
|
Direction = PortDirection.Input,
|
|
Kind = nodePortDefinition.Kind,
|
|
Value = (value.IsInitialized ? value.Normalize() : BSValue.FromText(nodePortDefinition.DefaultValue)),
|
|
Owner = this
|
|
});
|
|
}
|
|
for (int j = 0; j < Definition.OutputPorts.Count; j++)
|
|
{
|
|
NodePortDefinition nodePortDefinition2 = Definition.OutputPorts[j];
|
|
pendingOutputValues.TryGetValue(nodePortDefinition2.PortName, out var value2);
|
|
OutputPorts.Add(new NodePort
|
|
{
|
|
Index = j,
|
|
Name = nodePortDefinition2.PortName,
|
|
Direction = PortDirection.Output,
|
|
Kind = nodePortDefinition2.Kind,
|
|
Value = (value2.IsInitialized ? value2.Normalize() : BSValue.FromText(nodePortDefinition2.DefaultValue)),
|
|
Owner = this
|
|
});
|
|
}
|
|
}
|
|
|
|
private void BuildVisuals()
|
|
{
|
|
ApplyBoardSize();
|
|
ApplyNodeName();
|
|
ClearChildren(InputRunPortParent);
|
|
ClearChildren(InputValuePortParent);
|
|
ClearChildren(OutputRunPortParent);
|
|
ClearChildren(OutputValuePortParent);
|
|
ClearChildren(InputTextParent);
|
|
ClearChildren(OutputTextParent);
|
|
ClearVisualCaches();
|
|
BuildInputVisuals();
|
|
BuildOutputVisuals();
|
|
CacheVisuals();
|
|
}
|
|
|
|
private void ApplyBoardSize()
|
|
{
|
|
if (SmallBoard != null)
|
|
{
|
|
SmallBoard.SetActive(value: false);
|
|
}
|
|
if (MediumBoard != null)
|
|
{
|
|
MediumBoard.SetActive(value: false);
|
|
}
|
|
if (LargeBoard != null)
|
|
{
|
|
LargeBoard.SetActive(value: false);
|
|
}
|
|
if (ExtraLargeBoard != null)
|
|
{
|
|
ExtraLargeBoard.SetActive(value: false);
|
|
}
|
|
switch (BoardSize)
|
|
{
|
|
case NodeBoardSize.Small:
|
|
if (SmallBoard != null)
|
|
{
|
|
SmallBoard.SetActive(value: true);
|
|
}
|
|
break;
|
|
case NodeBoardSize.Medium:
|
|
if (MediumBoard != null)
|
|
{
|
|
MediumBoard.SetActive(value: true);
|
|
}
|
|
break;
|
|
case NodeBoardSize.Large:
|
|
if (LargeBoard != null)
|
|
{
|
|
LargeBoard.SetActive(value: true);
|
|
}
|
|
break;
|
|
case NodeBoardSize.ExtraLarge:
|
|
if (ExtraLargeBoard != null)
|
|
{
|
|
ExtraLargeBoard.SetActive(value: true);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void ApplyNodeName()
|
|
{
|
|
if (NodeNameText != null)
|
|
{
|
|
NodeNameText.text = ((Definition != null) ? Definition.NodeName : "Node");
|
|
}
|
|
}
|
|
|
|
private void BuildInputVisuals()
|
|
{
|
|
for (int i = 0; i < InputPorts.Count; i++)
|
|
{
|
|
NodePort nodePort = InputPorts[i];
|
|
bool num = nodePort.Kind == PortKind.Run;
|
|
Transform transform = (num ? InputRunPortParent : InputValuePortParent);
|
|
GameObject gameObject = (num ? InputRunPortPrefab : InputValuePortPrefab);
|
|
if (!(transform == null) && !(gameObject == null))
|
|
{
|
|
GameObject gameObject2 = UnityEngine.Object.Instantiate(gameObject, transform);
|
|
gameObject2.name = nodePort.Name;
|
|
Vector3 localPosition = gameObject2.transform.localPosition;
|
|
localPosition.y = FirstPortY + PortYOffset * (float)i;
|
|
gameObject2.transform.localPosition = localPosition;
|
|
nodePort.AttachVisual(gameObject2.transform);
|
|
nodePort.AttachVisualRenderers(gameObject2.GetComponentsInChildren<Renderer>(includeInactive: true));
|
|
if (PortTextPrefab != null && InputTextParent != null)
|
|
{
|
|
TMP_Text tMP_Text = UnityEngine.Object.Instantiate(PortTextPrefab, InputTextParent);
|
|
tMP_Text.name = nodePort.Name + " Text";
|
|
Vector3 localPosition2 = tMP_Text.transform.localPosition;
|
|
localPosition2.y = FirstTextY + TextYOffset * (float)i;
|
|
tMP_Text.transform.localPosition = localPosition2;
|
|
nodePort.AttachLabel(tMP_Text);
|
|
UpdatePortLabel(nodePort);
|
|
RegisterPortText(tMP_Text);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void BuildOutputVisuals()
|
|
{
|
|
for (int i = 0; i < OutputPorts.Count; i++)
|
|
{
|
|
NodePort nodePort = OutputPorts[i];
|
|
bool num = nodePort.Kind == PortKind.Run;
|
|
Transform transform = (num ? OutputRunPortParent : OutputValuePortParent);
|
|
GameObject gameObject = (num ? OutputRunPortPrefab : OutputValuePortPrefab);
|
|
if (!(transform == null) && !(gameObject == null))
|
|
{
|
|
GameObject gameObject2 = UnityEngine.Object.Instantiate(gameObject, transform);
|
|
gameObject2.name = nodePort.Name;
|
|
Vector3 localPosition = gameObject2.transform.localPosition;
|
|
localPosition.y = FirstPortY + PortYOffset * (float)i;
|
|
gameObject2.transform.localPosition = localPosition;
|
|
nodePort.AttachVisual(gameObject2.transform);
|
|
nodePort.AttachVisualRenderers(gameObject2.GetComponentsInChildren<Renderer>(includeInactive: true));
|
|
if (PortTextPrefab != null && OutputTextParent != null)
|
|
{
|
|
TMP_Text tMP_Text = UnityEngine.Object.Instantiate(PortTextPrefab, OutputTextParent);
|
|
tMP_Text.name = nodePort.Name + " Text";
|
|
Vector3 localPosition2 = tMP_Text.transform.localPosition;
|
|
localPosition2.y = FirstTextY + TextYOffset * (float)i;
|
|
tMP_Text.transform.localPosition = localPosition2;
|
|
nodePort.AttachLabel(tMP_Text);
|
|
UpdatePortLabel(nodePort);
|
|
RegisterPortText(tMP_Text);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UpdatePortLabel(NodePort port)
|
|
{
|
|
if (port != null && !(port.LabelText == null))
|
|
{
|
|
port.LabelText.text = ((port.Kind == PortKind.Value) ? (port.Name + ": " + FormatPortValue(port.Value)) : port.Name);
|
|
}
|
|
}
|
|
|
|
private static string FormatPortValue(BSValue value)
|
|
{
|
|
value = value.Normalize();
|
|
return value.Kind switch
|
|
{
|
|
BSValueKind.None => "None",
|
|
BSValueKind.Number => value.AsText(),
|
|
BSValueKind.Text => string.IsNullOrEmpty(value.AsText()) ? "\"\"" : value.AsText(),
|
|
BSValueKind.Bool => value.AsBool() ? "True" : "False",
|
|
BSValueKind.Player => $"Player {value.AsPlayerId()}",
|
|
BSValueKind.Group => $"Group {value.AsGroupId()}",
|
|
_ => value.ToString(),
|
|
};
|
|
}
|
|
|
|
private void RegisterPortText(TMP_Text text)
|
|
{
|
|
if (!(text == null))
|
|
{
|
|
cachedPortTexts.Add(text);
|
|
if (!defaultTextColors.ContainsKey(text))
|
|
{
|
|
defaultTextColors.Add(text, text.color);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ClearVisualCaches()
|
|
{
|
|
cachedPortTexts.Clear();
|
|
defaultTextColors.Clear();
|
|
cachedRenderers.Clear();
|
|
}
|
|
|
|
private void CacheVisuals()
|
|
{
|
|
cachedRenderers.Clear();
|
|
cachedRenderers.AddRange(GetComponentsInChildren<Renderer>(includeInactive: true));
|
|
cachedPortTexts.Clear();
|
|
defaultTextColors.Clear();
|
|
TMP_Text[] componentsInChildren = GetComponentsInChildren<TMP_Text>(includeInactive: true);
|
|
for (int i = 0; i < componentsInChildren.Length; i++)
|
|
{
|
|
RegisterPortText(componentsInChildren[i]);
|
|
}
|
|
}
|
|
|
|
private void ApplySelectionVisual()
|
|
{
|
|
base.transform.localScale = defaultScale * (selected ? 1.035f : 1f);
|
|
for (int i = 0; i < cachedPortTexts.Count; i++)
|
|
{
|
|
TMP_Text tMP_Text = cachedPortTexts[i];
|
|
if (!(tMP_Text == null))
|
|
{
|
|
Color value;
|
|
Color color = (defaultTextColors.TryGetValue(tMP_Text, out value) ? value : Color.white);
|
|
tMP_Text.color = (selected ? SelectedTextColor : color);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ApplyVisibility()
|
|
{
|
|
for (int i = 0; i < cachedRenderers.Count; i++)
|
|
{
|
|
if (cachedRenderers[i] != null)
|
|
{
|
|
cachedRenderers[i].enabled = visibleInEditMode;
|
|
}
|
|
}
|
|
for (int j = 0; j < cachedPortTexts.Count; j++)
|
|
{
|
|
if (cachedPortTexts[j] != null)
|
|
{
|
|
cachedPortTexts[j].enabled = visibleInEditMode;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UpdatePortVisualState()
|
|
{
|
|
UpdatePortVisualState(InputPorts);
|
|
UpdatePortVisualState(OutputPorts);
|
|
}
|
|
|
|
private void UpdatePortTintState()
|
|
{
|
|
UpdatePortTintState(InputPorts);
|
|
UpdatePortTintState(OutputPorts);
|
|
}
|
|
|
|
private void UpdatePortTintState(List<NodePort> ports)
|
|
{
|
|
for (int i = 0; i < ports.Count; i++)
|
|
{
|
|
NodePort nodePort = ports[i];
|
|
if (nodePort == null || nodePort.Kind != PortKind.Value || nodePort.VisualTransform == null)
|
|
{
|
|
continue;
|
|
}
|
|
if (nodePort.VisualRenderers == null)
|
|
{
|
|
nodePort.AttachVisualRenderers(nodePort.VisualTransform.GetComponentsInChildren<Renderer>(includeInactive: true));
|
|
}
|
|
if (nodePort.VisualRenderers == null || nodePort.VisualRenderers.Length == 0)
|
|
{
|
|
continue;
|
|
}
|
|
BSValueKind kind = ((nodePort.Direction != PortDirection.Input) ? nodePort.Value.Normalize().Kind : ((nodePort.SourceGroupId > 0) ? BSValueKind.Group : ((nodePort.IncomingConnection == null) ? nodePort.Value.Normalize().Kind : nodePort.IncomingConnection.Value.Normalize().Kind)));
|
|
Color color = NodeWirePalette.ForValueKind(kind);
|
|
if (nodePort.LastTint == color)
|
|
{
|
|
continue;
|
|
}
|
|
nodePort.LastTint = color;
|
|
NodePort nodePort2 = nodePort;
|
|
if (nodePort2.VisualBlock == null)
|
|
{
|
|
nodePort2.VisualBlock = new MaterialPropertyBlock();
|
|
}
|
|
nodePort.VisualBlock.Clear();
|
|
nodePort.VisualBlock.SetColor(BaseColorId, color);
|
|
nodePort.VisualBlock.SetColor(ColorId, color);
|
|
nodePort.VisualBlock.SetColor(TintColorId, color);
|
|
for (int j = 0; j < nodePort.VisualRenderers.Length; j++)
|
|
{
|
|
Renderer renderer = nodePort.VisualRenderers[j];
|
|
if (!(renderer == null))
|
|
{
|
|
renderer.SetPropertyBlock(nodePort.VisualBlock);
|
|
Material sharedMaterial = renderer.sharedMaterial;
|
|
if (sharedMaterial != null && !sharedMaterial.HasProperty(BaseColorId) && !sharedMaterial.HasProperty(ColorId) && !sharedMaterial.HasProperty(TintColorId))
|
|
{
|
|
renderer.material.color = color;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UpdatePortVisualState(List<NodePort> ports)
|
|
{
|
|
for (int i = 0; i < ports.Count; i++)
|
|
{
|
|
NodePort nodePort = ports[i];
|
|
if (nodePort != null && !(nodePort.VisualTransform == null))
|
|
{
|
|
float num = 0f;
|
|
if (nodePort.PulseUntil > Time.unscaledTime)
|
|
{
|
|
num = Mathf.Clamp01((nodePort.PulseUntil - Time.unscaledTime) / 0.16f);
|
|
}
|
|
float num2 = (nodePort.Highlighted ? 1.25f : 1f);
|
|
num2 += num * 0.22f;
|
|
nodePort.VisualTransform.localScale = nodePort.BaseLocalScale * num2;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ClearChildren(Transform parent)
|
|
{
|
|
if (parent == null)
|
|
{
|
|
return;
|
|
}
|
|
for (int num = parent.childCount - 1; num >= 0; num--)
|
|
{
|
|
Transform child = parent.GetChild(num);
|
|
if (!(child == null))
|
|
{
|
|
if (Application.isPlaying)
|
|
{
|
|
UnityEngine.Object.Destroy(child.gameObject);
|
|
}
|
|
else
|
|
{
|
|
UnityEngine.Object.DestroyImmediate(child.gameObject);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CreateBehavior()
|
|
{
|
|
if (behavior != null)
|
|
{
|
|
if (Application.isPlaying)
|
|
{
|
|
UnityEngine.Object.Destroy(behavior);
|
|
}
|
|
else
|
|
{
|
|
UnityEngine.Object.DestroyImmediate(behavior);
|
|
}
|
|
behavior = null;
|
|
}
|
|
if (Definition == null)
|
|
{
|
|
return;
|
|
}
|
|
Type type = FindType(Definition.BehaviorClassName);
|
|
if (!(type == null) && typeof(NodeBehavior).IsAssignableFrom(type))
|
|
{
|
|
behavior = base.gameObject.AddComponent(type) as NodeBehavior;
|
|
if (behavior != null)
|
|
{
|
|
behavior.Node = this;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static Type FindType(string typeName)
|
|
{
|
|
Type type = Type.GetType(typeName);
|
|
if (type != null)
|
|
{
|
|
return type;
|
|
}
|
|
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
|
for (int i = 0; i < assemblies.Length; i++)
|
|
{
|
|
type = assemblies[i].GetType(typeName);
|
|
if (type != null)
|
|
{
|
|
return type;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static bool BSValueEquals(BSValue a, BSValue b)
|
|
{
|
|
a = a.Normalize();
|
|
b = b.Normalize();
|
|
if (a.Kind != b.Kind)
|
|
{
|
|
return false;
|
|
}
|
|
return a.Kind switch
|
|
{
|
|
BSValueKind.Number => Math.Abs(a.Number - b.Number) <= 0.0001,
|
|
BSValueKind.Text => string.Equals(a.Text ?? string.Empty, b.Text ?? string.Empty, StringComparison.Ordinal),
|
|
BSValueKind.Bool => a.Bool == b.Bool,
|
|
BSValueKind.Player => a.PlayerId == b.PlayerId,
|
|
BSValueKind.Group => a.GroupId == b.GroupId,
|
|
_ => true,
|
|
};
|
|
}
|
|
}
|