1
0
mirror of https://github.com/Siccity/xNode.git synced 2025-12-21 01:36:03 +08:00

Merge branch 'master' of https://github.com/Siccity/xNode into examples

This commit is contained in:
Thor Brigsted 2018-03-23 11:57:34 +01:00
commit becd710fe0
15 changed files with 209 additions and 34 deletions

23
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,23 @@
## Contributing to xNode
💙Thank you for taking the time to contribute💙
If you haven't already, join our [Discord channel](https://discord.gg/qgPrHv4)!
## Pull Requests
Try to keep your pull requests relevant, neat, and manageable. If you are adding multiple features, try splitting them into separate commits.
* Avoid including irellevant whitespace or formatting changes.
* Comment your code.
* Spell check your code / comments
## New features
xNode aims to be simple and extendible, not trying to fix all of Unity's shortcomings.
If your feature aims to cover something not related to editing nodes, it generally won't be accepted. If in doubt, ask on the Discord channel.
## Coding conventions
Skim through the code and you'll get the hang of it quickly.
* Methods, Types and properties PascalCase
* Variables camelCase
* Public methods XML commented
* Open braces on same line as condition
* 4 spaces for indentation.

View File

@ -3,7 +3,7 @@
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/Siccity/xNode/master/LICENSE.md)
[![GitHub Wiki](https://img.shields.io/badge/wiki-available-brightgreen.svg)](https://github.com/Siccity/xNode/wiki)
[Go to Downloads](https://github.com/Siccity/xNode/releases) / [Go to Asset Store](http://u3d.as/108S)
[Downloads](https://github.com/Siccity/xNode/releases) / [Asset Store](http://u3d.as/108S) / [Documentation](https://github.com/Siccity/xNode/wiki)
### xNode
Thinking of developing a node-based plugin? Then this is for you. You can download it as an archive and unpack to a new unity project, or connect it as git submodule.
@ -20,7 +20,6 @@ With a minimal footprint, it is ideal as a base for custom state machines, dialo
* No runtime reflection (unless you need to edit/build node graphs at runtime. In this case, all reflection is cached.)
* Does not rely on any 3rd party plugins
* Custom node inspector code is very similar to regular custom inspector code
* For a full list of features, see [this page](https://github.com/Siccity/xNode/wiki/Full-features-list)
### Node example:
```csharp

View File

@ -140,8 +140,12 @@ namespace XNodeEditor {
} else if (!IsHoveringNode) {
// If click outside node, release field focus
if (!isPanning) {
// I've got no idea which of these do what, so we'll just reset all of it.
GUIUtility.hotControl = 0;
GUIUtility.keyboardControl = 0;
EditorGUIUtility.editingTextField = false;
EditorGUIUtility.keyboardControl = 0;
EditorGUIUtility.hotControl = 0;
}
AssetDatabase.SaveAssets();
}
@ -166,8 +170,12 @@ namespace XNodeEditor {
}
break;
case EventType.KeyDown:
if (e.keyCode == KeyCode.Delete) RemoveSelectedNodes();
else if (e.keyCode == KeyCode.D && e.control) DublicateSelectedNodes();
if (EditorGUIUtility.editingTextField) break;
else if (e.keyCode == KeyCode.F) Home();
break;
case EventType.ValidateCommand:
if (e.commandName == "SoftDelete") RemoveSelectedNodes();
else if (e.commandName == "Duplicate") DublicateSelectedNodes();
Repaint();
break;
case EventType.Ignore:

View File

@ -10,7 +10,6 @@ namespace XNodeEditor.Internal {
public class NodeEditorBase<T, A, K> where A : Attribute, NodeEditorBase<T, A, K>.INodeEditorAttrib where T : NodeEditorBase<T,A,K> where K : ScriptableObject {
/// <summary> Custom editors defined with [CustomNodeEditor] </summary>
private static Dictionary<Type, T> editors;
private static Dictionary<ScriptableObject, SerializedObject> serializeds;
public K target;
public SerializedObject serializedObject;
@ -19,17 +18,10 @@ namespace XNodeEditor.Internal {
Type type = target.GetType();
T editor = GetEditor(type);
editor.target = target;
editor.serializedObject = GetSerialized(target);
editor.serializedObject = new SerializedObject(target);
return editor;
}
private static SerializedObject GetSerialized(K target) {
if (target == null) return null;
if (serializeds == null) serializeds = new Dictionary<ScriptableObject, SerializedObject>();
if (!serializeds.ContainsKey(target)) serializeds.Add(target, new SerializedObject(target));
return serializeds[target];
}
private static T GetEditor(Type type) {
if (type == null) return null;
if (editors == null) CacheCustomEditors();

View File

@ -364,8 +364,8 @@ namespace XNodeEditor {
GUIContent content = new GUIContent();
content.text = type.PrettyName();
if (hoveredPort.IsStatic && hoveredPort.IsOutput) {
object obj = ObjectFromFieldName(hoveredPort.node, hoveredPort.fieldName);
if (obj != null) content.text += " = " + obj.ToString();
object obj = hoveredPort.node.GetValue(hoveredPort);
content.text += " = " + (obj != null ? obj.ToString() : "null");
}
Vector2 size = NodeEditorResources.styles.tooltip.CalcSize(content);
Rect rect = new Rect(Event.current.mousePosition - (size), size);

View File

@ -70,13 +70,13 @@ namespace XNodeEditor {
switch (showBacking) {
case XNode.Node.ShowBackingValue.Unconnected:
// Display a label if port is connected
if (port.IsConnected) EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName), NodeEditorResources.styles.outputPort, GUILayout.MinWidth(30));
if (port.IsConnected) EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName), NodeEditorResources.OutputPort, GUILayout.MinWidth(30));
// Display an editable property field if port is not connected
else EditorGUILayout.PropertyField(property, label, includeChildren, GUILayout.MinWidth(30));
break;
case XNode.Node.ShowBackingValue.Never:
// Display a label
EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName), NodeEditorResources.styles.outputPort, GUILayout.MinWidth(30));
EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName), NodeEditorResources.OutputPort, GUILayout.MinWidth(30));
break;
case XNode.Node.ShowBackingValue.Always:
// Display an editable property field

View File

@ -1,4 +1,5 @@
using UnityEngine;
using UnityEditor;
using UnityEngine;
namespace XNodeEditor {
public static class NodeEditorResources {
@ -15,9 +16,9 @@ namespace XNodeEditor {
// Styles
public static Styles styles { get { return _styles != null ? _styles : _styles = new Styles(); } }
public static Styles _styles = null;
public static GUIStyle OutputPort { get { return new GUIStyle(EditorStyles.label) { alignment = TextAnchor.UpperRight }; } }
public class Styles {
public GUIStyle inputPort, outputPort, nodeHeader, nodeBody, tooltip, nodeHighlight;
public GUIStyle inputPort, nodeHeader, nodeBody, tooltip, nodeHighlight;
public Styles() {
GUIStyle baseStyle = new GUIStyle("Label");
@ -27,10 +28,6 @@ namespace XNodeEditor {
inputPort.alignment = TextAnchor.UpperLeft;
inputPort.padding.left = 10;
outputPort = new GUIStyle(baseStyle);
outputPort.alignment = TextAnchor.UpperRight;
outputPort.padding.right = 10;
nodeHeader = new GUIStyle();
nodeHeader.alignment = TextAnchor.MiddleCenter;
nodeHeader.fontStyle = FontStyle.Bold;

View File

@ -1,12 +1,20 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
namespace XNodeEditor {
/// <summary> A set of editor-only utilities and extensions for UnityNodeEditorBase </summary>
public static class NodeEditorUtilities {
/// <summary>C#'s Script Icon [The one MonoBhevaiour Scripts have].</summary>
private static Texture2D scriptIcon = (EditorGUIUtility.IconContent("cs Script Icon").image as Texture2D);
public static bool GetAttrib<T>(Type classType, out T attribOut) where T : Attribute {
object[] attribs = classType.GetCustomAttributes(typeof(T), false);
return GetAttrib(attribs, out attribOut);
@ -85,5 +93,86 @@ namespace XNodeEditor {
}
} else return type.ToString();
}
/// <summary>Creates a new C# Class.</summary>
[MenuItem("Assets/Create/xNode/Node C# Script", false, 89)]
private static void CreateNode() {
string[] guids = AssetDatabase.FindAssets("xNode_NodeTemplate.cs");
if (guids.Length == 0) {
Debug.LogWarning("xNode_NodeTemplate.cs.txt not found in asset database");
return;
}
string path = AssetDatabase.GUIDToAssetPath(guids[0]);
CreateFromTemplate(
"NewNode.cs",
path
);
}
/// <summary>Creates a new C# Class.</summary>
[MenuItem("Assets/Create/xNode/NodeGraph C# Script", false, 89)]
private static void CreateGraph() {
string[] guids = AssetDatabase.FindAssets("xNode_NodeGraphTemplate.cs");
if (guids.Length == 0) {
Debug.LogWarning("xNode_NodeGraphTemplate.cs.txt not found in asset database");
return;
}
string path = AssetDatabase.GUIDToAssetPath(guids[0]);
CreateFromTemplate(
"NewNodeGraph.cs",
path
);
}
public static void CreateFromTemplate(string initialName, string templatePath) {
ProjectWindowUtil.StartNameEditingIfProjectWindowExists(
0,
ScriptableObject.CreateInstance<DoCreateCodeFile>(),
initialName,
scriptIcon,
templatePath
);
}
/// Inherits from EndNameAction, must override EndNameAction.Action
public class DoCreateCodeFile : UnityEditor.ProjectWindowCallback.EndNameEditAction {
public override void Action(int instanceId, string pathName, string resourceFile) {
Object o = CreateScript(pathName, resourceFile);
ProjectWindowUtil.ShowCreatedAsset(o);
}
}
/// <summary>Creates Script from Template's path.</summary>
internal static UnityEngine.Object CreateScript(string pathName, string templatePath) {
string className = Path.GetFileNameWithoutExtension(pathName).Replace(" ", string.Empty);
string templateText = string.Empty;
UTF8Encoding encoding = new UTF8Encoding(true, false);
if (File.Exists(templatePath)) {
/// Read procedures.
StreamReader reader = new StreamReader(templatePath);
templateText = reader.ReadToEnd();
reader.Close();
templateText = templateText.Replace("#SCRIPTNAME#", className);
templateText = templateText.Replace("#NOTRIM#", string.Empty);
/// You can replace as many tags you make on your templates, just repeat Replace function
/// e.g.:
/// templateText = templateText.Replace("#NEWTAG#", "MyText");
/// Write procedures.
StreamWriter writer = new StreamWriter(Path.GetFullPath(pathName), false, encoding);
writer.Write(templateText);
writer.Close();
AssetDatabase.ImportAsset(pathName);
return AssetDatabase.LoadAssetAtPath(pathName, typeof(Object));
} else {
Debug.LogError(string.Format("The template file was not found: {0}", templatePath));
return null;
}
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 86b677955452bb5449f9f4dd47b6ddfe
folderAsset: yes
timeCreated: 1519049391
licenseType: Free
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XNode;
[CreateAssetMenu]
public class #SCRIPTNAME# : NodeGraph {
#NOTRIM#
}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 8165767f64da7d94e925f61a38da668c
timeCreated: 1519049802
licenseType: Free
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XNode;
public class #SCRIPTNAME# : Node {
// Use this for initialization
protected override void Init() {
base.Init();
#NOTRIM#
}
// Return the correct value of an output port when requested
public override object GetValue(NodePort port) {
return null; // Replace this
}
}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 85f6f570600a1a44d8e734cb111a8b89
timeCreated: 1519049802
licenseType: Free
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -202,6 +202,10 @@ namespace XNode {
foreach (NodePort port in Ports) port.ClearConnections();
}
public override int GetHashCode() {
return JsonUtility.ToJson(this).GetHashCode();
}
/// <summary> Mark a serializable field as an input port. You can access this through <see cref="GetInputPort(string)"/> </summary>
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
public class InputAttribute : Attribute {

View File

@ -9,8 +9,15 @@ namespace XNode {
public enum IO { Input, Output }
public int ConnectionCount { get { return connections.Count; } }
/// <summary> Return the first connection </summary>
public NodePort Connection { get { return connections.Count > 0 ? connections[0].Port : null; } }
/// <summary> Return the first non-null connection </summary>
public NodePort Connection {
get {
for (int i = 0; i < connections.Count; i++) {
if (connections[i] != null) return connections[i].Port;
}
return null;
}
}
public IO direction { get { return _direction; } }
public Node.ConnectionType connectionType { get { return _connectionType; } }
@ -54,8 +61,7 @@ namespace XNode {
if (attribs[i] is Node.InputAttribute) {
_direction = IO.Input;
_connectionType = (attribs[i] as Node.InputAttribute).connectionType;
}
else if (attribs[i] is Node.OutputAttribute) {
} else if (attribs[i] is Node.OutputAttribute) {
_direction = IO.Output;
_connectionType = (attribs[i] as Node.OutputAttribute).connectionType;
}
@ -79,7 +85,7 @@ namespace XNode {
_direction = direction;
_node = node;
_dynamic = true;
_connectionType = connectionType;
_connectionType = connectionType;
}
/// <summary> Checks all connections for invalid references, and removes them. </summary>
@ -225,15 +231,17 @@ namespace XNode {
connections.RemoveAt(i);
}
}
// Remove the other ports connection to this port
for (int i = 0; i < port.connections.Count; i++) {
if (port.connections[i].Port == this) {
port.connections.RemoveAt(i);
if (port != null) {
// Remove the other ports connection to this port
for (int i = 0; i < port.connections.Count; i++) {
if (port.connections[i].Port == this) {
port.connections.RemoveAt(i);
}
}
}
// Trigger OnRemoveConnection
node.OnRemoveConnection(this);
port.node.OnRemoveConnection(port);
if (port != null) port.node.OnRemoveConnection(port);
}
public void ClearConnections() {