找回密码
 立即注册
查看: 448|回复: 0

Unity热更新Lua 插件Emmylua 补上CS代码属性方法呼出小工具

[复制链接]
发表于 2022-9-1 12:48 | 显示全部楼层 |阅读模式
使用Emmylua的时候尽管可以给本身Lua文件编辑属性函数注解,但是缺少Unity端CS代码的属性方法呼出, 平时也需要经常使用到。写了个小工具,按照Emmylua的规格导出一份CS代码类属性方法等的注解文件,以方便使用CS代码。



编译CS代码为Lua注解, 自己写的代码也一并导出



小工具位置和导出位置



导出的GameObject的示例



使用方法一



使用方法二

//Author : Bell
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEditor;
using UnityEngine;

namespace Assets.Editor
{
    public class GeneratorCSToLuaComment
    {
        public static string OutPutPath = Application.dataPath + "/LuaScripts/src/Gen/Comment/";
        public static List<string> FilterFileNameList = new List<string>() { "CS_UnityEditor", "_Editor_" , "CS_Microsoft_" ,"CS_System_"};

        [MenuItem("Lua/Generate Comment", false)]
        public static void GenAllComment()
        {
            List<Type> typeList = GetCSTypes();
            int removed = typeList.RemoveAll(type => type.BaseType != null && type.BaseType == typeof(UnityEditor.Editor));


            GenLuaCommentReference(typeList, OutPutPath);
        }
        public static List<Type> GetAllTypes(bool exclude_generic_definition = true)
        {
            List<Type> allTypes = new List<Type>();
            var assemblies = AppDomain.CurrentDomain.GetAssemblies();
            for (int i = 0; i < assemblies.Length; i++)
            {
                try
                {
                    allTypes.AddRange(assemblies.GetTypes().Where(type => exclude_generic_definition ? !type.IsGenericTypeDefinition : true));
                }
                catch (Exception)
                {
                }
            }

            return allTypes;
        }
        public static List<Type> GetCSTypes()
        {
            List<Type> list = GetAllTypes();

            //extra class
            list.Add(typeof(UnityEngine.AI.NavMesh));
            list.Add(typeof(UnityEngine.AI.NavMeshAgent));
            return list;
        }
        /// <summary>
        /// 编译出Lua注释引用追踪
        /// </summary>
        /// <param name="types"></param>
        /// <param name="save_path"></param>
        static void GenLuaCommentReference(IEnumerable<Type> types, string save_path)
        {
            if (!Directory.Exists(save_path)) Directory.CreateDirectory(save_path);

            int exportCount = 0;
            Dictionary<string, bool> existPropertyMethodDic = new Dictionary<string, bool>();
            Dictionary<string, bool> existClassDic = new Dictionary<string, bool>();
            types = types.Where(type => !type.IsEnum);
            foreach (var wrap_type in types)
            {
                existPropertyMethodDic.Clear();
                var fullName = null != wrap_type.DeclaringType ? wrap_type.DeclaringType.FullName : wrap_type.FullName;

                if (fullName.Contains("+")) continue;

                string classFileName = "CS_" + NormalizeName(fullName);
                if (existClassDic.ContainsKey(classFileName))
                {
                    Debug.Log("Duplicate Class :" + classFileName);
                    continue;
                }
                existClassDic[classFileName] = true;
               

                if (IsFilterFileName(classFileName))
                {
                    Debug.Log("Skip Editor Class :" + classFileName);
                    continue;
                }

                if (!IsFileNameValid(classFileName))
                {
                    Debug.Log("Skip InValid File Name :" + classFileName);
                    continue;
                }

                string filePath = save_path + classFileName + ".lua";
                try
                {
                    using (StreamWriter textWriter = new StreamWriter(filePath, false, Encoding.UTF8))
                    {
                        textWriter.WriteLine("---AUTO GENERATE! DON'T CHANGE!!!---");
                        textWriter.WriteLine("---  Do not require this in another lua file!   ---");
                        textWriter.WriteLine();

                        //string fullName = wrap_type.FullName;
                        //int plusIndex = fullName.IndexOf("+");
                        //if (0 < plusIndex) fullName = fullName.Substring(0, plusIndex);
                        textWriter.WriteLine("---@class CS." + fullName);
                        //属性
                        FieldInfo[] fields = wrap_type.GetFields();
                        foreach (FieldInfo field in fields)
                        {
                            if (!existPropertyMethodDic.ContainsKey(field.Name) && !field.Name.Contains("<>"))
                            {
                                textWriter.WriteLine("---@field " + ConverInValidName(field.Name) + " " + ConvertTypeToLuaType(field.FieldType.FullName));
                                existPropertyMethodDic[field.Name] = true;
                            }

                        }
                        PropertyInfo[] pInfos = wrap_type.GetProperties();
                        foreach (PropertyInfo property in pInfos)
                        {
                            if (!existPropertyMethodDic.ContainsKey(property.Name) && !property.Name.Contains("<>"))
                            {
                                textWriter.WriteLine("---@field " + ConverInValidName(property.Name) + " " + ConvertTypeToLuaType(property.PropertyType.FullName));
                                existPropertyMethodDic[property.Name] = true;
                            }
                        }

                        var methods = wrap_type.GetRuntimeMethods();
                        textWriter.WriteLine("local proto = {}");
                        foreach (var method in methods)
                        {
                            if (!existPropertyMethodDic.ContainsKey(method.Name)
                                && method.Name.IndexOf("set_") != 0
                                && method.Name.IndexOf("get_") != 0
                                && !method.Name.Contains(".")
                                && !method.Name.Contains("<"))
                            {
                                ParameterInfo[] prms = method.GetParameters();
                                string paramChars = "";
                                int index = 0;
                                foreach (ParameterInfo parameter in prms)
                                {
                                    string prmType = parameter.ParameterType.ToString().Replace("&", "");
                                    prmType = ConvertTypeToLuaType(prmType);

                                    textWriter.WriteLine("---@param " + ConverInValidName(parameter.Name) + " " + prmType);
                                    if (index < prms.Length - 1) paramChars += ConverInValidName(parameter.Name) + ",";
                                    else paramChars += ConverInValidName(parameter.Name);
                                    index++;
                                }

                                string returnType = method.ReturnType.ToString().Replace("&", "");
                                if (returnType != "System.Void")
                                {
                                    textWriter.WriteLine("---@return " + ConvertTypeToLuaType(returnType));
                                }

                                if (method.IsStatic)
                                {
                                    textWriter.WriteLine("function proto." + method.Name + "(" + paramChars + ")  end");
                                }
                                else
                                {
                                    textWriter.WriteLine("function proto:" + method.Name + "(" + paramChars + ")  end");
                                }

                                existPropertyMethodDic[method.Name] = true;
                            }
                        }

                        //| BindingFlags.Public | BindingFlags.Static
                    }
                    exportCount ++;
                    Debug.Log("Generated Class :" + filePath);
                }
                catch
                {
                    Debug.Log("Can't export " + filePath);
                }
            }

            Debug.Log("Exported Lua files : " + exportCount);
        }

        static string ConvertTypeToLuaType(string typeName)
        {
            switch (typeName)
            {
                case "System.Boolean": return "boolean";
                case "System.Object": return "object";
                case "System.Int32": return "number";
                case "System.Single": return "number"; //float
                case "System.String": return "string";
                case "System.UInt64": return "number";
                case "System.Double": return "number";

                case "System.Boolean[]": return "boolean[]";
                case "System.Object[]": return "object[]";
                case "System.Int32[]": return "number[]";
                case "System.Single[]": return "number[]";
                case "System.String[]": return "string[]";
                case "System.UInt64[]": return "number[]";
                case "System.Double[]": return "number[]";
                default: return "CS." + typeName;
            }
        }

        static string ConverInValidName(string paramName)
        {
            switch (paramName)
            {
                case "end": return "_end_";
                case "local": return "_local_";
                case "function": return "_function_";
                default: return paramName;
            }
        }
        

        static bool IsFilterFileName(string fileName)
        {
            foreach (string filter in FilterFileNameList)
            {
                if (fileName.Contains(filter))
                {
                    return true;
                }
            }
            return false;
        }
        static string NormalizeName(string name)
        {
            return name.Replace("+", "_").Replace(".", "_").Replace("`", "_").Replace("&", "_").Replace("[", "_").Replace("]", "_").Replace(",", "_");
        }
        private static bool IsFileNameValid(string name)
        {
            bool isFilename = true;
            string[] errorStr = new string[] { "/", "\\", ":", ",", "*", "?", "\"", "<", ">", "|" };
            if (string.IsNullOrEmpty(name))
            {
                isFilename = false;
            }
            else
            {
                for (int i = 0; i < errorStr.Length; i++)
                {
                    if (name.Contains(errorStr))
                    {
                        isFilename = false;
                        break;
                    }
                }
            }
            return isFilename;
        }
    }
}

小工具还有很多不足,比如泛型问题。
编译导出的文件只是注解文件,不需要和其他热更新文件一起打包。
如有需要可以在工具中修改导出位置, 添加一些想排除的类型等 (请随意)。
EmmyLuaUnity插件 好像也有这个功能。求推荐更好的插件。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-23 12:44 , Processed in 0.093162 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表