Unity3D 基于GraphView实现的节点编辑器框架详解

前言

在Unity3D游戏开发中,节点编辑器是一种强大的工具,它允许开发者以可视化的方式创建和编辑复杂的逻辑和流程。Unity提供了一个强大的UI工具包——GraphView,它使得创建自定义节点编辑器变得相对简单。本文将详细介绍如何使用GraphView实现一个节点编辑器框架,并提供技术详解和代码实现。

对惹,这里有一个游戏开发交流小组,希望大家可以点击进来一起交流一下开发经验呀!

一、GraphView简介

GraphView是Unity提供的一个用于创建节点编辑器的UI组件。它允许开发者以图形化的方式展示和编辑节点及其连接。GraphView提供了丰富的API,使得开发者可以轻松地自定义节点、边、面板和工具栏等。

二、节点编辑器框架设计

在创建一个节点编辑器框架时,我们需要考虑以下几个关键部分:

  1. 节点(Node):节点是编辑器中的基本元素,它代表了一个可以执行特定操作的单元。每个节点都应该有一个唯一的标识符、一个标题、一个或多个输入/输出端口,以及用于显示和操作节点的UI元素。
  2. 边(Edge):边用于连接节点,表示节点之间的数据流或逻辑依赖关系。在GraphView中,边通常由两个端口(一个输入端口和一个输出端口)组成。
  3. 面板(Panel):面板是节点的容器,它提供了用于添加、删除和移动节点的界面。面板还可以包含工具栏、小地图等辅助工具。
  4. 工具栏(Toolbar):工具栏提供了用于创建新节点、保存和加载编辑器状态、撤销和重做操作等功能的按钮和菜单。
  5. 数据存储:为了持久化编辑器状态,我们需要将节点的数据和连接关系存储在一个可序列化的数据结构中。在Unity中,ScriptableObject是一个常用的选择。

三、技术详解

  1. 创建节点和边
  • 节点可以通过继承GraphView的Node类来创建。在节点类中,我们需要重写BuildContextualMenu方法来添加右键菜单项,如添加输入/输出端口、删除节点等。
  • 边可以通过GraphView的Edge类来创建。在创建边时,我们需要指定边的输入和输出端口,并处理边的绘制和连接逻辑。

  1. 管理节点和边的数据
  • 我们可以使用ScriptableObject来存储节点的数据和连接关系。每个节点可以有一个对应的ScriptableObject来存储其特定的数据。
  • 连接关系可以通过存储边的输入和输出端口的标识符来表示。

  1. 实现撤销和重做功能
  • 撤销和重做功能可以通过维护一个操作历史记录来实现。每次对编辑器进行更改时,都可以将更改作为一个操作添加到历史记录中。
  • 撤销操作可以回滚到历史记录中的上一个状态,重做操作可以恢复到下一个状态。

  1. 实现保存和加载功能
  • 保存功能可以将编辑器的当前状态序列化为一个文件或字符串,并保存到磁盘上。
  • 加载功能可以从磁盘上读取文件或字符串,并将其反序列化为编辑器的状态。

四、代码实现

以下是一个简单的节点编辑器框架的代码实现示例:

using UnityEngine;
using UnityEditor;
using UnityEngine.UIElements;
using UnityEditor.UIElements;
// 定义一个用于存储节点数据的ScriptableObject
[CreateAssetMenu(fileName = "NewNodeGraph", menuName = "NodeGraph/NodeGraph")]
public class NodeGraph : ScriptableObject
{
// 存储节点和边的数据
public List<NodeBaseData> nodes = new List<NodeBaseData>();
public List<NodeLinkData> edges = new List<NodeLinkData>();
}
// 定义一个用于存储节点基础数据的类
[Serializable]
public abstract class NodeBaseData
{
public string GUID;
public string NodeName = "NodeBase";
public Rect Position = Rect.zero;
// 其他节点数据
}
// 定义一个用于存储边数据的类
[Serializable]
public class NodeLinkData
{
public string BaseNodeGUID;
public string OutputPortName;
public string TargetNodeGUID;
public string TargetPortName;
}
// 定义一个节点类,继承自GraphView的Node类
public class MyNode : Node
{
// 节点数据
public NodeBaseData nodeData;
// 构造函数
public MyNode()
{
// 设置节点标题和样式
title = "My Node";
styleSheets.Add(AssetDatabase.LoadAssetAtPath<StyleSheet>("Packages/com.unity.uielements/Editor/Resources/Styles/GraphView.uss"));
// 添加输入/输出端口
var inputPort = new Port(Orientation.Horizontal, Direction.Input, Port.Capacity.Single, typeof(float));
inputPort.portName = "Input";
inputContainer.Add(inputPort);
var outputPort = new Port(Orientation.Horizontal, Direction.Output, Port.Capacity.Single, typeof(float));
outputPort.portName = "Output";
outputContainer.Add(outputPort);
// 添加右键菜单
this.RegisterCallback<MouseDownEvent>(OnMouseDown);
}
// 处理右键菜单事件
private void OnMouseDown(MouseDownEvent evt)
{
if (evt.button == MouseButton.RightMouse)
{
var menu = new GenericMenu();
menu.AddItem(new GUIContent("Delete Node"), false, () => { DeleteNode(); });
menu.ShowAsContext();
evt.StopPropagation();
}
}
// 删除节点
private void DeleteNode()
{
// 从GraphView中移除节点
graphView.RemoveElement(this);
// 从NodeGraph中移除节点数据(需要自行实现)
}
}
// 定义一个节点视图类,继承自GraphView
public class MyGraphView : GraphView
{
// 构造函数
public MyGraphView(EditorWindow window, StyleSheet styleSheet)
{
this.styleSheets.Add(styleSheet);
this.AddManipulator(new ContextualMenuManipulator(OnContextualMenu));
this.AddManipulator(new SelectionDragManipulator());
this.AddManipulator(new RectangleSelector());
this.AddManipulator(new ZoomManipulator());
this.AddManipulator(new PanManipulator());
// 初始化节点和边(需要自行实现)
}
// 处理右键菜单事件
private void OnContextualMenu(ContextualMenuPopulateEvent evt)
{
var menu = new GenericMenu();
menu.AddItem(new GUIContent("Create Node"), false, () => { CreateNode(); });
menu.ShowAsContext();
}
// 创建节点
private void CreateNode()
{
var newNode = new MyNode();
newNode.SetPosition(new Rect(mousePosition, Vector2.one * 100));
this.Add(newNode);
// 添加节点数据到NodeGraph中(需要自行实现)
}
}
// 定义一个编辑器窗口类,用于显示节点编辑器
public class NodeEditorWindow : EditorWindow
{
private MyGraphView graphView;
private NodeGraph nodeGraph;
// 构造函数
[MenuItem("Window/Node Editor")]
public static void ShowWindow()
{
var window = GetWindow<NodeEditorWindow>("Node Editor");
window.minSize = new Vector2(800, 600);
}
// 初始化编辑器窗口
private void OnEnable()
{
// 加载或创建NodeGraph
nodeGraph = AssetDatabase.LoadAssetAtPath<NodeGraph>("Assets/NodeGraphs/MyNodeGraph.asset");
if (nodeGraph == null)
{
nodeGraph = ScriptableObject.CreateInstance<NodeGraph>();
AssetDatabase.CreateAsset(nodeGraph, "Assets/NodeGraphs/MyNodeGraph.asset");
AssetDatabase.SaveAssets();
}
// 初始化GraphView
var styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Packages/com.unity.uielements/Editor/Resources/Styles/GraphView.uss");
graphView = new MyGraphView(this, styleSheet);
graphView.StretchToParentSize();
rootVisualElement.Add(graphView);
// 初始化节点和边(根据nodeGraph加载数据)
// 需要自行实现
}
// 保存编辑器状态
private void OnDisable()
{
// 保存nodeGraph到磁盘(需要自行实现)
}
}

五、总结

本文介绍了如何使用Unity3D的GraphView组件创建一个简单的节点编辑器框架。我们详细讨论了节点编辑器框架的设计、技术实现和代码示例。通过自定义节点、边、面板和工具栏等组件,开发者可以轻松地创建出功能强大的节点编辑器,以满足游戏开发中的复杂需求。希望本文能为Unity3D开发者提供有价值的参考和指导。

更多教学视频

Unity3D​www.bycwedu.com/promotion_channels/2146264125

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/66050.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Springboot日志打印、SpringBoot集成Log4j2(附源码)、异步日志

文章目录 一、Log4j2介绍1.1、常用日志框架1.2、为什么选用log4j2 二、Log4j2整合步骤2.1、引入jar包2.2、配置文件2.3、配置文件模版 三、配置参数简介3.1、日志级别3.2、日志格式&#xff08;PatternLayout&#xff09;3.3、Appenders组件列表3.3.1、Console3.3.2、File3.3.3…

2025-01-03 同步

视野同步AOI 1.视窗同步(独立镜头)2.九宫格3.灯塔(九宫格的优化版)4.四叉树5.十字链表 1.视窗同步(独立镜头) SLG或RTS类型游戏一般用这种方法来实现。 两者还有细微的区别&#xff1a; RTS 对于即时性的要求很高&#xff0c;选手会经常切屏来观看游戏信息。所以通常会直接采…

uniapp:跳转第三方地图

1.跳转第三方高德地图 //跳转地图 toMap(item){uni.navigateTo({url: (window.location.href https://uri.amap.com/navigation?to${item.lng},${item.lat},${item.shopName}&modecar&policy1&srchttps://gawl.gazhcs.com/wap/index.html&callnative0)}) },…

系统设计——大文件传输方案设计

摘要 大文件传输是指通过网络将体积较大的文件从一个位置发送到另一个位置的过程。这些文件可能包括高清视频、大型数据库、复杂的软件安装包等&#xff0c;它们的大小通常超过几百兆字节&#xff08;MB&#xff09;甚至达到几个吉字节&#xff08;GB&#xff09;或更大。大文…

STM32-笔记32-ESP8266作为服务端

esp8266作为服务器的时候&#xff0c;这时候网络助手以客户端的模式连接到esp8266&#xff0c;其中IP地址写的是esp8266作为服务器时的IP地址&#xff0c;可以使用ATCIFSR查询esp8266的ip地址&#xff0c;端口号默认写333。 当esp8266作为服务器的时候&#xff0c;需要完成哪些…

ArcGIS JSAPI 高级教程 - 通过RenderNode实现视频融合效果(不借助三方工具)

ArcGIS JSAPI 高级教程 - 通过RenderNode实现视频融合效果(不借助三方工具) 核心代码完整代码在线示例地球中展示视频可以通过替换纹理的方式实现,但是随着摄像头和无人机的流行,需要视频和场景深度融合,简单的实现方式则不能满足需求。 三维视频融合技术将视频资源与三维…

小程序组件 —— 25 组件案例 - 商品导航区域

这一节主要实现商品导航区的结构和样式&#xff0c;商品导航区没有新的知识点&#xff0c;主要使用之前学习的三个组件&#xff1a; view&#xff1a;视图容器iamge&#xff1a;图片组件text&#xff1a;文本组件 商品导航区由五个商品导航来组成&#xff0c;每一个视频导航都…

MarkDown怎么转pdf;Mark Text怎么使用;

MarkDown怎么转pdf 目录 MarkDown怎么转pdf先用CSDN进行编辑,能双向看版式;标题最后直接导出pdfMark Text怎么使用一、界面介绍二、基本操作三、视图模式四、其他功能先用CSDN进行编辑,能双向看版式; 标题最后直接导出pdf Mark Text怎么使用 Mark Text是一款简洁的开源Mar…

内网渗透:域 Kerberos 认证机制

1. Kerberos 协议简介 Kerberos 是一种网络认证协议&#xff0c;其设计目标是通过密钥系统为客户端/服务器应用程序提供强大的认证服务。 该协议具有以下特点&#xff1a; 去中心化&#xff1a;认证过程独立于主机操作系统&#xff0c;不依赖基于主机地址的信任。安全传输&a…

1961-2022年中国大陆多干旱指数数据集(SPI/SPEI/EDDI/PDSI/SC-PDSI/VPD)

DOI: 10.5194/essd-2024-270 干旱指数对于评估和管理缺水和农业风险至关重要;然而&#xff0c;现有数据集中缺乏统一的数据基础&#xff0c;导致不一致&#xff0c;对干旱指数的可比性提出了挑战。本研究致力于创建CHM_Drought&#xff0c;这是一个创新且全面的长期气象干旱数…

C# 在PDF中添加和删除水印注释 (Watermark Annotation)

目录 使用工具 C# 在PDF文档中添加水印注释 C# 在PDF文档中删除水印注释 PDF中的水印注释是一种独特的注释类型&#xff0c;它通常以透明的文本或图片形式叠加在页面内容之上&#xff0c;为文档添加标识或信息提示。与传统的静态水印不同&#xff0c;水印注释并不会永久嵌入…

LLVM防忘录

目录 Windows中源码编译LLVMWindows下编译LLVM Pass DLL Windows中源码编译LLVM 直接从llvm-project下载源码, 然后解压后用VS2022打开该目录, 然后利用VS的开发终端执行: cmake -S llvm -B build -G "Visual Studio 17 2022" -DLLVM_ENABLE_PROJECTSclang -DLLVM_…

解释一下:运放的输入失调电流

输入失调电流 首先看基础部分:这就是同相比例放大器 按照理论计算,输入VIN=0时,输出VOUT应为0,对吧 仿真与理论差距较大,有200多毫伏的偏差,这就是输入偏置电流IBIAS引起的,接着看它的定义 同向和反向输入电流的平均值,也就是Ib1、Ib2求平均,即(Ib1+Ib2)/2 按照下面…

【双指针】算法题(二)

【双指针】算法题&#xff08;二&#xff09; 前言&#xff1a; 这里是几道算法题&#xff0c;双指针说明在上一章。 一、有效三角形的个数 题目链接&#xff1a; 有效三角形的个数 题目叙述&#xff1a; 解法一&#xff1a;暴力循环&#xff0c;叠加三层for循环&#xff0c…

docker 安装influxdb

docker pull influxdb mkdir -p /root/influxdb/data docker run -d --name influxdb -p 8086:8086 -v /root/influxdb/data:/var/lib/influxdb influxdb:latest#浏览器登录&#xff1a;http://192.168.31.135:8086&#xff0c;首次登录设置用户名密码&#xff1a;admin/admin1…

深入剖析MySQL数据库架构:核心组件、存储引擎与优化策略(四)

慢查询日志&#xff0c;顾名思义&#xff0c;就是查询慢的日志&#xff0c;是指mysql记录所有执行超过long_query_time&#xff08;默认的时间10秒&#xff09;参数设定的时间阈值的SQL语句的日志。该日志能为SQL语句的优化带来很好的帮助。默认情况下&#xff0c;慢查询日志是…

Ansys Discovery 中的网格划分方法:探索模式

本篇博客文章将介绍 Ansys Discovery 中可用于在探索模式下进行分析的网格划分方法。我们将在下一篇博客中介绍 Refine 模式下的网格划分技术。 了解 Discovery Explore 模式下的网格划分 网格划分是将几何模型划分为小单元以模拟系统在不同条件下的行为的过程。这是通过创建…

MT8788安卓核心板_MTK8788核心板参数_联发科模块定制开发

MT8788安卓核心板是一款尺寸为52.5mm x 38.5mm x 2.95mm的高集成度电路板&#xff0c;专为各种智能设备应用而设计。该板卡整合了处理器、图形处理单元(GPU)、LPDDR3内存、eMMC存储及电源管理模块&#xff0c;具备出色的性能与低功耗特性。 这款核心板搭载了联发科的MT8788处理…

Linux实验报告14-Linux内存管理实验

目录 一&#xff1a;实验目的 二&#xff1a;实验内容 1、编辑模块的源代码mm_viraddr.c 2、编译模块 3、编写测试程序mm_test.c 4、编译测试程序mm_test.c 5、在后台运行mm_test 6、验证mm_viraddr模块 一&#xff1a;实验目的 (1)掌握内核空间、用户空间&#xff…

SAP物料主数据界面增加客制化字段、客制化页签的方式

文章目录 前言一、不增加页签&#xff0c;只增加客制化字段二、增加物料主数据页签 前言 【SAP系统MM模块研究】 #SAP #MM #物料 #客制化 #物料主数据 项目上难免会遇到客户要在物料主数据的界面上&#xff0c;增加新字段的需求。 实现方式有&#xff1a; &#xff08;1&…