Unity 拓展编辑器 - 导出UI预制体lua文件

使用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")]

猜你喜欢

转载自blog.csdn.net/weixin_41316824/article/details/126016276