2025-06-07 17:43:34 +08:00

859 lines
31 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#if !BUILDING
using Cysharp.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.UI;
namespace EditorCreateUI
{
public partial class Editor_CreateUIPanel : EditorWindowBase
{
private GenerateAttData[] generateAttDatas = new GenerateAttData[]
{
new GenerateAttData("Ga_", "对象", "GameObject"),
new GenerateAttData("Tr_", "对象", "Transform"),
new GenerateAttData("Rect_", "RectTransform", "RectTransform"),
new GenerateAttData("Content_", "容器", "Transform"),
new GenerateAttData("Btn_", "按钮", "Button"),
new GenerateAttData("Tcg_", "复选框组", "TgCheckGroup"),
new GenerateAttData("Av_", "视频ui", "DisplayUGUI", "using RenderHeads.Media.AVProVideo;"),
new GenerateAttData("Tx_", "文本", "TextMeshProUGUI", "using TMPro;"),
new GenerateAttData("Img_", "图片", "Image或RawImage"),
new GenerateAttData("Sr_", "滑动列表", "ScrollRect"),
new GenerateAttData("Rv_", "无限列表", "RecycleView"),
new GenerateAttData("Ev_", "无限列表(带标签)", "ExpandableView"),
new GenerateAttData("Evs_", "无限列表(带标签,单二级)", "ExpandableViewSingle"),
new GenerateAttData("Input_", "输入框", "TMP_InputField", "using TMPro;"),
new GenerateAttData("Slider_", "滑动条", "Slider"),
new GenerateAttData("Toggle_", "复选框", "Toggle"),
new GenerateAttData("TG_", "复选组", "TgGroup"),
new GenerateAttData("Lt_", "多语言文本", "LanguageText"),
new GenerateAttData("Li_", "多语言图片", "LanguageImage"),
new GenerateAttData("Ldt_", "倒计时", "LanguageDownTime"),
new GenerateAttData("Tm_", "TMP文本", "TextMeshProUGUI", "using TMPro;"),
new GenerateAttData("Cam_", "摄像机", "Camera"),
new GenerateAttData("Class_{T}", "自定义类型", "{T}"),
};
private static string[] notClose_raycast = new string[]
{
"Scroll View",
"btn_",
"ray",
"sv_",
"close",
};
private static string[] notFindChild = new string[]
{
"UI_"
};
private string panelName = "";
private int selectedOptionIndex = 0;
private Vector2 scrollPosition = Vector2.zero;
string systemName;
/// <summary>
/// child, panelName, prefabDir
/// </summary>
List<Tuple<bool, string, string>> lastPanelName = new List<Tuple<bool, string, string>>();
int lastPanelIndex = 0;
[MenuItem("Window/Create UI Panel")]
public static void ShowWindow()
{
var window = EditorWindow.GetWindow(typeof(Editor_CreateUIPanel));
window.titleContent = new GUIContent("UI管理工具");
}
private void ShowTips()
{
try
{
if (position.width <= 350) return;
//GUILayout.Label("首字母大写前缀_名称。会自动创建变量。\n不是以下类型前缀的都自动生成为Transform。\n名称中带ray不会关闭RayCast");
scrollPosition = GUILayout.BeginScrollView(scrollPosition, false, true, GUILayout.ExpandHeight(true));
// 开始绘制表格布局
GUILayout.BeginVertical(GUI.skin.box);
// 表头
GUILayout.BeginHorizontal();
DrawTableCell("", 20, 20, Color.gray);
DrawTableCell("前缀", 150, 20, Color.gray);
DrawTableCell("类型", 150, 20, Color.gray);
DrawTableCell("描述", 150, 20, Color.gray);
GUILayout.EndHorizontal();
// 分隔线
DrawHorizontalLine(500);
// 数据行
for (int i = 0; i < generateAttDatas.Length; i++)
{
var data = generateAttDatas[i];
GUILayout.BeginHorizontal();
DrawTableCell((i + 1).ToString(), 20, 15, Color.gray);
DrawTableCell(data.prefix, 150);
DrawTableCell(data.type, 150);
DrawTableCell(data.description, 150);
GUILayout.EndHorizontal();
// 添加淡淡的分割线
DrawHorizontalLine(500, Color.gray);
}
// 结束绘制表格布局
GUILayout.EndVertical();
// 结束绘制滚动视图
GUILayout.EndScrollView();
}
catch (Exception)
{
}
}
bool CheckIsThis()
{
var sta = PrefabStageUtility.GetCurrentPrefabStage();
if (sta && sta.prefabContentsRoot)
{
if (sta.prefabContentsRoot.name == panelName)
{
return true;
}
}
return false;
}
bool GetIsSingle()
{
var sta = PrefabStageUtility.GetCurrentPrefabStage();
Debug.Log(sta.prefabContentsRoot);
return true;
}
string[] GetCompletion()
{
List<string> str = new List<string>();
// 指定目录
string directoryPath = "Assets/Scripts/HotUpdate/Game/UI";
DirectoryInfo directoryInfo = new DirectoryInfo(directoryPath);
DirectoryInfo[] di = directoryInfo.GetDirectories();
for (int i = 0; i < di.Length; i++)
{
DirectoryInfo[] di2 = di[i].GetDirectories();
for (int j = 0; j < di2.Length; j++)
{
str.Add(di2[j].Name);
GetCompletion(ref str, di2[j]);
}
}
return str.ToArray();
}
void GetCompletion(ref List<string> str, DirectoryInfo dir)
{
DirectoryInfo[] dirs = dir.GetDirectories();
for (int i = 0; i < dirs.Length; i++)
{
var item = dirs[i];
if (item.Name.ToLower().StartsWith("ui_"))
{
str.Add(item.Name);
GetCompletion(ref str, item);
}
}
}
private void OnGUI()
{
//try
//{
//// 页签名称数组
//string[] tabNames = { "Main", "Child" };
//// 创建一个横向页签栏
//selectedTabIndex = GUILayout.Toolbar(selectedTabIndex, tabNames);
if (IsCreateChild == false)
OnMainGUI();
else
OnCreateChildGUI();
//if (GUI_Button("测试"))
// GetCompletion();
GUILayout.Space(20);
// 开始绘制滚动视图
ShowTool();
ShowTips();
//}
//catch (Exception)
//{
//}
}
private void CreatePanel(string systemName)
{
string panelName = this.panelName;
if (!this.panelName.StartsWith("UI_"))
panelName = "UI_" + this.panelName;
// 复制 UI_Example 目录
string sourceDir = "Assets/Art/UI/Panels/UI_Example";
string targetDir = "Assets/Art/UI/Panels/" + panelName;
if (!AssetDatabase.IsValidFolder(targetDir))
{
AssetDatabase.CopyAsset(sourceDir, targetDir);
AssetDatabase.Refresh();
}
else
{
Debug.LogWarning("已存在同名的面板。");
return;
}
// 将 UI_Example 文件复制到新目录
string sourceFile = Path.Combine(sourceDir, "UI_Example.prefab");
string destFile = Path.Combine(targetDir, panelName + ".prefab");
string deleteFile = Path.Combine(targetDir, "UI_Example.prefab");
File.Copy(sourceFile, destFile);
File.Delete(deleteFile);
string ToSourceFolder = $"Assets/Scripts/HotUpdate/Game/UI/{systemName}/{panelName}";
if (!Directory.Exists(ToSourceFolder))
Directory.CreateDirectory(ToSourceFolder);
AssetDatabase.Refresh();
//复制 UI_Example.cs 脚本并创建新脚本
string scriptSource = $"Assets/Scripts/HotUpdate/Game/UI/Normal/UI_Example/UI_Example.cs";
string scriptTarget = $"Assets/Scripts/HotUpdate/Game/UI/{systemName}/{panelName}/{panelName}.cs";
AssetDatabase.CopyAsset(scriptSource, scriptTarget);
// 复制 UI_Example_Generate.cs 脚本并创建新脚本
string scriptSource2 = $"Assets/Scripts/HotUpdate/Game/UI/Normal/UI_Example/UI_Example_Generate.cs";
string scriptTarget2 = $"Assets/Scripts/HotUpdate/Game/UI/{systemName}/{panelName}/{panelName}_Generate.cs";
AssetDatabase.CopyAsset(scriptSource2, scriptTarget2);
// 读取并替换脚本内容
string scriptPath = Path.Combine(Application.dataPath, $"Scripts/HotUpdate/Game/UI/{systemName}/{panelName}/{panelName}.cs");
string scriptContent = File.ReadAllText(scriptPath);
scriptContent = scriptContent.Replace("UI_Example", panelName);
// 将修改后的内容写回脚本文件
File.WriteAllText(scriptPath, scriptContent);
// 读取并替换脚本内容
string scriptPath2 = Path.Combine(Application.dataPath, $"Scripts/HotUpdate/Game/UI/{systemName}/{panelName}/{panelName}_Generate.cs");
string scriptContent2 = File.ReadAllText(scriptPath2);
scriptContent2 = scriptContent2.Replace("UI_Example", panelName);
// 将修改后的内容写回脚本文件
File.WriteAllText(scriptPath2, scriptContent2);
AssetDatabase.Refresh();
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(destFile);
if (prefab != null)
{
// 修改 tx_name 的文本属性
//Text textComponent = prefab.GetComponentInChildren<Text>();
//if (textComponent != null)
// textComponent.text = panelName.Substring(3);
//else
// Debug.LogError("在预制体中未找到 Text 组件。");
// 保存预制体的更改
PrefabUtility.SaveAsPrefabAsset(prefab, destFile);
var sceneView = SceneView.sceneViews[0] as SceneView;
// 激活主场景视图
if (sceneView != null)
sceneView.Focus();
Selection.activeObject = prefab;
AssetDatabase.OpenAsset(prefab);
if (!SceneView.lastActiveSceneView.in2DMode)
// 切换到 2D 模式
SceneView.lastActiveSceneView.in2DMode = true;
}
else
{
//Debug.LogError("在路径 " + destFile + " 中未找到预制体。");
}
AssetDatabase.Refresh();
}
bool IsSpecialName(string name)
{
return char.IsUpper(name[0]) && name.Contains("_") && !name.Contains(" ")
&& !name.Contains("(")
&& !name.Contains(")");
}
void AutoSetRaycast(Transform tr)
{
bool isNot = false;
for (int i = 0; i < notClose_raycast.Length; i++)
{
if (tr.name.ToLower().Contains(notClose_raycast[i].ToLower()))
{
isNot = true;
break;
}
}
if (!isNot)
{
//如果不是特殊命名
if (!IsSpecialName(tr.name))
{
var gra = tr.GetComponent<MaskableGraphic>();
if (gra != null)
gra.raycastTarget = false;
}
else
{
//Text都关掉 Raycast
var te = tr.GetComponent<Text>();
if (te != null)
te.raycastTarget = false;
}
}
for (int k = 0; k < tr.childCount; k++)
AutoSetRaycast(tr.GetChild(k));
}
void GenerateAttribute(string systemName)
{
string panelName = this.panelName;
if (!this.panelName.StartsWith("UI_"))
panelName = "UI_" + this.panelName;
string targetDir = "Assets/Art/UI/Panels/" + panelName;
string destFile = Path.Combine(targetDir, panelName + ".prefab");
string str =
@"using UnityEngine;
using UnityEngine.UI;
[USING]
//子类[NAME]
[UIClassName(typeof([NAME]))]
public abstract class [NAME]_Generate : UIRootPanelBase
{
[ATTRIBUTE]
protected abstract void Init();
internal override void InitAttribute()
{
[FIND]
Init();
}
}";
str = str.Replace("[NAME]", panelName);
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(destFile);
List<Tuple<string, string>> tuples = new List<Tuple<string, string>>();
string str_using = "";
Action<Transform> action = null;
action = (tr) =>
{
// 检查节点名字首字母是否为大写,并且带_
if (IsSpecialName(tr.name))
{
Transform target = tr.parent;
string namePath = tr.name;
while (target != prefab.transform)
{
namePath = target.name + "/" + namePath;
target = target.parent;
}
//Debug.Log("生成:" + tr.name);
string attStr;
string findStr;
if (tr.name.StartsWith("Img_"))
{
if (tr.GetComponent<RawImage>() != null)
{
attStr = "internal RawImage " + tr.name + ";";
findStr = $"{tr.name} = transform.Find(\"{namePath}\").GetComponent<RawImage>();";
}
else if (tr.GetComponent<Image>() != null)
{
attStr = "internal Image " + tr.name + ";";
findStr = $"{tr.name} = transform.Find(\"{namePath}\").GetComponent<Image>();";
}
else
{
attStr = "internal Transform " + tr.name + ";";
findStr = $"{tr.name} = transform.Find(\"{namePath}\");";
}
}
else if (tr.name.StartsWith("Btn_"))
{
if (tr.GetComponent<Button>() != null)
{
attStr = "internal Button " + tr.name + ";";
findStr = $"{tr.name} = transform.Find(\"{namePath}\").GetComponent<Button>();";
}
else
{
attStr = "internal GameObject " + tr.name + ";";
findStr = $"{tr.name} = transform.Find(\"{namePath}\").gameObject;";
}
}
else if (tr.name.StartsWith("Content_"))
{
attStr = "internal Transform " + tr.name + ";";
findStr = $"{tr.name} = transform.Find(\"{namePath}\");";
}
else if (tr.name.StartsWith("Ga_"))
{
attStr = "internal GameObject " + tr.name + ";";
findStr = $"{tr.name} = transform.Find(\"{namePath}\").gameObject;";
}
else if (tr.name.StartsWith("Class_"))
{
string componName = tr.name.Replace("Class_", "");
attStr = $"internal {componName} {tr.name};";
findStr = $"{tr.name} = transform.Find(\"{namePath}\").GetComponent<{componName}>();";
}
else
{
GenerateAttData gen = null;
foreach (var item in generateAttDatas)
if (tr.name.StartsWith(item.prefix))
{
gen = item;
break;
}
if (gen == null)
{
attStr = $"internal Transform {tr.name};";
findStr = $"{tr.name} = transform.Find(\"{namePath}\");";
}
else
{
attStr = $"internal {gen.type} {tr.name};";
findStr = $"{tr.name} = transform.Find(\"{namePath}\").GetComponent<{gen.type}>();";
foreach (var item_u in gen.usin)
{
if (!str_using.Contains(item_u))
str_using += $"{item_u}\n";
}
}
}
tuples.Add(Tuple.Create(attStr, findStr));
}
bool isFind = true;
foreach (var startW in notFindChild)
if (tr.name.StartsWith(startW))
{
isFind = false;
break;
}
if (isFind)
for (int k = 0; k < tr.childCount; k++)
action.Invoke(tr.GetChild(k));
};
for (int i = 0; i < prefab.transform.childCount; i++)
action(prefab.transform.GetChild(i));
StringBuilder atts = new StringBuilder();
StringBuilder finds = new StringBuilder();
for (int j = 0; j < tuples.Count; j++)
{
var tuple = tuples[j];
if (j == 0)
{
atts.Append(tuple.Item1);
finds.Append(tuple.Item2);
}
else
{
atts.Append("\n " + tuple.Item1);
finds.Append("\n " + tuple.Item2);
}
}
str = str.Replace("[ATTRIBUTE]", atts.ToString());
str = str.Replace("[FIND]", finds.ToString());
str = str.Replace("[USING]", str_using);
string scriptTarget2 = $"Assets/Scripts/HotUpdate/Game/UI/{systemName}/{panelName}/{panelName}_Generate.cs";
File.WriteAllText(scriptTarget2, str);
AssetDatabase.Refresh();
}
DirectoryInfo GetDir(string uiName)
{
string sysFolder = Path.Combine(Application.dataPath, "Scripts/HotUpdate/Game/UI");
var sysF = new DirectoryInfo(sysFolder);
//sys
foreach (var item in sysF.GetDirectories())
{
//主ui
foreach (var item2 in item.GetDirectories())
{
if (item2.Name == uiName)
{
return item2;
}
else
{
//子ui
DirectoryInfo[] allDirectories = item2.GetDirectories("*", SearchOption.AllDirectories);
foreach (var dir in allDirectories)
if (uiName == dir.Name)
return dir;
}
}
}
return null;
}
static string ConvertPath(string path)
{
string fixedPart = @"Assets\Scripts\HotUpdate\Game\UI";
var parts = path.Split('\\');
int startIndex = Array.IndexOf(parts, fixedPart.Split('\\').Last()) + 1;
return startIndex > 0 && startIndex < parts.Length
? string.Join(" → ", parts.Skip(startIndex))
: "Path does not contain the specified structure.";
}
void OpenPanel(bool isChild, string prefabDir)
{
if (!isChild) this.currentPanelScriptDir = null;
string prefabPath = $"{prefabDir}/{panelName}.prefab";
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
Selection.activeObject = prefab;
AssetDatabase.OpenAsset(prefab);
var sceneView = SceneView.sceneViews[0] as SceneView;
// 激活主场景视图
if (sceneView != null)
sceneView.Focus();
if (!SceneView.lastActiveSceneView.in2DMode)
// 切换到 2D 模式
SceneView.lastActiveSceneView.in2DMode = true;
//临时处理解决描边不正常的bug
var sta = PrefabStageUtility.GetCurrentPrefabStage();
}
private void OnMainGUI()
{
if (currentPanelScriptDir != null)
{
GUILayout.Label(ConvertPath(currentPanelScriptDir.FullName));
}
GUILayout.Label("输入面板名称", EditorStyles.boldLabel);
this.panelName = PlayerPrefs.GetString("editor_panelName");
//this.panelName = EditorGUILayout.TextField("面板名称: UI_", this.panelName);
// 绘制自动补全输入框
string[] suggestions = GetCompletion();
bool isClick = false;
bool isReturn = false;
bool isShowReturn = lastPanelName.Count > 1;
this.panelName = AutoCompleteTextField(this.panelName, suggestions, ref isClick, isShowReturn, ref isReturn);
PlayerPrefs.SetString("editor_panelName", this.panelName);
if (string.IsNullOrEmpty(this.panelName))
{
EditorGUILayout.HelpBox("输入名称后创建/打开面板", MessageType.Info);
//ShowTips();
return;
}
string panelName = this.panelName;
if (!this.panelName.StartsWith("UI_"))
panelName = "UI_" + this.panelName;
bool isExa = false;
if (panelName == "UI_Example")
{
GUILayout.Label("示例ui");
isExa = true;
}
string prefabDir = $"Assets/Art/UI/Panels/{panelName}";
if (AssetDatabase.IsValidFolder(prefabDir))
{
bool isChild = false;
string sysFolder = Path.Combine(Application.dataPath, "Scripts/HotUpdate/Game/UI");
var sysF = new DirectoryInfo(sysFolder);
var dirs = sysF.GetDirectories();
string[] dirNames = new string[dirs.Length];
for (int i = 0; i < dirs.Length; i++)
dirNames[i] = dirs[i].Name;
int index = -1;
for (int i = 0; i < dirs.Length; i++)
{
var dirs2 = dirs[i].GetDirectories();
for (int j = 0; j < dirs2.Length; j++)
if (dirs2[j].Name == panelName)
{
index = i;
break;
}
}
if (index == -1)
isChild = true;
this.currentPanelScriptDir = GetDir(panelName);
int newIndex;
if (!isChild)
{
newIndex = EditorGUILayout.Popup("系统目录:", index, dirNames);
systemName = dirNames[index];
if (newIndex != index)
{
Debug.Log("修改目录为" + dirNames[newIndex]);
string oldFolder = $"{Application.dataPath}/Scripts/HotUpdate/Game/UI/{systemName}/{panelName}";
string newFolder = $"{Application.dataPath}/Scripts/HotUpdate/Game/UI/{dirNames[newIndex]}";
MoveDirectory(oldFolder, newFolder);
}
}
if (isReturn)
{
//打开上一个面板
lastPanelName.RemoveAt(lastPanelName.Count - 1);
var last = lastPanelName[lastPanelName.Count - 1];
this.panelName = last.Item2;
OpenPanel(last.Item1, last.Item3);
PlayerPrefs.SetString("editor_panelName", this.panelName);
}
if (CheckIsThis())
{
if (GUI_Button("生成属性"))
{
if (isChild)
{
GenerateAttribute_Child(currentPanelScriptDir);
}
else
{
GenerateAttribute(systemName);
}
var sta = PrefabStageUtility.GetCurrentPrefabStage();
AutoSetRaycast(sta.prefabContentsRoot.transform);
}
//if (GUI_Button("关闭多余 Raycast Target"))
//{
// var sta = PrefabStageUtility.GetCurrentPrefabStage();
// AutoSetRaycast(sta.prefabContentsRoot.transform);
//}
if (isClick)
{
var sceneView = SceneView.sceneViews[0] as SceneView;
// 激活主场景视图
if (sceneView != null)
sceneView.Focus();
if (!SceneView.lastActiveSceneView.in2DMode)
// 切换到 2D 模式
SceneView.lastActiveSceneView.in2DMode = true;
}
}
else
{
if (GUI_Button("打开面板") || isClick)
{
if (panelName != "")
if (lastPanelName.Count == 0 || lastPanelName[lastPanelName.Count - 1].Item2 != panelName)
lastPanelName.Add(Tuple.Create(isChild, this.panelName, prefabDir));
OpenPanel(isChild, prefabDir);
}
}
if (GUI_Button("打开脚本"))
{
if (isChild)
{
string scriptTarget = Path.Combine(currentPanelScriptDir.FullName, $"{panelName}.cs");
Application.OpenURL(scriptTarget);
}
else
{
var openPath = $"Scripts/HotUpdate/Game/UI/{systemName}/{panelName}/{panelName}.cs";
string scriptTarget = Path.Combine(Application.dataPath, openPath).Replace('\\', '/');
Application.OpenURL(scriptTarget);
}
}
if (GUI_Button("创建子面板"))
{
int controlID = GUIUtility.GetControlID(FocusType.Keyboard);
GUIUtility.keyboardControl = controlID; // 退出输入状态
//this.currentPanelScriptDir = new DirectoryInfo(currentPanelScriptDir.FullName);
IsCreateChild = true;
}
if (isExa == false)
if (GUI_Button("删除界面"))
{
bool confirm = EditorUtility.DisplayDialog("是否删除?", "是否删除" + panelName, "确定", "取消");
if (confirm)
{
Directory.Delete(prefabDir, true);
File.Delete(prefabDir + ".meta");
if (isChild)
{
if (currentPanelScriptDir != null)
{
File.Delete(currentPanelScriptDir.FullName + ".meta");
currentPanelScriptDir.Delete(true);
}
}
else
{
string scriptFolder = $"{Application.dataPath}/Scripts/HotUpdate/Game/UI/{systemName}/{panelName}";
Directory.Delete(scriptFolder, true);
File.Delete(scriptFolder + ".meta");
}
AssetDatabase.Refresh();
}
}
GUILayout.Space(10);
}
else
{
string sysFolder = Path.Combine(Application.dataPath, "Scripts/HotUpdate/Game/UI");
var sysF = new DirectoryInfo(sysFolder);
var dirs = sysF.GetDirectories();
string[] dirNames = new string[dirs.Length];
for (int i = 0; i < dirs.Length; i++)
dirNames[i] = dirs[i].Name;
// 绘制下拉框控件并将选定的选项索引保存到selectedOptionIndex变量中
this.selectedOptionIndex = PlayerPrefs.GetInt("editor_system");
selectedOptionIndex = EditorGUILayout.Popup("创建到目录:", selectedOptionIndex, dirNames);
PlayerPrefs.SetInt("editor_system", this.selectedOptionIndex);
if (GUI_Button("创建面板"))
CreatePanel(dirNames[selectedOptionIndex]);
}
}
}
public class GenerateAttData
{
public string prefix;
public string description;
public string type;
public string[] usin;
public GenerateAttData(string prefix, string description, string type, params string[] usin)
{
this.prefix = prefix;
this.description = description;
this.type = type;
this.usin = usin;
}
}
}
#endif