使用oldin插件 + Unity自带拓展编辑器编写
实现效果:
界面配置基类:UIDialogConfig
using Sirenix.OdinInspector;
using UnityEngine;
public class UIDialogConfig : MonoBehaviour
{
[LabelText("界面层级")]
public UIDialogLayerType layerType = UIDialogLayerType.PANEL;
}
在为界面配置类 编写一个专门的Inspector显示类:DialogConfigEditor
using System;
using Sirenix.OdinInspector.Editor;
using Sirenix.Utilities.Editor;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Editor.UI.Dialog
{
[CustomEditor(typeof(UIDialogConfig))]
public class DialogConfigEditor : OdinEditor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
GUIHelper.PushColor(Color.green);
if(GUILayout.Button("导出",GUILayout.Height(40)))
{
UIDialogExport.ExportDialog((target as UIDialogConfig)?.gameObject);
}
GUIHelper.PopColor();
SirenixEditorGUI.BeginBox("导出文件",true);
UIDialogExport.GetDialogExportPathAndName((target as UIDialogConfig)?.gameObject, out string dialogPath, out string dialogName);
UIDialogExport.GetDialogSkinExportPathAndName((target as UIDialogConfig)?.gameObject, out string dialogSkinPath, out string dialogSkinName);
UIDialogExport.GetDialogConfigExportPathAndName((target as UIDialogConfig)?.gameObject, out string dialogConfigPath, out string dialogConfigName);
CreatePath("Skin:",dialogName,dialogPath);
CreatePath("Dialog:",dialogSkinName,dialogSkinPath);
CreatePath("DialogConfig:",dialogConfigName,dialogConfigPath);
SirenixEditorGUI.EndBox();
}
public void CreatePath(string title,string cname,string path)
{
GUILayout.BeginHorizontal();
string filePath = path + cname + ".lua";
var obj = AssetDatabase.LoadAssetAtPath<Object>(filePath);
EditorGUILayout.TextField(title,obj == null ? String.Empty : filePath);
if (GUILayout.Button("定位",GUILayout.Width(40)))
{
if(obj != null)
EditorGUIUtility.PingObject(obj);
}
GUILayout.EndHorizontal();
}
}
}
编写一个组件收集器 用于提取出需要动态修改、操作的组件如:按钮,图片等
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sirenix.OdinInspector;
using UnityEditor;
using UnityEngine;
/// <summary>
/// 组件收集器
/// </summary>
public class UIComponentGatherer : UIComponentBase
{
public override UIComponentType CompType => UIComponentType.None;
[BoxGroup("基础属性",true,true,1),
Button("收集组件",ButtonSizes.Large),
GUIColor(0,1,0)]
public void GathererComponents()
{
Gatherer();
}
[ReadOnly]
[SerializeField]
[PropertyOrder(2)]
[LabelText("动态组件列表")]
[ListDrawerSettings(
DraggableItems = false, IsReadOnly = true,
NumberOfItemsPerPage = 10, ShowItemCount = true,
OnBeginListElementGUI = "BeginDrawListElement",
OnEndListElementGUI = "EndDrawListElement")]
public List<UIBaseProxy> dynamicProxies;
[ReadOnly]
[PropertyOrder(3)]
[LabelText("静态组件列表")]
[ListDrawerSettings(DraggableItems = false,IsReadOnly = true,NumberOfItemsPerPage = 10, ShowItemCount = true)]
public List<UIBaseProxy> staticProxies;
private void BeginDrawListElement(int index)
{
if (dynamicProxies[index] != null)
{
UIBaseProxy baseProxy = dynamicProxies[index] as UIBaseProxy;
Sirenix.Utilities.Editor.SirenixEditorGUI.BeginBox(baseProxy != null
? baseProxy.SelfName
: "default");
}
}
private void EndDrawListElement(int index)
{
if (dynamicProxies != null && dynamicProxies[index] != null)
Sirenix.Utilities.Editor.SirenixEditorGUI.EndBox();
}
private bool Gatherer()
{
Dictionary<string, Component> componentDic = new Dictionary<string, Component>();
dynamicProxies = new List<UIBaseProxy>();
staticProxies = new List<UIBaseProxy>();
UIBaseProxy[] proxies = transform.GetComponentsInChildren<UIBaseProxy>();
for (int i = 0; i < proxies.Length; i++)
{
if(proxies[i] == this) continue;
if (componentDic.ContainsKey(proxies[i].SelfName))
{
string path1 = GetInspectorPath(componentDic[proxies[i].SelfName].transform);
string path2 = GetInspectorPath(proxies[i].transform);
if (EditorUtility.DisplayDialog("收集组件",path1 + "\r\n" + path2 + "\r\n组件名称相同请修改","确定"))
{
dynamicProxies = null;
staticProxies = null;
}
return false;
}
if (proxies[i].IsDynamic)
dynamicProxies.Add(proxies[i]);
else
staticProxies.Add(proxies[i]);
componentDic.Add(proxies[i].SelfName,proxies[i]);
}
return true;
}
private string GetInspectorPath(Transform tran)
{
string path = tran.name;
while (true)
{
if(tran.parent == null) break;
path = tran.parent.name + "/" + path;
tran = tran.parent;
}
return path;
}
}
最后编写一个导出类:UIDialogExport,这里使用一个模板.txt的文件将里边的类名替换成界面或者模块的名称
using System;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
namespace Editor.UI.Dialog
{
public static partial class UIDialogExport
{
private const string REPLACE_TEMPLATE_DIALOG_NAME = "$DIALOG_NAME$";
private const string UI_DIALOG_TEMPLATE_FILE = "Assets/Scripts/CS/Core/UI/LuaTemplate/UIDialogTemplate.txt"; // dialog模板文件
private const string UI_DIALOG_SKIN_TEMPLATE_FILE = "Assets/Scripts/CS/Core/UI/LuaTemplate/UIDialogSkinTemplate.txt"; // dialogSkin模板文件
private const string UI_DIALOG_ROOT_PATH = "Assets/Resources/UI/Prefab/";
public const string UI_DIALOG_EXPORT_ROOT_PATH = "Assets/Scripts/Lua/Logic/UI/";
public const string UI_DIALOG_EXPORT_ROOT_SKIN_PATH = "Assets/Scripts/Lua/Logic/UI/Export/";
public static void ExportDialog(GameObject obj)
{
var prefabAssetType = PrefabUtility.GetPrefabAssetType(obj);
var canExportPrefab = prefabAssetType == PrefabAssetType.NotAPrefab ||
(prefabAssetType == PrefabAssetType.Regular && PrefabUtility.HasPrefabInstanceAnyOverrides(obj, false));
if (null == obj || canExportPrefab)
{
if (EditorUtility.DisplayDialog("导出失败", "该游戏物体不是预制体", "确定"))
{
}
return;
}
GetDialogExportPathAndName(obj,out string exportPath,out string dialogName);
GetDialogSkinExportPathAndName(obj,out string exportSkinPath,out string dialogSkinName);
UIDialogConfig uIDialogConfig = obj.GetComponent<UIDialogConfig>();
ExportDialogLua(exportPath,dialogName);
ExportDialogSkin(uIDialogConfig,exportSkinPath,dialogSkinName);
}
public static void GetDialogExportPathAndName(GameObject obj,out string exportPath,out string dialogName)
{
string prefabPath = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(obj);
exportPath = prefabPath.Replace(UI_DIALOG_ROOT_PATH, UI_DIALOG_EXPORT_ROOT_PATH).Replace(obj.name + ".prefab",String.Empty);
dialogName = obj.name.Replace("Win_", String.Empty).Replace("_",String.Empty);
}
public static void GetDialogSkinExportPathAndName(GameObject obj,out string exportPath,out string dialogSkinName)
{
string prefabPath = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(obj);
exportPath = prefabPath.Replace(UI_DIALOG_ROOT_PATH, UI_DIALOG_EXPORT_ROOT_SKIN_PATH).Replace(obj.name + ".prefab",String.Empty);
dialogSkinName = obj.name.Replace("Win_", String.Empty).Replace("_",String.Empty) + "Skin";
}
public static void GetDialogConfigExportPathAndName(GameObject obj,out string exportPath,out string dialogConfigName)
{
string prefabPath = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(obj);
exportPath = prefabPath.Replace(UI_DIALOG_ROOT_PATH, UI_DIALOG_EXPORT_ROOT_SKIN_PATH).Replace(obj.name + ".prefab",String.Empty);
dialogConfigName = obj.name.Replace("Win_", String.Empty).Replace("_",String.Empty) + "Config";
}
private static void ExportDialogLua(string exportPath,string dialogName)
{
if (!Directory.Exists(exportPath))
{
Directory.CreateDirectory(exportPath);
}
string dailogFilePath = $"{exportPath}{dialogName}{".lua"}";
if (File.Exists(dailogFilePath)) return;
StreamReader sr = File.OpenText(UI_DIALOG_TEMPLATE_FILE);
StreamWriter sw = File.CreateText(dailogFilePath);
string template = sr.ReadToEnd();
sw.Write(template.Replace(REPLACE_TEMPLATE_DIALOG_NAME ,dialogName));
sw.Close();
sr.Close();
AssetDatabase.Refresh();
}
private static void ExportDialogSkin(UIDialogConfig uIDialogConfig,string exportPath,string dialogName)
{
if (!Directory.Exists(exportPath))
{
Directory.CreateDirectory(exportPath);
}
string dailogFilePath = $"{exportPath}{dialogName}{".lua"}";
StreamReader sr = File.OpenText(UI_DIALOG_SKIN_TEMPLATE_FILE);
StreamWriter sw = File.CreateText(dailogFilePath);
string template = sr.ReadToEnd();
StringBuilder sb = new StringBuilder();
UIBaseProxy[] proxies = uIDialogConfig.GetComponent<UIComponentGatherer>().dynamicProxies.ToArray();
for (int i = 0; i < proxies.Length; i++)
{
string parm = "self._" + proxies[i].SelfName + " = nil";
sb.AppendLine(parm);
}
template = template.Replace("$DIALOG_SKIN$", sb.ToString());
sw.Write(template.Replace(REPLACE_TEMPLATE_DIALOG_NAME ,dialogName));
sw.Close();
sr.Close();
AssetDatabase.Refresh();
}
private static void ExportDialogConfig()
{
}
}
}
至此基本算完事
最要API:
定位文件功能:
扫描二维码关注公众号,回复:
14654027 查看本文章
string path = "Assets/Scripts/Lua/Core/UI/Component/UIBaseProxy.lua";
var obj = AssetDatabase.LoadAssetAtPath<Object>(path);
if(obj != null)
EditorGUIUtility.PingObject(obj);
Unity系统弹窗:
EditorUtility.DisplayDialog("导出失败", "该游戏物体不是预制体", "确定")
创建文件夹
Directory.CreateDirectory(exportPath);
创建文件并写入文件:
if (File.Exists(dailogFilePath)) return;
StreamReader sr = File.OpenText(UI_DIALOG_TEMPLATE_FILE);
StreamWriter sw = File.CreateText(dailogFilePath);
string template = sr.ReadToEnd();
sw.Write(template.Replace(REPLACE_TEMPLATE_DIALOG_NAME ,dialogName));
sw.Close();
sr.Close();
刷新unity Asset下的文件
AssetDatabase.Refresh();
oldin 特性使用
- 只读特性 [ReadOnly]
- 特性组 [BoxGroup("基础属性",true,true,1)]
- 按钮特性 Button("收集组件",ButtonSizes.Large), GUIColor(0,1,0)]
- 列表特性:[TableList]
[ListDrawerSettings( DraggableItems = false, IsReadOnly = true, NumberOfItemsPerPage = 10, ShowItemCount = true, OnBeginListElementGUI = "BeginDrawListElement", OnEndListElementGUI = "EndDrawListElement")]