323 lines
9.6 KiB
C#
323 lines
9.6 KiB
C#
using UnityEditor;
|
||
using UnityEngine;
|
||
using System.Collections.Generic;
|
||
|
||
public class TechTreeEditorWindow : EditorWindow
|
||
{
|
||
private List<TechNode> nodes = new List<TechNode>(); // 所有节点
|
||
private TechNode headnode;
|
||
private Vector2 mousePosition;
|
||
private TechNode selectedNode; // 当前选中的节点
|
||
private TechNode nodeToConnect; // 用于保存待连接的节点
|
||
|
||
// TechTreeData 用于保存和加载的 ScriptableObject
|
||
private TechTreeData techTreeData;
|
||
|
||
[MenuItem("Window/Tech Tree Editor")]
|
||
public static void ShowWindow()
|
||
{
|
||
GetWindow<TechTreeEditorWindow>("Tech Tree Editor");
|
||
}
|
||
|
||
private void OnGUI()
|
||
{
|
||
Event e = Event.current;
|
||
mousePosition = e.mousePosition;
|
||
|
||
// 继续绘制节点和处理事件
|
||
DrawNodes();
|
||
ProcessNodeEvents(e);
|
||
|
||
if (e.button == 1 && e.type == EventType.MouseDown)
|
||
{
|
||
ShowContextMenu();
|
||
}
|
||
|
||
// 检测点击空白区域以取消选中节点
|
||
if (e.button == 0 && e.type == EventType.MouseDown && !IsMouseOverAnyNode(mousePosition))
|
||
{
|
||
// 取消选择
|
||
if (nodeToConnect != null)
|
||
{
|
||
nodeToConnect.title = "节点"; // 恢复节点标题
|
||
nodeToConnect = null;
|
||
selectedNode = null;
|
||
Debug.Log("取消选中");
|
||
}
|
||
}
|
||
|
||
// 刷新窗口
|
||
Repaint();
|
||
}
|
||
|
||
private void OnEnable()
|
||
{
|
||
LoadTechTreeData();
|
||
}
|
||
|
||
private void OnDisable()
|
||
{
|
||
SaveTechTreeData();
|
||
}
|
||
|
||
private void DrawNodes()
|
||
{
|
||
foreach (TechNode node in nodes)
|
||
{
|
||
node.Draw();
|
||
}
|
||
}
|
||
|
||
// 显示右键菜单
|
||
private void ShowContextMenu()
|
||
{
|
||
GenericMenu menu = new GenericMenu();
|
||
|
||
if (selectedNode != null)
|
||
{
|
||
menu.AddItem(new GUIContent("Remove Node"), false, RemoveSelectedNode);
|
||
menu.AddItem(new GUIContent("Remove Connections"), false, RemoveSelectedNodeConnections);
|
||
}
|
||
else
|
||
{
|
||
menu.AddItem(new GUIContent("Add Node"), false, () => AddNode(mousePosition));
|
||
}
|
||
|
||
menu.ShowAsContext();
|
||
}
|
||
|
||
// 添加节点
|
||
private void AddNode(Vector2 position)
|
||
{
|
||
TechNode newNode = new TechNode(position, 100, 50);
|
||
if (nodes.Count == 0)
|
||
{
|
||
headnode = newNode;
|
||
}
|
||
nodes.Add(newNode);
|
||
}
|
||
|
||
// 删除选中的节点
|
||
private void RemoveSelectedNode()
|
||
{
|
||
if (selectedNode != null)
|
||
{
|
||
if (selectedNode == headnode)
|
||
{
|
||
// 不能删除头节点
|
||
}
|
||
else
|
||
{
|
||
// 首先删除与其他节点的连接
|
||
foreach (TechNode node in nodes)
|
||
{
|
||
node.RemoveConnection(selectedNode);
|
||
}
|
||
nodes.Remove(selectedNode);
|
||
selectedNode = null;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 删除选中节点的所有连接
|
||
private void RemoveSelectedNodeConnections()
|
||
{
|
||
if (selectedNode != null)
|
||
{
|
||
selectedNode.RemoveAllConnections();
|
||
}
|
||
}
|
||
|
||
// 检查鼠标是否悬停在某个节点上
|
||
private bool IsMouseOverAnyNode(Vector2 mousePos)
|
||
{
|
||
foreach (TechNode node in nodes)
|
||
{
|
||
if (node.rect.Contains(mousePos))
|
||
{
|
||
return true; // 鼠标在某个节点上
|
||
}
|
||
}
|
||
return false; // 鼠标不在任何节点上
|
||
}
|
||
|
||
// 处理节点事件(拖拽、连接、选择)
|
||
private void ProcessNodeEvents(Event e)
|
||
{
|
||
foreach (TechNode node in nodes)
|
||
{
|
||
bool guiChanged = node.ProcessEvents(e);
|
||
|
||
if (guiChanged)
|
||
{
|
||
GUI.changed = true;
|
||
}
|
||
|
||
// 处理右键选择
|
||
if (e.type == EventType.MouseDown && e.button == 1 && node.rect.Contains(e.mousePosition))
|
||
{
|
||
selectedNode = node; // 选中节点
|
||
}
|
||
|
||
// 处理左键点击的节点选择与连接
|
||
if (e.type == EventType.MouseDown && e.button == 0 && node.rect.Contains(e.mousePosition))
|
||
{
|
||
Debug.LogWarning("鼠标位置" + e.mousePosition + "---" + node.rect);
|
||
// 如果是第一次选择节点,标记为待连接
|
||
if (nodeToConnect == null)
|
||
{
|
||
Debug.Log("已选择");
|
||
nodeToConnect = node;
|
||
node.title = "已选择";
|
||
}
|
||
else if (nodeToConnect != node && node.techItem != null && node.techItem != null)
|
||
{
|
||
// 如果选择了不同的节点,执行连接操作
|
||
nodeToConnect.ConnectTo(node); // 支持多个父节点
|
||
nodeToConnect.title = "节点";
|
||
nodeToConnect = null; // 完成连接后清空选择
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#region 存储
|
||
private void SaveTechTreeData()
|
||
{
|
||
// 确保文件夹存在
|
||
string folderPath = "Assets/科技树/科技树窗口";
|
||
if (!System.IO.Directory.Exists(folderPath))
|
||
{
|
||
System.IO.Directory.CreateDirectory(folderPath);
|
||
}
|
||
|
||
// 检查是否有 techTreeData,如果没有则创建
|
||
if (techTreeData == null)
|
||
{
|
||
techTreeData = CreateInstance<TechTreeData>();
|
||
}
|
||
|
||
// 保存头节点的数据
|
||
if (headnode != null)
|
||
{
|
||
techTreeData.headNode = SaveNodeData(headnode); // 递归保存头节点及其子节点
|
||
}
|
||
|
||
// 保存 TechTreeData 资产文件
|
||
string techTreeDataPath = $"{folderPath}/窗口数据.asset";
|
||
if (!AssetDatabase.Contains(techTreeData))
|
||
{
|
||
AssetDatabase.CreateAsset(techTreeData, techTreeDataPath);
|
||
}
|
||
|
||
AssetDatabase.SaveAssets();
|
||
AssetDatabase.Refresh();
|
||
|
||
Debug.Log("科技树数据已成功保存!");
|
||
}
|
||
|
||
// 递归保存节点及其子节点数据
|
||
private TechNodeData SaveNodeData(TechNode node)
|
||
{
|
||
var nodeData = new TechNodeData
|
||
{
|
||
position = node.rect.position,
|
||
title = node.title,
|
||
techItemPath = AssetDatabase.GetAssetPath(node.techItem) // 保存 TechItem 的路径
|
||
};
|
||
|
||
// 如果 TechItem 是新建的,保存它
|
||
if (string.IsNullOrEmpty(nodeData.techItemPath) && node.techItem != null)
|
||
{
|
||
string techItemPath = AssetDatabase.GenerateUniqueAssetPath($"Assets/科技树/科技树窗口/{node.techItem.itemName}.asset");
|
||
AssetDatabase.CreateAsset(node.techItem, techItemPath);
|
||
nodeData.techItemPath = techItemPath;
|
||
}
|
||
|
||
// 递归保存子节点
|
||
foreach (var childNode in node.children)
|
||
{
|
||
var childNodeData = SaveNodeData(childNode); // 递归调用
|
||
nodeData.children.Add(childNodeData);
|
||
}
|
||
|
||
return nodeData;
|
||
}
|
||
|
||
private void LoadTechTreeData()
|
||
{
|
||
string assetPath = "Assets/科技树/科技树窗口/窗口数据.asset";
|
||
|
||
techTreeData = AssetDatabase.LoadAssetAtPath<TechTreeData>(assetPath);
|
||
if (techTreeData != null && techTreeData.headNode != null)
|
||
{
|
||
// 通过保存的头节点数据加载头节点
|
||
headnode = LoadNodeData(techTreeData.headNode);
|
||
|
||
Debug.Log("科技树数据已加载成功!");
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning("TechTreeData 资产不存在!");
|
||
}
|
||
}
|
||
|
||
// 递归加载节点及其子节点
|
||
private TechNode LoadNodeData(TechNodeData nodeData)
|
||
{
|
||
var newNode = new TechNode(nodeData.position, 100, 50)
|
||
{
|
||
title = nodeData.title
|
||
};
|
||
|
||
// 加载对应的 TechItem
|
||
if (!string.IsNullOrEmpty(nodeData.techItemPath))
|
||
{
|
||
newNode.techItem = AssetDatabase.LoadAssetAtPath<TechItem>(nodeData.techItemPath);
|
||
|
||
if (newNode.techItem != null)
|
||
{
|
||
// 清空旧的父子关系
|
||
newNode.techItem.parent = new List<TechItem>();
|
||
newNode.techItem.children.Clear();
|
||
|
||
// 恢复 TechItem 的父对象
|
||
foreach (var parentPath in nodeData.parents)
|
||
{
|
||
TechItem parentTechItem = AssetDatabase.LoadAssetAtPath<TechItem>(parentPath);
|
||
if (parentTechItem != null)
|
||
{
|
||
newNode.techItem.parent.Add(parentTechItem);
|
||
}
|
||
}
|
||
|
||
// 恢复 TechItem 的子对象
|
||
List<TechItem> validChildren = new List<TechItem>();
|
||
foreach (var childTechItem in newNode.techItem.children)
|
||
{
|
||
string childPath = AssetDatabase.GetAssetPath(childTechItem);
|
||
TechItem childItem = AssetDatabase.LoadAssetAtPath<TechItem>(childPath);
|
||
if (childItem != null)
|
||
{
|
||
validChildren.Add(childItem);
|
||
}
|
||
}
|
||
newNode.techItem.children = validChildren; // 更新 TechItem 的子节点列表
|
||
}
|
||
}
|
||
|
||
nodes.Add(newNode);
|
||
|
||
// 递归加载子节点
|
||
foreach (var childNodeData in nodeData.children)
|
||
{
|
||
var childNode = LoadNodeData(childNodeData); // 递归调用
|
||
newNode.ConnectTo(childNode); // 恢复父子关系
|
||
}
|
||
|
||
return newNode;
|
||
}
|
||
|
||
#endregion
|
||
}
|