RedZero9 发表于 2022-9-1 12:48

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

使用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_"};

      
      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 = 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 = 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 = 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 = 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插件 好像也有这个功能。求推荐更好的插件。
页: [1]
查看完整版本: Unity热更新Lua 插件Emmylua 补上CS代码属性方法呼出小工具