• tolua源码分析(十一)代码生成


    tolua源码分析(十一)代码生成

    上一节我们分析了tolua中struct数据在lua和C#之间传递的过程,这一节我们来看一下tolua自动生成各种辅助代码的流程。

    生成所有代码的入口位于ToLuaMenu.cs的GenLuaAll

    [MenuItem("Lua/Generate All", false, 5)]
    static void GenLuaAll()
    {
        if (EditorApplication.isCompiling)
        {
            EditorUtility.DisplayDialog("警告", "请等待编辑器完成编译再执行此功能", "确定");
            return;
        }
    
        beAutoGen = true;
        GenLuaDelegates();
        AssetDatabase.Refresh();
        GenerateClassWraps();
        GenLuaBinder();
        beAutoGen = false;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    首先是生成委托的GenLuaDelegates

    [MenuItem("Lua/Gen Lua Delegates", false, 2)]
    static void GenLuaDelegates()
    {
        if (!beAutoGen && EditorApplication.isCompiling)
        {
            EditorUtility.DisplayDialog("警告", "请等待编辑器完成编译再执行此功能", "确定");
            return;
        }
    
        ToLuaExport.Clear();
        List list = new List();
        list.AddRange(CustomSettings.customDelegateList);
        HashSet set = GetCustomTypeDelegates();        
    
        foreach (Type t in set)
        {
            if (null == list.Find((p) => { return p.type == t; }))
            {
                list.Add(new DelegateType(t));
            }
        }
    
        ToLuaExport.GenDelegates(list.ToArray());
        set.Clear();
        ToLuaExport.Clear();
        AssetDatabase.Refresh();
        Debug.Log("Create lua delegate over");
    }    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    该函数首先对CustomSettings里的customDelegateList所包含的委托类型做处理,这个list里只需放系统内置的委托类型,自定义的委托类型不需要放在这里:

    //附加导出委托类型(在导出委托时, customTypeList 中牵扯的委托类型都会导出, 无需写在这里)
    public static DelegateType[] customDelegateList = 
    {        
        _DT(typeof(Action)),                
        _DT(typeof(UnityEngine.Events.UnityAction)),
        _DT(typeof(System.Predicate)),
        _DT(typeof(System.Action)),
        _DT(typeof(System.Comparison)),
        _DT(typeof(System.Func)),
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    DelegateType类主要包含type,strType,name三个成员,它们分别表示委托的类型,对应类型的字符串,以及tolua即将自动生成的类型名称:

    public DelegateType(Type t)
    {
        type = t;
        strType = ToLuaExport.GetTypeStr(t);                
        name = ToLuaExport.ConvertToLibSign(strType);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    对于customDelegateList中的委托类型,它们对应的这三个成员,打印如下:

    tolua源码分析(十一)1

    接下来,GetCustomTypeDelegates函数对CustomSettings里的customTypeList进行扫描,customTypeList包含所有要导出给lua的C#类型,通过扫描得到list中所有C#类型引用(通过field,property,method parameter引用)的委托类型。例如UnityEngine中的Camera类,它包含若干类型为CameraCallback的成员,而CameraCallback是一个委托类型:

    namespace UnityEngine
    {
        public sealed class Camera : Behaviour
        {
            public static CameraCallback onPreCull;
            public static CameraCallback onPreRender;
            public static CameraCallback onPostRender;
            public delegate void CameraCallback(Camera cam);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    由于Camera类在customTypeList中,所以这里的CameraCallback也会加入进来。IsDelegateType用于判断一个类型是否为需要加入的委托类型:

    public static bool IsDelegateType(Type t)
    {
        if (!typeof(System.MulticastDelegate).IsAssignableFrom(t) || t == typeof(System.MulticastDelegate))
        {
            return false;
        }        
    
        if (IsMemberFilter(t))
        {
            return false;
        }
    
        return true;
    }
    
    public static bool IsMemberFilter(Type t)
    {
        string name = LuaMisc.GetTypeName(t);
        return memberInfoFilter.Contains(t) || memberFilter.Find((p) => { return name.Contains(p); }) != null;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    其中IsMemberFilter函数可以过滤掉我们不想要导出的委托类型,也就是黑名单。

    得到所有要导出的委托类型之后,接下来就是正式的生成代码了,我们来看下GenDelegates这个函数,通过前几行代码就能发现,它是用于生成DelegateFactory.cs这个文件的:

    public static void GenDelegates(DelegateType[] list)
    {        
        ...
    
        sb.Append("public class DelegateFactory\r\n");
        sb.Append("{\r\n");        
        sb.Append("\tpublic delegate Delegate DelegateCreate(LuaFunction func, LuaTable self, bool flag);\r\n");
        sb.Append("\tpublic static Dictionary dict = new Dictionary();\r\n");
        sb.Append("\tstatic DelegateFactory factory = new DelegateFactory();\r\n");
        sb.AppendLineEx();
        sb.Append("\tpublic static void Init()\r\n");
        sb.Append("\t{\r\n");
        sb.Append("\t\tRegister();\r\n");
        sb.AppendLineEx("\t}\r\n");        
        
        sb.Append("\tpublic static void Register()\r\n");
        sb.Append("\t{\r\n");
        sb.Append("\t\tdict.Clear();\r\n");
    
        ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    我们知道DelegateFactory的作用就是记录不同委托类型所对应的转换,检查和push函数,分别用于将lua栈上的function转换为对应的委托类型,或者检查当前lua栈上的userdata,是否为对应的委托类型,以及将当前委托类型压入到lua栈上。tolua使用4个数据结构来管理这些函数,以System.Action为例:

    dict.Add(typeof(System.Action), factory.System_Action);
    DelegateTraits.Init(factory.System_Action);
    TypeTraits.Init(factory.Check_System_Action);
    StackTraits.Push = factory.Push_System_Action;
    
    • 1
    • 2
    • 3
    • 4

    对应这4句代码的生成逻辑如下:

    for (int i = 0; i < list.Length; i++)
    {            
        string type = list[i].strType;
        string name = list[i].name;
        sb.AppendFormat("\t\tdict.Add(typeof({0}), factory.{1});\r\n", type, name);            
    }
    
    sb.AppendLineEx();
    
    for (int i = 0; i < list.Length; i++)
    {
        string type = list[i].strType;
        string name = list[i].name;            
        sb.AppendFormat("\t\tDelegateTraits<{0}>.Init(factory.{1});\r\n", type, name);                        
    }
    
    sb.AppendLineEx();
    
    for (int i = 0; i < list.Length; i++)
    {
        string type = list[i].strType;
        string name = list[i].name;            
        sb.AppendFormat("\t\tTypeTraits<{0}>.Init(factory.Check_{1});\r\n", type, name);            
    }
    
    sb.AppendLineEx();
    
    for (int i = 0; i < list.Length; i++)
    {
        string type = list[i].strType;
        string name = list[i].name;
        sb.AppendFormat("\t\tStackTraits<{0}>.Push = factory.Push_{1};\r\n", type, name);            
    }
    
    sb.Append("\t}\r\n");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    接下来要为这4句代码用到的方法进行生成,为了实现这些方法,还需要先生成辅助的委托类,依旧以System.Action为例:

    class System_Action_Event : LuaDelegate
    {
        public System_Action_Event(LuaFunction func) : base(func) { }
        public System_Action_Event(LuaFunction func, LuaTable self) : base(func, self) { }
    
        public void Call()
        {
            func.Call();
        }
    
        public void CallWithSelf()
        {
            func.BeginPCall();
            func.Push(self);
            func.PCall();
            func.EndPCall();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    这里的Call和CallWithSelf方法,是对lua的function进行了封装,方便赋值给System.Action对象用的。对应的生成逻辑如下:

    //生成委托类
    sb.AppendFormat("\tclass {0}_Event : LuaDelegate\r\n", name);
    sb.AppendLineEx("\t{");
    sb.AppendFormat("\t\tpublic {0}_Event(LuaFunction func) : base(func) {{ }}\r\n", name);
    sb.AppendFormat("\t\tpublic {0}_Event(LuaFunction func, LuaTable self) : base(func, self) {{ }}\r\n", name);
    sb.AppendLineEx();
    sb.AppendFormat("\t\tpublic {0} Call({1})\r\n", GetTypeStr(mi.ReturnType), args);
    GenDelegateBody(sb, t, "\t\t");
    sb.AppendLineEx();
    sb.AppendFormat("\t\tpublic {0} CallWithSelf({1})\r\n", GetTypeStr(mi.ReturnType), args);
    GenDelegateBody(sb, t, "\t\t", true);
    sb.AppendLineEx("\t}\r\n");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    有了辅助类之后,就可以方便地将lua function转换为C#的委托类型:

    public System.Action System_Action(LuaFunction func, LuaTable self, bool flag)
    {
        if (func == null)
        {
            System.Action fn = delegate() { };
            return fn;
        }
    
        if(!flag)
        {
            System_Action_Event target = new System_Action_Event(func);
            System.Action d = target.Call;
            target.method = d.Method;
            return d;
        }
        else
        {
            System_Action_Event target = new System_Action_Event(func, self);
            System.Action d = target.CallWithSelf;
            target.method = d.Method;
            return d;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    这里的生成逻辑如下:

    //生成转换函数
    sb.AppendFormat("\tpublic {0} {1}(LuaFunction func, LuaTable self, bool flag)\r\n", strType, name);
    sb.AppendLineEx("\t{");
    sb.AppendLineEx("\t\tif (func == null)");
    sb.AppendLineEx("\t\t{");
    sb.AppendFormat("\t\t\t{0} fn = delegate({1}) {2}", strType, args, GetDefaultDelegateBody(mi));
    sb.AppendLineEx("\t\t\treturn fn;");
    sb.AppendLineEx("\t\t}\r\n");
    sb.AppendLineEx("\t\tif(!flag)");
    sb.AppendLineEx("\t\t{");
    sb.AppendFormat("\t\t\t{0}_Event target = new {0}_Event(func);\r\n", name);
    sb.AppendFormat("\t\t\t{0} d = target.Call;\r\n", strType);
    sb.AppendLineEx("\t\t\ttarget.method = d.Method;");
    sb.AppendLineEx("\t\t\treturn d;");
    sb.AppendLineEx("\t\t}");
    sb.AppendLineEx("\t\telse");
    sb.AppendLineEx("\t\t{");
    sb.AppendFormat("\t\t\t{0}_Event target = new {0}_Event(func, self);\r\n", name);
    sb.AppendFormat("\t\t\t{0} d = target.CallWithSelf;\r\n", strType);
    sb.AppendLineEx("\t\t\ttarget.method = d.Method;");
    sb.AppendLineEx("\t\t\treturn d;");
    sb.AppendLineEx("\t\t}");
    sb.AppendLineEx("\t}\r\n");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    此外还要生成检查委托类型和push委托类型的代码。这里直接调用已有的方法即可,不需要再额外实现,因此生成逻辑比较简单:

    sb.AppendFormat("\tbool Check_{0}(IntPtr L, int pos)\r\n", name);
    sb.AppendLineEx("\t{");
    sb.AppendFormat("\t\treturn TypeChecker.CheckDelegateType(typeof({0}), L, pos);\r\n", strType);
    sb.AppendLineEx("\t}\r\n");
    
    sb.AppendFormat("\tvoid Push_{0}(IntPtr L, {1} o)\r\n", name, strType);
    sb.AppendLineEx("\t{");
    sb.AppendLineEx("\t\tToLua.Push(L, o);");
    sb.AppendLineEx("\t}\r\n");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    自此,DelegateFactory的生成就结束了。下面就是为导出到lua的C#类生成对应的wrap类,也就是GenerateClassWraps函数。与委托类似,函数遍历CustomSettings里的customTypeList,它包含了所有要导出给lua的C#类型:

    //在这里添加你要导出注册到lua的类型列表
    public static BindType[] customTypeList =
    {                
        _GT(typeof(Component)),
        _GT(typeof(Transform)),
        _GT(typeof(Material)),
        ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    BindType主要包括以下成员:

    public class BindType
    {
        public string name;                 //类名称
        public Type type;
        public bool IsStatic;        
        public string wrapName = "";        //产生的wrap文件名字
        public string libName = "";         //注册到lua的名字
        public Type baseType = null;
        public string nameSpace = null;     //注册到lua的table层级
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    同样我们也打印下看看具体这些数据长什么样:

    tolua源码分析(十一)2

    IsStatic字段表示是否作为静态类导出,截图中可以看到UnityEngine.Application和UnityEngine.Physics的IsStatic字段为true,这两个类中的方法和成员基本都是static的,作为静态类导出可以比普通类要节省一些开销,static class在lua层实现中只有一个ref table,而且index和newindex元方法也不会递归查找,因为static class不存在基类的概念。tolua中abstract和sealed的类会自动识别为静态类,如果要手动添加则需要在CustomSettings里的staticClassTypes中添加:

    //导出时强制做为静态类的类型(注意customTypeList 还要添加这个类型才能导出)
    //unity 有些类作为sealed class, 其实完全等价于静态类
    public static List staticClassTypes = new List
    {        
        typeof(UnityEngine.Application),
        typeof(UnityEngine.Time),
        typeof(UnityEngine.Screen),
        typeof(UnityEngine.SleepTimeout),
        typeof(UnityEngine.Input),
        typeof(UnityEngine.Resources),
        typeof(UnityEngine.Physics),
        typeof(UnityEngine.RenderSettings),
        typeof(UnityEngine.QualitySettings),
        typeof(UnityEngine.GL),
        typeof(UnityEngine.Graphics),
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    接下来就是遍历这个BindType类型的list,对每个type生成对应的wrap文件。这个逻辑在Generate函数中:

    public static void Generate(string dir)
    {
        sb = new StringBuilder();
        usingList.Add("System");
    
        if (wrapClassName == "")
        {
            wrapClassName = className;
        }
    
        if (type.IsEnum)
        {
            BeginCodeGen();
            GenEnum();                                    
            EndCodeGen(dir);
            return;
        }
    
        InitMethods();
        InitPropertyList();
        InitCtorList();
    
        BeginCodeGen();
    
        GenRegisterFunction();
        GenConstructFunction();
        GenItemPropertyFunction();             
        GenFunctions();
        GenIndexFunc();
        GenNewIndexFunc();
        GenOutFunction();
        GenEventFunctions();
    
        EndCodeGen(dir);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    BeginCodeGen和EndCodeGen函数的实现比较简单,就是生成一下wrap类的开头和结尾:

    static void BeginCodeGen()
    {
        sb.AppendFormat("public class {0}Wrap\r\n", wrapClassName);
        sb.AppendLineEx("{");
    }
    
    static void EndCodeGen(string dir)
    {
        sb.AppendLineEx("}\r\n");
        SaveFile(dir + wrapClassName + "Wrap.cs");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    先看一下枚举类型的生成,这是在GenEnum函数中完成的,我们以UnityEngine.Space这个枚举为例,看一下最后生成出来的UnityEngine_SpaceWrap文件:

    //this source code was auto-generated by tolua#, do not modify it
    using System;
    using LuaInterface;
    
    public class UnityEngine_SpaceWrap
    {
    	public static void Register(LuaState L)
    	{
    		L.BeginEnum(typeof(UnityEngine.Space));
    		L.RegVar("World", get_World, null);
    		L.RegVar("Self", get_Self, null);
    		L.RegFunction("IntToEnum", IntToEnum);
    		L.EndEnum();
    		TypeTraits.Check = CheckType;
    		StackTraits.Push = Push;
    	}
    
    	static void Push(IntPtr L, UnityEngine.Space arg)
    	{
    		ToLua.Push(L, arg);
    	}
    
    	static bool CheckType(IntPtr L, int pos)
    	{
    		return TypeChecker.CheckEnumType(typeof(UnityEngine.Space), L, pos);
    	}
    
    	[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
    	static int get_World(IntPtr L)
    	{
    		ToLua.Push(L, UnityEngine.Space.World);
    		return 1;
    	}
    
    	[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
    	static int get_Self(IntPtr L)
    	{
    		ToLua.Push(L, UnityEngine.Space.Self);
    		return 1;
    	}
    
    	[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
    	static int IntToEnum(IntPtr L)
    	{
    		int arg0 = (int)LuaDLL.lua_tonumber(L, 1);
    		UnityEngine.Space o = (UnityEngine.Space)arg0;
    		ToLua.Push(L, o);
    		return 1;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    枚举wrap类的Register函数固定以BeginEnum打头,以EndEnum结尾,类似地,作为枚举类导出可以比普通类要节省一些开销。枚举不能继承,也不能对其对象进行赋值,因此index元方法不需要递归查找,而newindex元方法是直接禁止触发的。枚举类型中所有满足BindingFlags.GetField | BindingFlags.Public | BindingFlags.Static的字段都会以RegVar的形式注册进来,而RegFunction则只有一个IntToEnum,而且所有的枚举wrap类都只有这一个。IntToEnum的实现很简单,就是把lua栈上的number类型转换为相应的枚举类型,再push到lua栈上。除此之外,wrap类还会生成TypeTraits中用于检查lua栈对象类型的CheckType函数,和将枚举对象压入lua栈的StackTraits的push函数。

    对于普通类,则需要通过反射得到该类可以被导出的普通方法,属性,以及构造函数的列表,然后才开始生成Register函数:

    static void GenRegisterFunction()
    {
        sb.AppendLineEx("\tpublic static void Register(LuaState L)");
        sb.AppendLineEx("\t{");
    
        if (isStaticClass)
        {
            sb.AppendFormat("\t\tL.BeginStaticLibs(\"{0}\");\r\n", libClassName);            
        }
        else if (!type.IsGenericType)
        {
            if (baseType == null)
            {
                sb.AppendFormat("\t\tL.BeginClass(typeof({0}), null);\r\n", className);
            }
            else
            {
                sb.AppendFormat("\t\tL.BeginClass(typeof({0}), typeof({1}));\r\n", className, GetBaseTypeStr(baseType));
            }
        }
        else
        {
            if (baseType == null)
            {
                sb.AppendFormat("\t\tL.BeginClass(typeof({0}), null, \"{1}\");\r\n", className, libClassName);
            }
            else
            {
                sb.AppendFormat("\t\tL.BeginClass(typeof({0}), typeof({1}), \"{2}\");\r\n", className, GetBaseTypeStr(baseType), libClassName);
            }
        }
    
        GenRegisterFuncItems();
        GenRegisterOpItems();
        GenRegisterVariables();
        GenRegisterEventTypes();            //注册事件类型
    
        if (!isStaticClass)
        {
            if (CustomSettings.outList.IndexOf(type) >= 0)
            {
                sb.AppendLineEx("\t\tL.RegVar(\"out\", get_out, null);");
            }
    
            sb.AppendFormat("\t\tL.EndClass();\r\n");
        }
        else
        {
            sb.AppendFormat("\t\tL.EndStaticLibs();\r\n");
        }
    
        sb.AppendLineEx("\t}");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    如果是静态类导出,函数会使用BeginStaticLibs进行注册,如果是普通类则会使用BeginClass;如果普通类有基类,则需要在BeginClass时把基类的类型也传进去。接下来调用的是GenRegisterFuncItems函数,这个函数就是扫描类的导出方法,过滤掉类的重载操作符方法,这部分需要特殊处理。如果类重载了下标操作符,则会根据get/set增加相应的导出方法,同时还会新增一个this函数,用来在lua层下标索引。如果类导出了构造函数,那么还会新增一个New函数用于lua层构造。我们以UnityEngine.Animation这个类为例,看一下GenRegisterFuncItems生成的部分:

    L.RegFunction("Stop", Stop);
    L.RegFunction("Rewind", Rewind);
    L.RegFunction("Sample", Sample);
    L.RegFunction("IsPlaying", IsPlaying);
    L.RegFunction("get_Item", get_Item);
    L.RegFunction("Play", Play);
    L.RegFunction("CrossFade", CrossFade);
    L.RegFunction("Blend", Blend);
    L.RegFunction("CrossFadeQueued", CrossFadeQueued);
    L.RegFunction("PlayQueued", PlayQueued);
    L.RegFunction("AddClip", AddClip);
    L.RegFunction("RemoveClip", RemoveClip);
    L.RegFunction("GetClipCount", GetClipCount);
    L.RegFunction("SyncLayer", SyncLayer);
    L.RegFunction("GetEnumerator", GetEnumerator);
    L.RegFunction("GetClip", GetClip);
    L.RegFunction("New", _CreateUnityEngine_Animation);
    L.RegVar("this", _this, null);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    我们看到这里导出的方法有get_Itemthis,但是没有set_Item,是因为UnityEngine.Animation类中重载了下标操作符,它只有get属性:

    public sealed class Animation : Behaviour, IEnumerable
    {
        public Animation();
        public AnimationState this[string name] { get; }
        ...
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    然后我们看一下GenRegisterOpItems函数,这个函数就是统计了C#重载的运算符和ToString方法,然后对应到lua层的元方法上去,还是以UnityEngine.Animation类为例,这里生成了两个导出函数:

    L.RegFunction("__eq", op_Equality);
    L.RegFunction("__tostring", ToLua.op_ToString);
    
    • 1
    • 2

    GenRegisterVariables函数就是对C#类中要导出的属性和字段进行扫描,如果是属性需要看一下是否可以get和set,只生成可以访问的导出方法:

    L.RegVar("clip", get_clip, set_clip);
    L.RegVar("playAutomatically", get_playAutomatically, set_playAutomatically);
    L.RegVar("wrapMode", get_wrapMode, set_wrapMode);
    L.RegVar("isPlaying", get_isPlaying, null);
    L.RegVar("animatePhysics", get_animatePhysics, set_animatePhysics);
    L.RegVar("cullingType", get_cullingType, set_cullingType);
    L.RegVar("localBounds", get_localBounds, set_localBounds);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    可以看到这里isPlaying没有导出set方法,因为Animation类的isPlaying只有get属性:

    public sealed class Animation : Behaviour, IEnumerable
    {
        public Bounds localBounds { get; set; }
        public bool playAutomatically { get; set; }
        public AnimationCullingType cullingType { get; set; }
        public AnimationClip clip { get; set; }
        public WrapMode wrapMode { get; set; }
        public bool animatePhysics { get; set; }
        [Obsolete("Use cullingType instead")]
        public bool animateOnlyIfVisible { get; set; }
        public bool isPlaying { get; }
        ...
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    最后是GenRegisterEventTypes这个函数,这个函数负责将C#类中定义的委托类型导出为函数,这样lua层就可以通过这个函数构造出对应的委托,例如UnityEngine.Application类中有如下这些委托类型:

    public class Application
    {
        ...
        public delegate void AdvertisingIdentifierCallback(string advertisingId, bool trackingEnabled, string errorMsg);
        public delegate void LowMemoryCallback();
        public delegate void MemoryUsageChangedCallback(in ApplicationMemoryUsageChange usage);
        public delegate void LogCallback(string condition, string stackTrace, LogType type);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    对应生成的导出函数如下:

    L.RegFunction("AdvertisingIdentifierCallback", UnityEngine_Application_AdvertisingIdentifierCallback);
    L.RegFunction("LogCallback", UnityEngine_Application_LogCallback);
    L.RegFunction("MemoryUsageChangedCallback", UnityEngine_Application_MemoryUsageChangedCallback);
    L.RegFunction("LowMemoryCallback", UnityEngine_Application_LowMemoryCallback);
    
    • 1
    • 2
    • 3
    • 4

    在所有的导出类都生成完毕之后,还有一步就是要生成LuaBinder类。这个类提供了唯一的Bind方法,用来初始化所有导出类的注册逻辑,大致分为三个部分:

    public static void Bind(LuaState L)
    {
        L.BeginModule("UnityEngine");
        UnityEngine_ComponentWrap.Register(L);
        L.EndModule();
    
        L.BeginModule("System");
        L.RegFunction("Action", System_Action);
        L.EndModule();
    
        L.BeginPreLoad();
        L.AddPreLoad("UnityEngine.MeshRenderer", LuaOpen_UnityEngine_MeshRenderer, typeof(UnityEngine.MeshRenderer));
        L.EndPreLoad();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    第一部分就是普通导出类的注册过程,第二部分是对所有用到的委托类型增加导出函数,这个与上文类似,就是方便lua层直接构造委托,最后一个部分是preload,实际上是一个延迟加载逻辑,这里面的导出类,不会在Bind的时候就注册,而是在lua层调用require的时候,才会注册进来。延迟导出的类配置在CustomSettings里的dynamicList中:

    public static List dynamicList = new List()
    {
        typeof(MeshRenderer),
    
        typeof(BoxCollider),
        typeof(MeshCollider),
        typeof(SphereCollider),
        typeof(CharacterController),
        typeof(CapsuleCollider),
    
        typeof(Animation),
        typeof(AnimationClip),
        typeof(AnimationState),
    
        typeof(SkinWeights),
        typeof(RenderTexture),
        typeof(Rigidbody),
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    自此,整个tolua的框架基本上算是梳理完了。

    如果你觉得我的文章有帮助,欢迎关注我的微信公众号 我是真的想做游戏啊

  • 相关阅读:
    开发小经验积累
    【Liunx】部署WEB服务:Apache
    SpringMVC源码解析-doDispatch方法
    从服务器端获取人脸数据,在本地检测特征,并将特征发送给服务器
    Verilog HDL复习总结
    HTML <th> 标签
    JavaScript函数this指向
    IP6809三线圈15W无线充电发射端方案ic英集芯
    AOP实现
    24【备忘录设计模式】
  • 原文地址:https://blog.csdn.net/weixin_45776473/article/details/132949883