Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions Editor/CanvasView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class CanvasView : GraphView

public Graph Graph { get; private set; }

private readonly Label title;
private Label title;
private readonly List<CommentView> commentViews = new List<CommentView>();
private readonly SearchWindow searchWindow;
private readonly EdgeConnectorListener edgeConnectorListener;
Expand Down Expand Up @@ -69,7 +69,7 @@ public CanvasView(GraphEditorWindow window)

RegisterCallback<GeometryChangedEvent>(OnFirstResize);

title = new Label("BLUEGRAPH");
title = new Label("BlueGraph");
title.AddToClassList("canvasViewTitle");
Add(title);

Expand Down Expand Up @@ -224,6 +224,9 @@ public void AddSearchProvider(ISearchProvider provider)
public void Load(Graph graph)
{
Graph = graph;

graph.ReconstructPortConnections();

serializedGraph = new SerializedObject(Graph);
title.text = graph.Title;
SetupZoom(graph.ZoomMinScale, graph.ZoomMaxScale);
Expand Down Expand Up @@ -267,8 +270,6 @@ public void Load(Graph graph)
node.Name = required.nodeName;
node.Position = required.position;
AddNodeFromSearch(node, node.Position, null, false);


}

}
Expand All @@ -278,7 +279,7 @@ public void Load(Graph graph)
/// <summary>
/// Create a new node from reflection data and insert into the Graph.
/// </summary>
internal void AddNodeFromSearch(Node node, Vector2 screenPosition, PortView connectedPort = null, bool registerUndo = true)
public void AddNodeFromSearch(Node node, Vector2 screenPosition, PortView connectedPort = null, bool registerUndo = true)
{
// Calculate where to place this node on the graph
var windowRoot = EditorWindow.rootVisualElement;
Expand Down
4 changes: 3 additions & 1 deletion Editor/GraphAssetHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public static bool OnOpenAsset(int instanceID, int line)
/// <summary>
/// Open the appropriate GraphEditor for the Graph asset
/// </summary>
public static void OnOpenGraph(Graph graph)
public static GraphEditor OnOpenGraph(Graph graph)
{
var editor = UnityEditor.Editor.CreateEditor(graph) as GraphEditor;
if (!editor)
Expand All @@ -34,7 +34,9 @@ public static void OnOpenGraph(Graph graph)
else
{
editor.CreateOrFocusEditorWindow();
return editor;
}
return null;
}
}
}
52 changes: 51 additions & 1 deletion Editor/GraphEditorWindow.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using UnityEditor;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

Expand All @@ -13,12 +14,17 @@ public class GraphEditorWindow : EditorWindow

public Graph Graph { get; protected set; }

private int cacheDelay = 10;
private int cacheTick = 0;

/// <summary>
/// Load a graph asset in this window for editing
/// </summary>
public virtual void Load(Graph graph)
{
Graph = graph;
Graph.IsBeingEditted = true;
Graph.ReconstructPortConnections();

Canvas = new CanvasView(this);
Canvas.Load(graph);
Expand All @@ -29,6 +35,29 @@ public virtual void Load(Graph graph)
Repaint();
}

private void OnDestroy()
{
OnClose();
}

protected virtual void OnFocus()
{
if (Graph != null)
Graph.IsBeingEditted = true;
}

protected virtual void OnLostFocus()
{
if (Graph != null)
Graph.IsBeingEditted = false;
}

/// <summary>
/// Override to add additional functional to the closing functionality of the Graph Editor.
/// </summary>
protected virtual void OnClose()
{ }

protected virtual void Update()
{
// Canvas can be invalidated when the Unity Editor
Expand All @@ -39,6 +68,20 @@ protected virtual void Update()
return;
}

cacheTick++;

if(cacheTick%cacheDelay == 0)
{
if (Application.isPlaying)
{
Graph?.ReconstructPortConnections();
}
else
{
Graph?.CachePortConnections();
}
}

Canvas.Update();
}

Expand All @@ -52,5 +95,12 @@ protected virtual void OnEnable()
Load(Graph);
}
}

private Task NextEditorFrame()
{
var tcs = new TaskCompletionSource<bool>();
EditorApplication.delayCall += () => tcs.SetResult(true);
return tcs.Task;
}
}
}
15 changes: 14 additions & 1 deletion Editor/NodeReflection.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
Expand Down Expand Up @@ -187,7 +188,7 @@ public NodeReflectionData(Type type, NodeAttribute nodeAttr)
contextMethods = new Dictionary<ContextMenu, MethodInfo>();

var attrs = type.GetCustomAttributes(true);
foreach (var attr in attrs)
foreach (var attr in attrs.Reverse())
{
if (attr is TagsAttribute tagAttr)
{
Expand All @@ -206,6 +207,17 @@ public NodeReflectionData(Type type, NodeAttribute nodeAttr)
HasControlElement = false
});
}
else if (attr is InputAttribute input)
{
Ports.Add(new PortReflectionData()
{
Name = input.Name,
Type = input.Type,
Direction = PortDirection.Input,
Capacity = input.Multiple ? PortCapacity.Multiple : PortCapacity.Single,
HasControlElement = false
});
}
}

// Load additional data from class fields
Expand All @@ -215,6 +227,7 @@ public NodeReflectionData(Type type, NodeAttribute nodeAttr)
LoadContextMethods();
}


public bool HasInputOfType(Type type)
{
foreach (var port in Ports)
Expand Down
7 changes: 7 additions & 0 deletions Editor/NodeView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ internal void Initialize(Node node, CanvasView canvas, EdgeConnectorListener con
errorMessage = new Label { name = "error-label" };
errorContainer.Add(errorMessage);

if (errorContainer != null)
{
errorContainer.RegisterCallback<MouseEnterEvent>( (MouseEnterEvent evt) => errorMessage.name = "error-label-hover");
errorContainer.RegisterCallback<MouseLeaveEvent>( (MouseLeaveEvent evt) => errorMessage.name = "error-label");
}

Insert(0, errorContainer);

SetPosition(new Rect(node.Position, Vector2.one));
Expand Down Expand Up @@ -77,6 +83,7 @@ internal void Initialize(Node node, CanvasView canvas, EdgeConnectorListener con
OnInitialize();
}


/// <summary>
/// Executed after receiving a node target and initial configuration
/// but before being added to the graph.
Expand Down
1 change: 1 addition & 0 deletions Editor/Resources/BlueGraphEditor/NodeView Hovered.uss
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

10 changes: 10 additions & 0 deletions Editor/Resources/BlueGraphEditor/NodeView Hovered.uss.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 33 additions & 5 deletions Editor/Resources/BlueGraphEditor/NodeView.uss
Original file line number Diff line number Diff line change
Expand Up @@ -117,21 +117,49 @@
.nodeView #error-label {
color: var(--bluegraph-node-error-color);
-unity-text-align: upper-center;
font-size: 110%;
font-size: 16px;

position: relative;
bottom: -50px;
bottom: 40%;

overflow: hidden;

min-width: 200px;
align-self: center;
min-width: 500px;
height: 16px;

align-self: left;
white-space: normal;
-unity-text-align: upper-left

transition: all 0.5s;
}
.nodeView #error-label-hover {
color: var(--bluegraph-node-error-color);
-unity-text-align: upper-center;
font-size: 16px;

position: relative;
bottom: 40%;

overflow: hidden;

min-width: 500px;

align-self: left;
white-space: normal;

height: 0px;
-unity-text-align: lower-left;

overflow: visible;

transition: height 0.5s;
}

.nodeView #error-icon {
background-image: resource("Error@2x");
width: 20px;
height: 20px;
left: -16px;
top: -16px;
top: -24px;
}
8 changes: 8 additions & 0 deletions Editor/Resources/BlueGraphEditorHover.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 21 additions & 2 deletions Runtime/Attributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public TagsAttribute(params string[] tags)
/// <summary>
/// An input port exposed on a Node
/// </summary>
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Class, AllowMultiple = true)]
public class InputAttribute : Attribute
{
/// <summary>
Expand All @@ -68,6 +68,8 @@ public class InputAttribute : Attribute
/// </summary>
public string Name { get; set; }

public Type Type;

/// <summary>
/// Can this input accept multiple outputs at once.
/// </summary>
Expand All @@ -78,9 +80,10 @@ public class InputAttribute : Attribute
/// </summary>
public bool Editable { get; set; } = true;

public InputAttribute(string name = null)
public InputAttribute(string name = null, Type type = null)
{
Name = name;
this.Type = type;
}
}

Expand Down Expand Up @@ -229,4 +232,20 @@ public CustomNodeViewAttribute(Type nodeType)
NodeType = nodeType;
}
}

/// <summary>
/// Tells the Node Caching system to cache the Node to a specific type.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public class CacheToAttribute : Attribute
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have an example of how would you use this? So we can add something to the Wiki docs.

Copy link
Author

@thechayed thechayed Aug 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, sorry for all the edits! I realized my original response was probably too complex.

I added a Node Cache that caches nodes by their type, or by a desired base type/interface using CacheToAttribute. Lookups using the cache are O(1) with no extra overhead compared to the previous methods, and it returns an array for traversal, which is more efficient at runtime.

CacheTo lets you group nodes together by a base type or interface, so you can declare functionality based on a shared type rather than the concrete node type.

{
public Type Type;
public bool CacheAsBoth = true;

public CacheToAttribute(Type type, bool cacheAsBoth = true)
{
Type = type;
CacheAsBoth = cacheAsBoth;
}
}
}
Loading