微信扫一扫 分享朋友圈

已有 1566 人浏览分享

开启左侧

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

[复制链接]
1566 3

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

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

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

QQ截图20231119115626.png

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

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

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

QQ截图20231119121940.png

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

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

QQ截图20231119122734.png

打开“分拆视图”脚本 -> 保存QQ截图20231119123406.png

打开“龙哥的秘密花园视图” 保存QQ截图20231119124023.png

QQ截图20231119123716.png

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

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

QQ截图20231119124023.png

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

QQ截图20231119124716.png

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

QQ截图20231119125526.png

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

QQ截图20231119125638.png

QQ截图20231119125814.png

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

QQ截图20231119130226.png

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

QQ截图20231119133524.png

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

QQ截图20231119133739.png

如图QQ截图20231119134118.png

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

QQ录屏20231119134521.mp4

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

QQ截图20231119135440.png

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

QQ录屏20231119135620.mp4

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

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

QQ截图20231119141142.png

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

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;//鼠标在当前目标坐标系中的位置。 } } } }

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

QQ录屏20231119162322.mp4

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

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;//鼠标在当前目标坐标系中的位置。
            }
        }
    }
}

QQ录屏20231119165433.mp4

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

评论 3

lneedy  魔法画童  发表于 2024-5-27 22:58:59 | 显示全部楼层
赚积分
s5341728  咒语学徒  发表于 2024-5-31 17:35:25 | 显示全部楼层
牛逼
雷伊莫  咒语学徒  发表于 2024-6-3 14:56:01 | 显示全部楼层
赚积分
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

精彩推荐
热门资讯
网友晒图
图文推荐
  • iOS App

  • 安卓App

Archiver|手机版|小黑屋|技你太美101

GMT+8, 2024-11-24 13:10 , Processed in 0.104296 second(s), 32 queries .

Powered by 技你太美101

© 2024 JNTM101 Team