栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 游戏开发 > 其他

Unity Editor扩展 GraphView

其他 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Unity Editor扩展 GraphView

GraphView介绍

GraphView是Unity推出的一个基于UIElement的节点编辑器UI模块,功能很完全,有多选,拖动,缩放,Group等功能。

GraphView的基本使用(一)

图形视图由四个主要元素组成。

  • GraphView
  • Node
  • Port
  • Edge

创建编辑器窗口

首先,创建编辑器扩展熟悉的编辑器窗口。

using UnityEditor;

public class SampleGraphEditorWindow : EditorWindow
{
    [MenuItem("Window/Open SampleGraphView")]
    public static void Open()
    {
        GetWindow("SampleGraphView");
    }
}

创建图形视图

接下来,我们将立即创建图形视图,这是节点和边的父级。

using UnityEditor.Experimental.GraphView;

public class SampleGraphView : GraphView
{
}

此外,在此处向编辑器窗口SampleGraphEditorWindow.cs添加图形视图。

    void OnEnable()
    {
        rootVisualElement.Add(new SampleGraphView());
    }

创建节点

尽管 GraphView 通常称为节点编辑器,但Node 顾名思义,它是节点编辑器中最重要的部分。

创建基础节点,在实际使用时继承此节点。

using UnityEditor.Experimental.GraphView;

public class SampleNode : Node
{
}

在构造函数中完成将此节点添加到图形视图。

    public SampleGraphView() : base()
    {
        AddElement(new SampleNode());
    }

设置高度,使其正确显示。

    private void OnEnable()
    {
        rootVisualElement.Add(new SampleGraphView()
        {
          style  = { flexGrow = 1}
        });
    }

可以看到显示窗口中已经显示出节点了。

为节点添加端口

节点可以与其他节点连接,可以将边从输出端口连接到输入端口。

    public SampleNode()
    {
        title = "Sample";

        var inputPort = Port.Create(Orientation.Horizontal, Direction.Input, Port.Capacity.Single, typeof(Port));
        inputContainer.Add(inputPort);

        var outputPort = Port.Create(Orientation.Horizontal, Direction.Output, Port.Capacity.Single, typeof(Port));
        outputContainer.Add(outputPort);
    }

使节点能够移动

使用SelectionDragger做AddManipulator。

    public SampleGraphView() : base()
    {
        AddElement(new SampleNode());
        this.AddManipulator(new SelectionDragger());
    }

创建多个节点

可以从右键单击菜单中添加新节点。

    public SampleGraphView() : base()
    {
        this.AddManipulator(new SelectionDragger());

        nodeCreationRequest += context =>
        {
            AddElement(new SampleNode());
        };
    }

连接节点

在SampleGraphView中重写GetCompatiblePorts并返回正确的端口。

    public override List GetCompatiblePorts(Port startAnchor, NodeAdapter nodeAdapter)
    {
        return ports.ToList();
    }

 可以看到已经能连接节点了。

放大和缩小

只需在图形视图中调用SetupZoom即可放大和缩小。

    public SampleGraphView() : base()
    {
        SetupZoom(ContentZoomer.DefaultMinScale, ContentZoomer.DefaultMaxScale);

        this.AddManipulator(new SelectionDragger());

        nodeCreationRequest += context =>
        {
            AddElement(new SampleNode());
        };
    }

更改背景颜色

因为网格背景出现在图形视图的元素之前,所以需要在添加视图显示元素之前调用Insert方法。

    public SampleGraphView() : base()
    {
        SetupZoom(ContentZoomer.DefaultMinScale, ContentZoomer.DefaultMaxScale);

        Insert(0, new GridBackground());

        this.AddManipulator(new SelectionDragger());

        nodeCreationRequest += context =>
        {
            AddElement(new SampleNode());
        };
    }

实现节点

创建派生自基类节点的具体业务节点。创建如下节点为:

  • 根节点
  • 日志记录节点
  • 字符串输出节点

现在,我们将示例节点作为抽象类,并且继承它。

using UnityEditor.Experimental.GraphView;

public abstract class SampleNode : Node
{
}

首先,创建日志处理节点。

using UnityEditor.Experimental.GraphView;

public class ProcessNode : SampleNode
{
    public ProcessNode()
    {
        var inputPort = Port.Create(Orientation.Horizontal, Direction.Input, Port.Capacity.Single, typeof(Port));
        inputPort.portName = "In";
        inputContainer.Add(inputPort);

        var outputPort = Port.Create(Orientation.Horizontal, Direction.Output, Port.Capacity.Single, typeof(Port));
        outputPort.portName = "Out";
        outputContainer.Add(outputPort);
    }
}

继承此节点创建日志节点。

using UnityEditor.Experimental.GraphView;

public class LogNode : ProcessNode
{
    public LogNode() : base()
    {
        title = "Log";

        var inputPort = Port.Create(Orientation.Horizontal, Direction.Input, Port.Capacity.Single, typeof(string));
        inputContainer.Add(inputPort);
    }
}

创建字符串输出节点。

using UnityEngine.UIElements;
using UnityEditor.Experimental.GraphView;

public class StringNode : SampleNode
{
    private TextField textField;
    public string Text { get { return textField.value; } }

    public StringNode() : base()
    {
        title = "String";

        var outputPort = Port.Create(Orientation.Horizontal, Direction.Output, Port.Capacity.Multi, typeof(string));
        outputContainer.Add(outputPort);

        textField = new TextField();
        mainContainer.Add(textField);
    }
}

最后,创建根节点。

由于根节点不消失,请消除相应的委托。

using UnityEditor.Experimental.GraphView;

public class RootNode : SampleNode
{
    public RootNode() : base()
    {
        title = "Root";

        capabilities -= Capabilities.Deletable;

        var outputPort = Port.Create(Orientation.Horizontal, Direction.Output, Port.Capacity.Single, typeof(Port));
        outputPort.portName = "Out";
        outputContainer.Add(outputPort);
    }
}

在生成图形视图时,就立刻放置一个根节点。

root = new RootNode();
AddElement(root);

选择并创建任何节点

使用搜索窗口,可以轻松地创建允许选择节点的 UI。
 

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor.Experimental.GraphView;

public class SampleSearchWindowProvider : ScriptableObject, ISearchWindowProvider
{
    private SampleGraphView graphView;

    public void Initialize(SampleGraphView graphView)
    {
        this.graphView = graphView;
    }

    List ISearchWindowProvider.CreateSearchTree(SearchWindowContext context)
    {
        var entries = new List();
        entries.Add(new SearchTreeGroupEntry(new GUIContent("Create Node")));

        foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
        {
            foreach (var type in assembly.GetTypes())
            {
                if (type.IsClass && !type.IsAbstract && (type.IsSubclassOf(typeof(SampleNode)))
                    && type != typeof(RootNode))
                {
                    entries.Add(new SearchTreeEntry(new GUIContent(type.Name)) { level = 1, userData = type });
                }
            }
        }

        return entries;
    }

    bool ISearchWindowProvider.OnSelectEntry(SearchTreeEntry searchTreeEntry, SearchWindowContext context)
    {
        var type = searchTreeEntry.userData as System.Type;
        var node = Activator.CreateInstance(type) as SampleNode;
        graphView.AddElement(node);
        return true;
    }
}

创建搜索树将继承示例节点类的类返回到搜索树条目,并处理OnSelectEntry 中选择的项。
右键单击更改您创建节点的位置,以调用搜索窗口。

using System.Collections.Generic;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
using UnityEngine.UIElements;

public class SampleGraphView : GraphView
{
    public RootNode root;
    
    public SampleGraphView()
    {
        SetupZoom(ContentZoomer.DefaultMinScale, ContentZoomer.DefaultMaxScale);

        Insert(0, new GridBackground());
        
        //AddElement(new SampleNode());
        this.AddManipulator(new SelectionDragger());
        
        var searchWindowProvider = ScriptableObject.CreateInstance();
        searchWindowProvider.Initialize(this);
        
        root = new RootNode();
        AddElement(root);

        nodeCreationRequest += context =>
        {
            SearchWindow.Open(new SearchWindowContext(context.screenMousePosition), searchWindowProvider);
        };
    }

    public override List GetCompatiblePorts(Port startPort, NodeAdapter nodeAdapter)
    {
        return ports.ToList();
    }
}

现在,就可以选择和创建任何节点。

确保只有正确的端口才能彼此连接

由于 UI 实现可以连接所有节点,因此我们将修复它。

    public override List GetCompatiblePorts(Port startAnchor, NodeAdapter nodeAdapter)
    {
        var compatiblePorts = new List();
        foreach (var port in ports.ToList())
        {
            if (startAnchor.node == port.node ||
                startAnchor.direction == port.direction ||
                startAnchor.portType != port.portType)
            {
                continue;
            }

            compatiblePorts.Add(port);
        }
        return compatiblePorts;
    }

现在

  • 无法连接到同一节点
  • 从输入到输入,从输出到输出,不连接
  • 端口上配置的类型不匹配,无法连接

实际使用节点进行处理

实际处理时,必须按顺序获取连接到根节点的节点并执行操作。
由于没有获取连接到节点的另一个节点的功能,因此在生成端口时必须缓存它。
此外,由于我们希望在节点端描述处理,因此我们将让进程节点具有用于处理的方法。

using UnityEditor.Experimental.GraphView;

public abstract class ProcessNode : SampleNode
{
    public Port InputPort;
    public Port OutputPort;

    public ProcessNode()
    {
        InputPort = Port.Create(Orientation.Horizontal, Direction.Input, Port.Capacity.Single, typeof(Port));
        InputPort.portName = "In";
        inputContainer.Add(InputPort);

        OutputPort = Port.Create(Orientation.Horizontal, Direction.Output, Port.Capacity.Single, typeof(Port));
        OutputPort.portName = "Out";
        outputContainer.Add(OutputPort);
    }

    public abstract void Execute();
}
using System.Linq;
using UnityEngine;
using UnityEditor.Experimental.GraphView;

public class LogNode : ProcessNode
{
    private Port inputString;

    public LogNode() : base()
    {
        title = "Log";

        inputString = Port.Create(Orientation.Horizontal, Direction.Input, Port.Capacity.Single, typeof(string));
        inputContainer.Add(inputString);
    }

    public override void Execute()
    {
        var edge = inputString.connections.FirstOrDefault();
        var node = edge.output.node as StringNode;

        if (node == null) return;

        Debug.Log(node.Text);
    }
}
using UnityEditor.Experimental.GraphView;

public class RootNode : SampleNode
{
    public Port OutputPort;

    public RootNode() : base()
    {
        title = "Root";

        capabilities -= Capabilities.Deletable;

        OutputPort = Port.Create(Orientation.Horizontal, Direction.Output, Port.Capacity.Single, typeof(Port));
        OutputPort.portName = "Out";
        outputContainer.Add(OutputPort);
    }
}

现在,我们从根开始获取节点并执行操作。

using System.Linq

-------------------------------------

    public void Execute()
    {
        var rootEdge = root.OutputPort.connections.FirstOrDefault();
        if (rootEdge == null) return;

        var currentNode = rootEdge.input.node as ProcessNode;

        while (true)
        {
            currentNode.Execute();

            var edge = currentNode.OutputPort.connections.FirstOrDefault();
            if (edge == null) break;

            currentNode = edge.input.node as ProcessNode;
        }
    }

绘制一个按钮,执行此方法。

    void OnEnable()
    {
        var graphView = new SampleGraphView()
        {
            style = { flexGrow = 1 }
        };
        rootVisualElement.Add(graphView);

        rootVisualElement.Add(new Button(graphView.Execute) { text = "Execute" });
    }

执行结果如下:

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/906152.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号