`)
关键词UnityEditor、可视化节点编辑、Unity编辑器自定义窗口工具使用Newtonsoft.Json、UnityEditor相关接口实现主要代码Handles.DrawBezier(起点终点起点切线向量终点切线向量颜色null, 线粗度) 绘制贝塞尔曲线Handles.DrawAAPolyLine(线粗度顶点1, 顶点2, ...) 根据线段点绘制不规则线段GUI.Window(id, rect, DrawNodeWindow, 窗口标题);void DrawNodeWindow(int id) 传入的id即GUI.Window第一个参数一般传节点唯一标识ID。LinkObj类是节点类里面有一个位置pos数据是存储该节点窗口位于编辑器的位置SerializableVector2类型是一个可被Json序列化的Vector2不然无法被序列化。using Newtonsoft.Json; using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; public class LinkObj { public int id; public Listint preList; public Listint nextList; public SerializableVector2 pos;//位于编辑窗口的位置 public LinkObj(int _id, SerializableVector2 _pos) { id _id; pos _pos; preList new Listint(); nextList new Listint(); } } public static class PathConfig { public static string SaveJsonPath Application.dataPath /MyGraphicsEditorDemo/Editor/LinkList.json; } public class MyGraphicsEditorWindow : EditorWindow { private Dictionaryint, LinkObj linkObjDict new Dictionaryint, LinkObj(); private int tempAddId; private Vector2 scrollViewPos; private Dictionaryint, Rect linkObjRectDict new Dictionaryint, Rect(); private LinkObj currentSelectLinkObj; private Color defaultColor; private Vector2 detailScrollViewPos; private bool isLoaded; [MenuItem(Tools/可视链结构编辑器)] private static void ShowWindow() { Debug.Log(打开可视链结构编辑器); var window EditorWindow.GetWindow(typeof(MyGraphicsEditorWindow)) as MyGraphicsEditorWindow; window.minSize new Vector2(1280, 500); window.Show(true); window.isLoaded false; } MyGraphicsEditorWindow() { this.titleContent new GUIContent(可视链结构编辑器); } private void OnGUI() { defaultColor GUI.color; EditorGUILayout.BeginHorizontal(GUILayout.Width(position.width), GUILayout.Height(position.height)); { //左面板操作 EditorGUILayout.BeginVertical(GUILayout.Width(80)); { EditorGUILayout.BeginHorizontal(); { if (GUILayout.Button(加载)) { //如果本地没有对应的json 文件重新创建 if (File.Exists(PathConfig.SaveJsonPath)) { string json File.ReadAllText(PathConfig.SaveJsonPath); linkObjDict JsonConvert.DeserializeObjectDictionaryint, LinkObj(json); if (linkObjDict null) { linkObjDict new Dictionaryint, LinkObj(); } isLoaded true; } else { isLoaded false; Debug.LogError(加载失败,尚未存在json文件: PathConfig.SaveJsonPath); } } bool isExistFile File.Exists(PathConfig.SaveJsonPath); if ((isLoaded || !isExistFile) GUILayout.Button(保存)) { //如果本地没有对应的json 文件重新创建 if (!isExistFile) { File.Create(PathConfig.SaveJsonPath); } AssetDatabase.Refresh(); string json JsonConvert.SerializeObject(linkObjDict); File.WriteAllText(PathConfig.SaveJsonPath, json); Debug.Log(保存成功: json); AssetDatabase.SaveAssets(); } } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginVertical(); { if (GUILayout.Button(添加节点)) { LinkObj obj new LinkObj(tempAddId, new SerializableVector2(scrollViewPos)); if (!linkObjDict.ContainsKey(tempAddId)) { linkObjDict.Add(tempAddId, obj); } else { Debug.LogError(节点ID已存在: tempAddId); } } tempAddId int.Parse(EditorGUILayout.TextField(节点ID, tempAddId.ToString())); } EditorGUILayout.EndVertical(); } EditorGUILayout.EndVertical(); //中间面板可视节点 EditorGUILayout.BeginVertical(GUILayout.Width(position.width - 500)); { EditorGUILayout.LabelField(string.Format(所有链节点), EditorStyles.boldLabel); EditorGUILayout.BeginHorizontal(box, GUILayout.Height(position.height)); { scrollViewPos EditorGUILayout.BeginScrollView(scrollViewPos, GUILayout.Height(position.height)); { BeginWindows(); if (linkObjDict ! null linkObjDict.Count 0) { foreach (var item in linkObjDict) { int id item.Key; var linkObj item.Value; Rect oRect; if (!linkObjRectDict.TryGetValue(id, out oRect)) { Rect windowRect new Rect(180, 50, 180, 100); windowRect.x linkObj.pos.x; windowRect.y linkObj.pos.y; linkObjRectDict.Add(id, windowRect); } string str string.Format({0}-[节点], id); if (currentSelectLinkObj ! null currentSelectLinkObj.id id) GUI.color Color.yellow; else if (currentSelectLinkObj ! null currentSelectLinkObj.preList.Exists((int x) x id)) GUI.color Color.blue; else if (currentSelectLinkObj ! null currentSelectLinkObj.nextList.Exists((int x) x id)) GUI.color Color.green; //绘画窗口 linkObjRectDict[id] GUI.Window(id, linkObjRectDict[id], DrawNodeWindow, str); GUI.color defaultColor; foreach (int nextId in linkObj.nextList) { Rect nextRect; if (linkObjRectDict.TryGetValue(nextId, out nextRect)) { DrawNodeCurve(linkObjRectDict[id], nextRect, Color.red); } } } } EndWindows(); } EditorGUILayout.EndScrollView(); } EditorGUILayout.EndHorizontal(); } EditorGUILayout.EndVertical(); //右面板编辑选中节点 EditorGUILayout.BeginVertical(GUILayout.Width(250)); { EditorGUILayout.LabelField(节点属性, EditorStyles.boldLabel); EditorGUILayout.BeginHorizontal(box, GUILayout.Height(position.height)); { EditorGUILayout.BeginVertical(); { detailScrollViewPos EditorGUILayout.BeginScrollView(detailScrollViewPos, GUILayout.Height(position.height)); { if (currentSelectLinkObj ! null) { DrawCurrentLinkObj(); } } EditorGUILayout.EndScrollView(); } EditorGUILayout.EndVertical(); } EditorGUILayout.EndHorizontal(); } EditorGUILayout.EndVertical(); } EditorGUILayout.EndHorizontal(); } //绘画窗口函数 private void DrawNodeWindow(int id) { EditorGUILayout.LabelField(string.Format(节点ID:{0}, linkObjDict[id].id), EditorStyles.boldLabel); EditorGUILayout.BeginHorizontal(); { //创建一个GUI Button if (GUILayout.Button(选择)) { currentSelectLinkObj linkObjDict[id]; } GUI.color Color.red; if (GUILayout.Button(删除)) { if (EditorUtility.DisplayDialog(询问, 确认删除?, 确认, 取消)) { linkObjDict.Remove(id); linkObjRectDict.Remove(id); return; } } GUI.color defaultColor; } EditorGUILayout.EndHorizontal(); //设置改窗口可以拖动 GUI.DragWindow(); var oItem linkObjDict[id]; Rect oRect; if (oItem ! null linkObjRectDict.TryGetValue(id, out oRect)) { oItem.pos new SerializableVector2(linkObjRectDict[id].position); } } //***描绘连线 private void DrawNodeCurve(Rect start, Rect end, Color color, float fValue 4) { //根据不同相对位置决定线条的起点和终点 (看似复杂,实际简单,可优化写法) float startX, startY, endX, endY; //start左 end右时, 起点是start右侧中点, 终点是end左侧中点 if (start.x end.x Mathf.Abs(start.x start.width / 2 - end.x - end.width / 2) 50) { startX start.x start.width; endX end.x; startY start.y start.height / 2; endY end.y end.height / 2; } //start右 end左时, 起点是start左侧中点, 终点是end右侧中点 else if (start.x end.x Mathf.Abs(start.x start.width / 2 - end.x - end.width / 2) 50) { startX start.x; endX end.x end.width; startY start.y start.height / 2; endY end.y end.height / 2; } else { //start上 end下时, 起点是start下侧中点, 终点是end上侧中点 if (start.y end.y) { startX start.x start.width / 2; startY start.y; endX end.x end.width / 2; endY end.y end.height; } //start下 end上时, 起点是start上侧中点, 终点是end下侧中点 else { startX start.x start.width / 2; startY start.y start.height; endX end.x end.width / 2; endY end.y; } } Vector3 startPos new Vector3(startX, startY, 0); Vector3 endPos new Vector3(endX, endY, 0); //根据起点和终点偏向给出不同朝向的Tan切线 Vector3 startTan, endTan; if (start.x end.x) { startTan startPos Vector3.right * 50; endTan endPos Vector3.left * 50; } else { startTan startPos Vector3.left * 50; endTan endPos Vector3.right * 50; } //绘制线条 color颜色 fValue控制粗细 Handles.DrawBezier(startPos, endPos, startTan, endTan, color, null, fValue); //绘制线条终点的2条斜线 形成箭头 Handles.color color; Vector2 to endPos; Vector2 v1, v2; //与上方大同小异,根据相对位置得出不同的箭头线段点 if (start.x end.x Mathf.Abs(start.x start.width / 2 - end.x - end.width / 2) 50) { v1 new Vector2(-8f, 8f); v2 new Vector2(-8f, -8f); } else if (start.x end.x Mathf.Abs(start.x start.width / 2 - end.x - end.width / 2) 50) { v1 new Vector2(8f, 8f); v2 new Vector2(8f, -8f); } else { if (start.y end.y) { v1 new Vector2(-8f, 8f); v2 new Vector2(8f, 8f); } else { v1 new Vector2(-8f, -8f); v2 new Vector2(8f, -8f); } } //fValue粗细绘制由3个点构成的线段形成箭头 Handles.DrawAAPolyLine(fValue, to v1, to, to v2); } // 当前选中节点详情编辑页面 private void DrawCurrentLinkObj() { EditorGUILayout.LabelField(string.Format(节点ID{0}, currentSelectLinkObj.id), EditorStyles.boldLabel); EditorGUILayout.Space(10); EditorGUILayout.LabelField(下一个节点); DrawListMember(currentSelectLinkObj.nextList); EditorGUILayout.Space(10); EditorGUILayout.LabelField(上一个节点); DrawListMember(currentSelectLinkObj.preList); } //列表显示 private void DrawListMember(Listint lst, bool isOnlyRead false) { EditorGUILayout.BeginVertical(); { if (lst.Count ! 0) { for (int i 0; i lst.Count; i) { EditorGUILayout.BeginHorizontal(); { GUILayout.Label((i 1).ToString(), GUILayout.Width(25)); lst[i] EditorGUILayout.IntField(lst[i]); GUI.color Color.red; if (GUILayout.Button(-, GUILayout.Width(30))) { lst.RemoveAt(i); } GUI.color defaultColor; } EditorGUILayout.EndHorizontal(); } } if (GUILayout.Button()) { lst.Add(lst.Count); } EditorGUILayout.EndVertical(); } } }using Newtonsoft.Json; using System.Collections.Generic; using UnityEngine; [System.Serializable] public class SerializableVector2 { public float x; public float y; [JsonIgnore] public Vector2 UnityVector { get { return new Vector2(x, y); } } public SerializableVector2(Vector2 v) { x v.x; y v.y; } public static ListSerializableVector2 GetSerializableList(ListVector2 vList) { ListSerializableVector2 list new ListSerializableVector2(vList.Count); for (int i 0; i vList.Count; i) { list.Add(new SerializableVector2(vList[i])); } return list; } public static ListVector2 GetSerializableList(ListSerializableVector2 vList) { ListVector2 list new ListVector2(vList.Count); for (int i 0; i vList.Count; i) { list.Add(vList[i].UnityVector); } return list; } }
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。