Home

Awesome

NodeGraphProcessor + Odin

Node graph editor framework focused on data processing using Unity UIElements, GraphView and C# 4.7

这个项目是本人NodeGraphProcessor接入Odin序列化的示例。本仓库将持续跟进更新。 目前已同步更新至 https://github.com/alelievr/NodeGraphProcessor/commit/efd190c1d24ff785ba75183fd88a501a3e941c7e

NodeGraphProcessor版本:https://github.com/alelievr/NodeGraphProcessor/

Odin插件官网地址:https://odininspector.com/ (使用本仓库需要自行购买Odin然后导入至Plugins文件夹下)

相关博客:https://www.lfzxb.top/nodegraphprocesssor-and-odin/

功能

基于此项目的拓展内容

技能编辑器

基于行为树的MOBA技能系统(国内码云) 基于行为树的MOBA技能系统(国外GitHub,暂未同步更新)

截图

在GraphView选中节点后,由Odin在Inspector面板显示节点信息

端口自定义图标演示

选中Graph Asset文件后,由Odin在Inspector面板显示整个Graph信息

SkillGraph提供了预览节点数据流向示例

使用

节点端口值的获取与操作

由于我们重写了这部分的代码来达到值传递0GC的目的,所以那些原本由框架通过反射提供的节点之间值传递需要我们自己手动来做,这部分工作主要为

正常的出端口和入端口值获取

[System.Serializable, NodeMenuItem("Conditional/Switch")]
public class SwitchNode : BaseNode
{
	[Input(name = "In")]
    	public float                input;

	[Output(name = "Out")]
	public float				output;

	public override string		name => "Switch";
	
	protected override void Process()
	{
        	//获取入端口的值,第一个参数为要获取值的字段名称,推荐使用nameof
        	//第二个参数是要获取值的字段,使用ref传递方便进行修改
		TryGetInputValue(nameof(input), ref input);
		output = output = input * 42;
	}
	
    	/// <summary>
	/// 获取出端口的值
	/// </summary>
	/// <param name="outputPort">output端口</param>
	/// <param name="inputPort">与上面的output端口相连的input端口</param>
	/// <param name="value">要返回的值</param>
	/// <typeparam name="T"></typeparam>
	public override void TryGetOutputValue<T>(NodePort outputPort, NodePort inputPort, ref T value)
	{
		if (output is T finalValue)
		{
			value = finalValue;
		}
	}
}

单个入端口连接了多个出端口

[System.Serializable, NodeMenuItem("Custom/MultiAdd")]
public class MultiAddNode : BaseNode
{
	[Input]
	public IEnumerable< float >	inputs = null;

	[Output]
	public float				output;

	public override string		name => "Add";

	protected override void Process()
	{
		output = 0;
		inputs = TryGetAllInputValues<float>(nameof(inputs));
		if (inputs == null)
			return ;

		foreach (float input in inputs)
			output += input;
	}

	public override void TryGetOutputValue<T>(NodePort outputPort, NodePort inputPort, ref T value)
	{
		if (output is T finalValue)
		{
			value = finalValue;
		}
	}

	[CustomPortBehavior(nameof(inputs))]
	IEnumerable< PortData > GetPortsForInputs(List< SerializableEdge > edges)
	{
		yield return new PortData{ displayName = "In ", displayType = typeof(float), acceptMultipleEdges = true};
	}
}

出端口的自定义处理

[System.Serializable, NodeMenuItem("Custom/CircleRadians")]
public class CircleRadians : BaseNode
{
	[Output(name = "In")]
	public List< float >		outputRadians;

	public override string		name => "CircleRadians";

	public override void TryGetOutputValue<T>(NodePort outputPort, NodePort inputPort, ref T value)
	{
        	//因为此节点会根据出端口所连接的多个入端口顺序为入端口分配值,所以需要判断传进来的inputPort的Index
		int inputPortIndexInOutputPortEdge = outputPort.GetEdges().FindIndex(edge => edge.inputPort == inputPort);
		if (outputRadians[inputPortIndexInOutputPortEdge] is T finalValue)
		{
			value = finalValue;
		}
	}
}

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GraphProcessor;
using System.Linq;

[System.Serializable, NodeMenuItem("Custom/Inheritance2")]
public class Inheritance2 : Inheritance1
{
    [Input(name = "In 2")] public float input2;

    [Output(name = "Out 2")] public float output2;

    public float field2;

    public override string name => "Inheritance2";

    protected override void Process()
    {
        base.Process();
        TryGetInputValue(nameof(input2), ref input2);
        output2 = input2 * 44;
    }
	
    public override void TryGetOutputValue<T>(NodePort outputPort, NodePort inputPort, ref T value)
    {
        //因为此节点有多个输出端口,所以需要判断所需要的数据是哪个端口
        switch (outputPort.fieldName)
        {
            case nameof(output1):
                if (output1 is T finalValue1)
                {
                    value = finalValue1;
                }
                break;
            case nameof(output2):
                if (output2 is T finalValue2)
                {
                    value = finalValue2;
                }
                break;
        }
    }
}

更多示例可见仓库中的 https://github.com/wqaetly/NodeGraphProcessor/tree/master/Assets/Plugins/Examples/DefaultNodes/Nodes

TODO