FairyGUI-Unity侧菜单扩展

目录

缘由:

分析: 

准备:

完整代码:

缘由:

在使用FairyGUI作为项目UI开发时,有时会使用FairyGUI提供的Scripting Define Symbols。当前FairyGUI中的Scripting Define Symbols有:

骨骼动画 Spine:FAIRYGUI_SPINE,龙骨:FAIRYGUI_DRAGONBONES

字体 TextMeshPro:FAIRYGUI_TMPRO

使用ToLua:FAIRYGUI_TOLUA

使用Puerts:FAIRYGUI_PUERTS

显示阿拉伯文本:RTL_TEXT_SUPPORT

UI自动化测试:FAIRYGUI_TEST

 为了方便开发,以上Scripting Define Symbols我将其做成了Unity的菜单,直接看完整代码。

分析: 

在开始制作菜单之前,需要做一些准备工作。比如上述的Scripting Define Symbols是否需要额外的Unity资源包,是否需要熟悉Unity编辑器中的一些方法才能进行?

而且需要注意的是新建的Unity工程中,TextMeshPro组件的必要资源库是需要手动导入或引用的,所以在使用FGUI提供的FAIRYGUI_TMPRO时,需要对TextMeshPro包进行检测。

对于编辑器中的Scripting Define Symbols设置可以通过方法GetScriptingDefineSymbolsForGrouphSetScriptingDefineSymbolsForGroup进行操作。菜单状态则可以通过方法:GetCheckedSetChecked来进行操作。菜单状态的变化Scripting Define Symbols设置有关,所以还要自定义一个同步刷新菜单状态的方法RefreshMenuState

准备:

分析结束,我们在代码中写一下上面的分析结果。新建脚本EditorMenuTool,空间名设置为FairyGUIEditor,添加对UnityEditor的引用。将新建的EditorMenuTool脚本,放到FairyGUI的Editor目录下(也可以根据项目目录结构放置),打开脚本。

#if UNITY_EDITOR
using System;
using UnityEditor;
using UnityEngine;

namespace FairyGUIEditor
{
    public class EditorMenuTool
    {
        
    }
}
#endif

Scripting Define Symbols的操作方法:

        /// 
        /// 获取Scripting Define Symbols的值
        /// 
        /// 
        private static string GetScriptingDefineSymbolsForGroup()
        {
            return PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
        }

        /// 
        /// 设置Scripting Define Symbols的值
        /// 
        /// 新的宏定义
        private static void SetScriptingDefineSymbolsForGroup(string newSymbol)
        {
            PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup,newSymbol);
        }

        /// 
        /// 检测Scripting Define Symbols中是否存在目标值
        /// 
        /// 目标宏
        /// 
        private static bool CheckScriptingDefineSymbolsExist(string define)
        {
            string symbol = GetScriptingDefineSymbolsForGroup();
            return symbol.Contains(define);
        }

        /// 
        /// 根据菜单状态修改目标宏
        /// 
        /// 目标宏定义
        /// 当前菜单状态
        private static void SwitchToTargetState(string define, bool menuState = false)
        {
            //检测目标宏定义
            if (define == null)
                return;

            //获取当前的宏定义
            string symbol = GetScriptingDefineSymbolsForGroup();
            
            if (menuState) //菜单已选中
            {
                //获取目标宏所在的位置
                int index = symbol.IndexOf(define);
                if (index < 0)
                    return;
                //如果不在第一个 则将其前面的分号删掉
                if (index > 0)
                    index -= 1;
                
                int length = define.Length;
                
                //如果当前宏长度大于要删除的当前长度,才会有分号
                if (symbol.Length > length)
                    length += 1;
                
                //删除目标宏定义
                symbol = symbol.Remove(index, length);
                SetScriptingDefineSymbolsForGroup(symbol);
            }
            else //菜单未选中
            {
                //如果当前的宏是空的,则直接将目标的宏加入
                if (symbol.Equals(string.Empty))
                    SetScriptingDefineSymbolsForGroup(define);
                else
                {
                    //否则,以分号分割加入目标宏
                    string newSymbol = symbol + ";" + define;
                    SetScriptingDefineSymbolsForGroup(newSymbol);
                }
            }
        }

包管理(PackageManager)的检测方法:

        /// 
        /// 检测目标包是否存在
        /// 
        /// 包名
        /// 
        private static void CheckTargetPackageExists(string packageName,Action callback = null)
        {
            // 创建一个ListRequest请求,用来查询PackageManager中已经安装的packages列表
            ListRequest request = Client.List();

            // 发送ListRequest请求,并在每一帧检查请求是否已经完成
            EditorApplication.CallbackFunction checkUpdateAction = null;
            checkUpdateAction = () =>
            {
                if (request.IsCompleted)
                {
                    bool packageExists = false;
                    EditorUtility.ClearProgressBar();

                    if (request.Status == StatusCode.Success)
                    {
                        // 遍历packages列表,查找目标packageName是否存在
                        foreach (var package in request.Result)
                        {
                            if (package.name.Contains(packageName) || package.displayName.Contains(packageName))
                            {
                                packageExists = true;
                                break;
                            }
                        }
                    }
                    else if (request.Status >= StatusCode.Failure)
                    {
                        Debug.LogError(request.Error.message);
                    }
                    callback?.Invoke(packageExists);

                    // 取消update回调函数
                    EditorApplication.update -= checkUpdateAction;
                }
            };
            EditorApplication.update += checkUpdateAction;
            EditorUtility.DisplayProgressBar("Check", "Please wait...", 0f);
        }

同步刷新菜单的方法:

/// 
/// 刷新菜单状态
/// 标签"UnityEditor.Callbacks.DidReloadScripts"可以在脚本编译完成后自动回调这个方法
/// 标签"RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)"在场景加载前执行这个方法
/// 
[UnityEditor.Callbacks.DidReloadScripts,RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void RefreshMenuState()
{
    //添加需要更新菜单
}

添加UnityEditor.Callbacks.DidReloadScriptsRuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)标签,会在脚本编译完或场景加载前进行状态同步,是个很方便的功能。举例:和小伙伴们一起开发时,只需要自己这边配置好,同步到其他小伙伴那边时,他们的编译器自动就会刷新新配置对应菜单状态。 

完整代码:

#if UNITY_EDITOR
using System;
using UnityEditor;
using UnityEngine;
using UnityEditor.PackageManager;
using UnityEditor.PackageManager.UI;
using UnityEditor.PackageManager.Requests;

namespace FairyGUIEditor
{
    public class EditorMenuTool
    {
        #region Define Symbols
        
        /// 
        ///  骨骼动画  Spine:FAIRYGUI_SPINE,龙骨:FAIRYGUI_DRAGONBONES
        /// 
        private const string DefineSpine = "FAIRYGUI_SPINE", DefineDragonBones = "FAIRYGUI_DRAGONBONES";
        /// 
        ///  字体 TextMeshPro :FAIRYGUI_TMPRO
        /// 
        private const string DefineTMP = "FAIRYGUI_TMPRO";
        /// 
        ///  使用ToLua :FAIRYGUI_TOLUA
        /// 
        private const string DefineToLua = "FAIRYGUI_TOLUA";
        /// 
        ///  使用Puerts :FAIRYGUI_PUERTS
        /// 
        private const string DefinePuerts = "FAIRYGUI_PUERTS";
        /// 
        ///  阿拉伯语言文字显示 :RTL_TEXT_SUPPORT
        /// 
        private const string DefineRTL = "RTL_TEXT_SUPPORT";
        /// 
        ///  UI自动化测试 :FAIRYGUI_TEST
        /// 
        private const string DefineAirTest = "FAIRYGUI_TEST";

        #endregion

        private const string PackageName = "TextMeshPro";
        private const string TextMeshProCheckPkg = "FairyGUI/TextMeshPro/Check Package";
        private const string TextMeshProEssential = "FairyGUI/TextMeshPro/Import TMP Essential Resources";
        
        private const string MenuNameTextMeshPro = "FairyGUI/TextMeshPro/Use TextMeshPro";
        private const string MenuNameSpine = "FairyGUI/Use Spine";
        private const string MenuNameDragonBones = "FairyGUI/Use DragonBones";
        private const string MenuNameToLua = "FairyGUI/Use ToLua";
        private const string MenuNamePuerts = "FairyGUI/Use Puerts";
        private const string MenuNameRTLTextSupport = "FairyGUI/Use RTLTextSupport";
        private const string MenuNameAirTest = "FairyGUI/Use AirTest";
        /// 
        /// 检测TextMeshPro包是否存在
        /// 
        [MenuItem(TextMeshProCheckPkg,false,1000)]
        private static void CheckTextMeshProPackage()
        {
            CheckTargetPackageExists(PackageName, b =>
            {
                //不存在则打开包管理面板,并搜索
                if (!b)
                {
                    //UnityEditor.PackageManager.UI
                    Window.Open(PackageName);
                }
                else
                {
                    //提示已经安装了
                    EditorUtility.DisplayDialog("Tips", 
                        $"{PackageName} package is installed!\n{PackageName}包已存在!","Ok");
                }
            });
        }

        /// 
        /// 导入TextMeshPro包必备资源
        /// 
        [MenuItem(TextMeshProEssential,false,1015)]
        private static void ImportTMPEssentialResources()
        {
            //做一次安全检查
            if (UnityEditor.PackageManager.PackageInfo.FindForAssetPath("Packages/com.unity.textmeshpro/Scripts/Runtime/TextMeshPro.cs") == null) {
                Debug.LogError("TextMeshPro package is not installed.");
                return;
            }
            //执行目标菜单项
            EditorApplication.ExecuteMenuItem("Window/TextMeshPro/Import TMP Essential Resources");
        }

        /// 
        /// 启用或关闭TextMeshPro支持
        /// 
        [MenuItem(MenuNameTextMeshPro,false,1030)]
        private static void SelectTextMeshPro()
        {
            //获取当前状态
            bool state = Menu.GetChecked(MenuNameTextMeshPro);
            //切换宏定义状态
            SwitchToTargetState(DefineTMP,state);
            //刷新菜单状态
            RefreshMenuState();
            //更新
            AssetDatabase.Refresh();
        }

        /// 
        /// 启用或关闭Spine
        /// 
        [MenuItem(MenuNameSpine,false,1015)]
        private static void SelectSpine()
        {
            //获取当前状态
            bool state = Menu.GetChecked(MenuNameSpine);
            //切换宏定义状态
            SwitchToTargetState(DefineSpine,state);
            //刷新菜单状态
            RefreshMenuState();
            //更新
            AssetDatabase.Refresh();
        }

        /// 
        /// 启用或关闭DragonBones
        /// 
        [MenuItem(MenuNameDragonBones,false,1030)]
        private static void SelectDragonBones()
        {
            //获取当前状态
            bool state = Menu.GetChecked(MenuNameDragonBones);
            //切换宏定义状态
            SwitchToTargetState(DefineDragonBones,state);
            //刷新菜单状态
            RefreshMenuState();
            //更新
            AssetDatabase.Refresh();
        }

        /// 
        /// 启用或关闭ToLua
        /// 
        [MenuItem(MenuNameToLua,false,1045)]
        private static void SelectToLua()
        {
            //获取当前状态
            bool state = Menu.GetChecked(MenuNameToLua);
            //切换宏定义状态
            SwitchToTargetState(DefineToLua,state);
            //刷新菜单状态
            RefreshMenuState();
            //更新
            AssetDatabase.Refresh();
        }

        /// 
        /// 启用或关闭Puerts
        /// 
        [MenuItem(MenuNamePuerts,false,1060)]
        private static void SelectPuerts()
        {
            //获取当前状态
            bool state = Menu.GetChecked(MenuNamePuerts);
            //切换宏定义状态
            SwitchToTargetState(DefinePuerts,state);
            //刷新菜单状态
            RefreshMenuState();
            //更新
            AssetDatabase.Refresh();
        }

        /// 
        /// 启用或关闭阿拉伯语言文字
        /// 
        [MenuItem(MenuNameRTLTextSupport,false,1075)]
        private static void SelectRTLTextSupport()
        {
            //获取当前状态
            bool state = Menu.GetChecked(MenuNameRTLTextSupport);
            //切换宏定义状态
            SwitchToTargetState(DefineRTL,state);
            //刷新菜单状态
            RefreshMenuState();
            //更新
            AssetDatabase.Refresh();
        }

        /// 
        /// 启用或关闭UI自动化测试
        /// 
        [MenuItem(MenuNameAirTest,false,1090)]
        private static void SelectUIAirTest()
        {
            //获取当前状态
            bool state = Menu.GetChecked(MenuNameAirTest);
            //切换宏定义状态
            SwitchToTargetState(DefineAirTest,state);
            //刷新菜单状态
            RefreshMenuState();
            //更新
            AssetDatabase.Refresh();
        }
        
        /// 
        /// 刷新菜单状态
        /// 标签"UnityEditor.Callbacks.DidReloadScripts"可以在脚本编译完成后自动回调这个方法
        /// 标签"RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)"在场景加载前执行这个方法
        /// 
        [UnityEditor.Callbacks.DidReloadScripts,RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
        private static void RefreshMenuState()
        {
            //放置需要更新菜单
            Menu.SetChecked(MenuNameTextMeshPro, CheckScriptingDefineSymbolsExist(DefineTMP));
            Menu.SetChecked(MenuNameSpine, CheckScriptingDefineSymbolsExist(DefineSpine));
            Menu.SetChecked(MenuNameDragonBones, CheckScriptingDefineSymbolsExist(DefineDragonBones));
            Menu.SetChecked(MenuNameToLua, CheckScriptingDefineSymbolsExist(DefineToLua));
            Menu.SetChecked(MenuNamePuerts, CheckScriptingDefineSymbolsExist(DefinePuerts));
            Menu.SetChecked(MenuNameRTLTextSupport, CheckScriptingDefineSymbolsExist(DefineRTL));
            Menu.SetChecked(MenuNameAirTest, CheckScriptingDefineSymbolsExist(DefineAirTest));
        }

        #region Package Check

        /// 
        /// 检测目标包是否存在
        /// 
        /// 包名
        /// 
        private static void CheckTargetPackageExists(string packageName,Action callback = null)
        {
            // 创建一个ListRequest请求,用来查询PackageManager中已经安装的packages列表
            ListRequest request = Client.List();

            // 发送ListRequest请求,并在每一帧检查请求是否已经完成
            EditorApplication.CallbackFunction checkUpdateAction = null;
            checkUpdateAction = () =>
            {
                if (request.IsCompleted)
                {
                    bool packageExists = false;
                    EditorUtility.ClearProgressBar();

                    if (request.Status == StatusCode.Success)
                    {
                        // 遍历packages列表,查找目标packageName是否存在
                        foreach (var package in request.Result)
                        {
                            if (package.name.Contains(packageName) || package.displayName.Contains(packageName))
                            {
                                packageExists = true;
                                break;
                            }
                        }
                    }
                    else if (request.Status >= StatusCode.Failure)
                    {
                        Debug.LogError(request.Error.message);
                    }
                    callback?.Invoke(packageExists);

                    // 取消update回调函数
                    EditorApplication.update -= checkUpdateAction;
                }
            };
            EditorApplication.update += checkUpdateAction;
            EditorUtility.DisplayProgressBar("Check", "Please wait...", 0f);
        }

        #endregion
        
        #region Scripting Define Symbols

        /// 
        /// 获取Scripting Define Symbols的值
        /// 
        /// 
        private static string GetScriptingDefineSymbolsForGroup()
        {
            return PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
        }

        /// 
        /// 设置Scripting Define Symbols的值
        /// 
        /// 新的宏定义
        private static void SetScriptingDefineSymbolsForGroup(string newSymbol)
        {
            PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup,newSymbol);
        }

        /// 
        /// 检测Scripting Define Symbols中是否存在目标值
        /// 
        /// 目标宏
        /// 
        private static bool CheckScriptingDefineSymbolsExist(string define)
        {
            string symbol = GetScriptingDefineSymbolsForGroup();
            return symbol.Contains(define);
        }

        /// 
        /// 更新菜单状态修改目标宏
        /// 
        /// 目标宏定义
        /// 当前菜单状态
        private static void SwitchToTargetState(string define, bool menuState = false)
        {
            //检测目标宏定义
            if (define == null)
                return;

            //获取当前的宏定义
            string symbol = GetScriptingDefineSymbolsForGroup();
            
            if (menuState) //菜单已选中
            {
                //获取目标宏所在的位置
                int index = symbol.IndexOf(define);
                if (index < 0)
                    return;
                //如果不在第一个 则将其前面的分号删掉
                if (index > 0)
                    index -= 1;
                
                int length = define.Length;
                
                //如果当前宏长度大于要删除的当前长度,才会有分号
                if (symbol.Length > length)
                    length += 1;
                
                //删除目标宏定义
                symbol = symbol.Remove(index, length);
                SetScriptingDefineSymbolsForGroup(symbol);
            }
            else //菜单未选中
            {
                //如果当前的宏是空的,则直接将目标的宏加入
                if (symbol.Equals(string.Empty))
                    SetScriptingDefineSymbolsForGroup(define);
                else
                {
                    //否则,以分号分割加入目标宏
                    string newSymbol = symbol + ";" + define;
                    SetScriptingDefineSymbolsForGroup(newSymbol);
                }
            }
        }

        #endregion
        
    }
}

#endif

你可能感兴趣的:(Unity,FairyGUI,unity,FairyGUI)