龙哥的秘密花园 发表于 2023-11-19 16:50:00

UI toolkit构建节点编辑器(视图界面)

本帖最后由 龙哥的秘密花园 于 2023-11-19 16:58 编辑

> 本帖最后由 龙哥的秘密花园 于 2023-11-19 16:48 编辑

在“Assets”下创建文件夹“龙哥的秘密花园/图视图”,并创建两个脚本文件起名为“分拆视图”“龙哥的秘密花园视图”

!(data/attachment/forum/202311/19/130830kmhodxxix79z7vmm.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "QQ截图20231119115626.png")

在“图视图”文件夹下鼠标右键“Create/UI toolkit/Style Sheet”创建样式起名为“龙哥的秘密花园样式”

再次创建“Create/UI toolkit/UI Document”创建UXML起名为“龙哥的秘密花园UXML”

在“图视图”文件夹下创建“Editor”并将刚创建的文件放到该文件夹下

!(data/attachment/forum/202311/19/131053cro8bn35g5gtop4w.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "QQ截图20231119121940.png")

在该文件下创建C#脚本起名为“路径”,选中右键“龙哥的秘密花园UXML”点击“Copy Path”粘贴到UXML路径字符串下

再次选中右键“龙哥的秘密花园样式”点击“Copy Path”粘贴到“样式路径”字符串下

!(data/attachment/forum/202311/19/131616gdnwhqh9hz55aohu.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "QQ截图20231119122734.png")

打开“分拆视图”脚本 -> 保存!(data/attachment/forum/202311/19/132007q7zu7okfx2wxiqbk.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "QQ截图20231119123406.png")

打开“龙哥的秘密花园视图” 保存!(data/attachment/forum/202311/19/132211myou7g0vv57n0o77.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "QQ截图20231119124023.png")

!(data/attachment/forum/202311/19/132107i2kg5zcfgjzfwswf.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "QQ截图20231119123716.png")

在“Editor”文件夹下 双击“龙哥的秘密花园UXML”弹出“UI Bulider”刚刚创建的两个脚本“分拆视图”

“龙哥的秘密花园视图”在“Project”下,根据命名空间下类名可以看到

!(data/attachment/forum/202311/19/132242e4zo13ovt18mp8ss.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "QQ截图20231119124023.png")

简单的构建一下编辑器视图在拖拽“分拆视图”时“Console”下会报错,这是因为“分拆视图”需要两个子控件,当添加后就不会报错了

!(data/attachment/forum/202311/19/132553k9p9ul2z9lrsd7lp.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "QQ截图20231119124716.png")

创建C#脚本起名“编辑器窗口”保存

!(data/attachment/forum/202311/19/132644svrrw22hhve2psoe.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "QQ截图20231119125526.png")

在标头中打开“龙哥的秘密花园/编辑器”弹出如下窗口

!(data/attachment/forum/202311/19/132952k9hqbqyhlzhj9jhh.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "QQ截图20231119125638.png")

!(data/attachment/forum/202311/19/132955xeep8pesi8xvxpvu.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "QQ截图20231119125814.png")

打开脚本“龙哥的秘密花园视图”在构造函数中添加一些控制器->保存回到Unity查看“编辑器窗口”

!(data/attachment/forum/202311/19/133134wgkppwpknynkett7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "QQ截图20231119130226.png")

因为背景下无法准确观察到缩放这里将添加网格布局,双击“龙哥的秘密花园样式”保存

!(data/attachment/forum/202311/19/133543hiiyz9dd5g9vc3r0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "QQ截图20231119133524.png")

在“龙哥的秘密花园视图”也添加下面代码 保存回到Unity

!(data/attachment/forum/202311/19/133753zzudwcr8m84xwcg7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "QQ截图20231119133739.png")

如图!(data/attachment/forum/202311/19/134123b5ucbbfb5ji5xl4z.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "QQ截图20231119134118.png")

这里遇到到第一问题:矩形选择器(`RectangleSelector`)的位置似乎和鼠标位置差了一个“分拆视图”下的左边控件距离,这里可以看出来矩形选择器(`RectangleSelector`)是以父的坐标空间下位置。

!(data/attachment/forum/?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "QQ录屏20231119134521.mp4")

解决方法:双击 ”龙哥的秘密花园视图“在“Hierarchy”找到“龙哥的秘密花园视图”,拖拽一个“VisualElement”。将“龙哥的秘密花园视图”添加到“VisualElement”。

!(data/attachment/forum/202311/19/135449t857o768zwqo3zoo.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "QQ截图20231119135440.png")

保存重新打开“编辑器窗口”

!(data/attachment/forum/?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "QQ录屏20231119135620.mp4")

现在该在“龙哥的秘密花园视图”下添加节点了

打开脚本“龙哥的秘密花园视图”

!(data/attachment/forum/202311/19/141159jeef9usgbg98ffkv.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "QQ截图20231119141142.png")

打开“编辑器窗口”在视图中鼠标右键 “创建节点”

!(data/attachment/forum/202311/19/141331eeqzmomyott8ev73.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

当尝试连线时,无法连线, 这是因为没有重新连线规则,打开“龙哥的秘密花园视图”添加如下代码

```
namespace 龙哥的秘密花园.独特
{
    public class 龙哥的秘密花园视图 : GraphView
    {
      public new class UxmlFactory : UxmlFactory<龙哥的秘密花园视图, UxmlTraits>{}

      public 龙哥的秘密花园视图(){
            Insert(0,new GridBackground());
            //增加内容缩放,拖动,拖拽,框选控制器
            this.AddManipulator(new ContentZoomer());
            this.AddManipulator(new ContentDragger());
            this.AddManipulator(new SelectionDragger());
            this.AddManipulator(new RectangleSelector());
            styleSheets.Add(AssetDatabase.LoadAssetAtPath<StyleSheet>(路径.样式路径));
            this.graphViewChanged+=视图改变时;

      }
      /// <summary>
      /// 用于处理移除 节点连接节点的逻辑
      /// </summary>
      /// <param name="graphViewChange"></param>
      /// <returns></returns>
      private GraphViewChange 视图改变时(GraphViewChange graphViewChange)
      {
            if (graphViewChange.elementsToRemove != null) {
                graphViewChange.elementsToRemove.ForEach(elem => {
      
                });
            }
            if (graphViewChange.edgesToCreate != null) {
                graphViewChange.edgesToCreate.ForEach(edge => {
                });
            }

            return graphViewChange;
      }

      public override List<Port> GetCompatiblePorts(Port startPort, NodeAdapter nodeAdapter)
      {
            //第1种写法
                // List<Port> ports1=new();
                // foreach (var item in ports){//遍历所有的端口

                //   //当前端口的方向不能和startPort的方向一致--->Input!=Input Output!=Output
                //   //当前端口的节点不能和startPort的节点一样
                //   if(item.direction!=startPort.direction&&item.node!=startPort.node)
                //   {
                //         ports1.Add(item);   
                //   }
                // }
            //第二种写法:引用System.Linq
            //ports.Where(endport=>endport.direction!=startPort.direction&&endport.node!=startPort.node).ToList();
            return ports.Where(endport=>endport.direction!=startPort.direction&&endport.node!=startPort.node).ToList();
      }
      public override void BuildContextualMenu(ContextualMenuPopulateEvent evt)
      {
            //base.BuildContextualMenu(evt);
            evt.menu.AppendAction("创建节点",创建节点);
      }
      private Port 实例化端口(Node node,Orientation orientation,Direction direction,Port.Capacity capacity)
      {
            return node.InstantiatePort(orientation,direction,capacity,typeof(bool));
      }
      private void 创建节点(DropdownMenuAction action)
      {
            Node node=new Node();
            node.title="节点";
            var 输入= 实例化端口(node,Orientation.Vertical,Direction.Input,Port.Capacity.Single);
            //
            输入.portName="输入";
            var 输出= 实例化端口(node,Orientation.Vertical,Direction.Output,Port.Capacity.Multi);
            //
            输出.portName="输出";
            输出.portName="输出";
            输入.portColor=new Color
            (64/255,224/255f,208/255f,1);
            输出.portColor=new Color
            (64/255,224/255f,208/255f,1);
            node.inputContainer.Add(输入);
            node.outputContainer.Add(输出);

            AddElement(node);
      }
    }
}
````

在“编辑器窗口”中就可以连接了,但是创建的节点视图位置并不是在鼠标位置下的,接下来继续实现节点视图的位置

public class 龙哥的秘密花园视图 : GraphView
{
public new class UxmlFactory : UxmlFactory<龙哥的秘密花园视图, UxmlTraits>{}public 龙哥的秘密花园视图(){
Insert(0,new GridBackground());
//增加内容缩放,拖动,拖拽,框选控制器
this.AddManipulator(new ContentZoomer());
this.AddManipulator(new ContentDragger());
this.AddManipulator(new SelectionDragger());
this.AddManipulator(new RectangleSelector());
styleSheets.Add(AssetDatabase.LoadAssetAtPath(路径.样式路径));
this.graphViewChanged+=视图改变时;}
///
/// 用于处理移除 节点连接节点的逻辑
///
///
///
private GraphViewChange 视图改变时(GraphViewChange graphViewChange)
{
if (graphViewChange.elementsToRemove != null) {
graphViewChange.elementsToRemove.ForEach(elem => {});}
if (graphViewChange.edgesToCreate != null) {
graphViewChange.edgesToCreate.ForEach(edge => {
});
}

return graphViewChange;}public override List<Port> GetCompatiblePorts(Port startPort, NodeAdapter nodeAdapter)
{
//第1种写法
// List<Port> ports1=new();
// foreach (var item in ports){//遍历所有的端口

//   //当前端口的方向不能和startPort的方向一致--->Input!=Input Output!=Output
//   //当前端口的节点不能和startPort的节点一样
//   if(item.direction!=startPort.direction&&item.node!=startPort.node)
//   {
//         ports1.Add(item);
//   }
// }

//第二种写法:引用System.Linq
//ports.Where(endport=>endport.direction!=startPort.direction&&endport.node!=startPort.node).ToList();
return ports.Where(endport=>endport.direction!=startPort.direction&&endport.node!=startPort.node).ToList();
}
public override void BuildContextualMenu(ContextualMenuPopulateEvent evt)
{
base.BuildContextualMenu(evt);
evt.menu.AppendAction("创建节点",创建节点);
}
private Port 实例化端口(Node node,Orientation orientation,Direction direction,Port.Capacity capacity)
{
return node.InstantiatePort(orientation,direction,capacity,typeof(bool));
}
private Vector2 mousepos=Vector2.zero;
private void 创建节点(DropdownMenuAction action)
{
Node node=new Node();
node.title="节点";
var 输入= 实例化端口(node,Orientation.Vertical,Direction.Input,Port.Capacity.Single);
//
输入.portName="输入";
var 输出= 实例化端口(node,Orientation.Vertical,Direction.Output,Port.Capacity.Multi);
//
输出.portName="输出";
输出.portName="输出";
输入.portColor=new Color
(64/255,224/255f,208/255f,1);
输出.portColor=new Color
(64/255,224/255f,208/255f,1);
node.inputContainer.Add(输入);
node.outputContainer.Add(输出);
node.SetPosition(new Rect(mousepos,new Vector2(150,200)));
AddElement(node);
}
/// <summary>
/// 在龙哥的秘密花园视图执行的所有事件都会调用这个方法
/// </summary>
/// <param name="evt"></param>
protected override void ExecuteDefaultAction(EventBase evt)
{
base.ExecuteDefaultAction(evt);
if(evt is MouseDownEvent 点击事件)
{
mousepos=点击事件.localMousePosition;//鼠标在当前目标坐标系中的位置。
}
}
}
}

嘿嘿还有一个问题没有解决,这个视频可以看到当我缩小视图时位置似乎又不对了

!(data/attachment/forum/?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "QQ录屏20231119162322.mp4")

解决方法:需要重新计算当前坐标空间。

!(data/attachment/forum/202311/19/163235iuzp631ddzxy556x.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "QQ截图20231119163224.png")

回到Unity,不管怎么缩放或者移动,都能准确的在鼠标点击的位置生成。

```

namespace 龙哥的秘密花园.独特
{
    public class 龙哥的秘密花园视图 : GraphView
    {
      public new class UxmlFactory : UxmlFactory<龙哥的秘密花园视图, UxmlTraits>{}

      public 龙哥的秘密花园视图(){
            SetupZoom(ContentZoomer.DefaultMinScale,ContentZoomer.DefaultMaxScale);
            Insert(0,new GridBackground());
            //增加内容缩放,拖动,拖拽,框选控制器
            this.AddManipulator(new ContentZoomer());
            this.AddManipulator(new ContentDragger());
            this.AddManipulator(new SelectionDragger());
            this.AddManipulator(new RectangleSelector());
            styleSheets.Add(AssetDatabase.LoadAssetAtPath<StyleSheet>(路径.样式路径));
            this.graphViewChanged+=视图改变时;
   
      }
      /// <summary>
      /// 用于处理移除 节点连接节点的逻辑
      /// </summary>
      /// <param name="graphViewChange"></param>
      /// <returns></returns>
      private GraphViewChange 视图改变时(GraphViewChange graphViewChange)
      {
            if (graphViewChange.elementsToRemove != null) {
                graphViewChange.elementsToRemove.ForEach(elem => {
   
                });
            }
            if (graphViewChange.edgesToCreate != null) {
                graphViewChange.edgesToCreate.ForEach(edge => {
                });
            }

            return graphViewChange;
      }

      public override List<Port> GetCompatiblePorts(Port startPort, NodeAdapter nodeAdapter)
      {
            //第1种写法
                // List<Port> ports1=new();
                // foreach (var item in ports){//遍历所有的端口

                //   //当前端口的方向不能和startPort的方向一致--->Input!=Input Output!=Output
                //   //当前端口的节点不能和startPort的节点一样
                //   if(item.direction!=startPort.direction&&item.node!=startPort.node)
                //   {
                //         ports1.Add(item);   
                //   }
                // }
            //第二种写法:引用System.Linq
            //ports.Where(endport=>endport.direction!=startPort.direction&&endport.node!=startPort.node).ToList();
            return ports.Where(endport=>endport.direction!=startPort.direction&&endport.node!=startPort.node).ToList();
      }
      public override void BuildContextualMenu(ContextualMenuPopulateEvent evt)
      {
            base.BuildContextualMenu(evt);
            evt.menu.AppendAction("创建节点",(a)=>创建节点(a));
      }
      private Port 实例化端口(Node node,Orientation orientation,Direction direction,Port.Capacity capacity)
      {
            return node.InstantiatePort(orientation,direction,capacity,typeof(bool));
      }
      private Vector2 mousepos=Vector2.zero;
      private void 创建节点(DropdownMenuAction action)
      {
            Node node=new Node();
            node.title="节点";
            var 输入= 实例化端口(node,Orientation.Vertical,Direction.Input,Port.Capacity.Single);
            //
            输入.portName="输入";
            var 输出= 实例化端口(node,Orientation.Vertical,Direction.Output,Port.Capacity.Multi);
            //
            输出.portName="输出";
            输出.portName="输出";
            输入.portColor=new Color
            (64/255,224/255f,208/255f,1);
            输出.portColor=new Color
            (64/255,224/255f,208/255f,1);
            node.inputContainer.Add(输入);
            node.outputContainer.Add(输出);
            //mousepos= this.ChangeCoordinatesTo(contentViewContainer,mousepos);//或者使用这个拓展方法
            mousepos= contentViewContainer.WorldToLocal(this.LocalToWorld(mousepos));//先将鼠标位置转换到“龙哥的秘密花园视图”的世界空间
            //然后将鼠标在“龙哥的秘密花园视图”的世界空间转换到“contentViewContainer”的本地坐标空间
            node.SetPosition(new Rect(mousepos,new Vector2(150,200)));
            AddElement(node);
      }
      /// <summary>
      /// 在龙哥的秘密花园视图执行的所有事件都会调用这个方法
      /// </summary>
      /// <param name="evt"></param>
      protected override void ExecuteDefaultAction(EventBase evt)
      {
            base.ExecuteDefaultAction(evt);
            if(evt is MouseDownEvent 点击事件)
            {
                mousepos=点击事件.localMousePosition;//鼠标在当前目标坐标系中的位置。
            }
      }
    }
}
```

!(data/attachment/forum/?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "QQ录屏20231119165433.mp4")

lneedy 发表于 2024-5-27 22:58:59

赚积分

s5341728 发表于 2024-5-31 17:35:25

牛逼

雷伊莫 发表于 2024-6-3 14:56:01

赚积分
页: [1]
查看完整版本: UI toolkit构建节点编辑器(视图界面)