每一个wrap文件都是对一个c#类的包装,在lua中,经过对wrap类中的函数调用,间接的对c#实例进行操做。c#
这部分主要经过分析类的反射信息完成。函数
使用UnityEngine_GameObjectWrap.cs进行举例。this
public static void Register(LuaState L) { L.BeginClass(typeof(UnityEngine.GameObject), typeof(UnityEngine.Object)); L.RegFunction("CreatePrimitive", CreatePrimitive); L.RegFunction("GetComponent", GetComponent); L.RegFunction("GetComponentInChildren", GetComponentInChildren); L.RegFunction("GetComponentInParent", GetComponentInParent); L.RegFunction("GetComponents", GetComponents); L.RegFunction("GetComponentsInChildren", GetComponentsInChildren); L.RegFunction("GetComponentsInParent", GetComponentsInParent); L.RegFunction("SetActive", SetActive); L.RegFunction("CompareTag", CompareTag); L.RegFunction("FindGameObjectWithTag", FindGameObjectWithTag); L.RegFunction("FindWithTag", FindWithTag); L.RegFunction("FindGameObjectsWithTag", FindGameObjectsWithTag); L.RegFunction("Find", Find); L.RegFunction("AddComponent", AddComponent); L.RegFunction("BroadcastMessage", BroadcastMessage); L.RegFunction("SendMessageUpwards", SendMessageUpwards); L.RegFunction("SendMessage", SendMessage); L.RegFunction("New", _CreateUnityEngine_GameObject); L.RegFunction("__eq", op_Equality); L.RegFunction("__tostring", ToLua.op_ToString); L.RegVar("transform", get_transform, null); L.RegVar("layer", get_layer, set_layer); L.RegVar("activeSelf", get_activeSelf, null); L.RegVar("activeInHierarchy", get_activeInHierarchy, null); L.RegVar("isStatic", get_isStatic, set_isStatic); L.RegVar("tag", get_tag, set_tag); L.RegVar("scene", get_scene, null); L.RegVar("gameObject", get_gameObject, null); L.EndClass(); }
这部分代码由GenRegisterFunction()生成,能够看到,这些代码分为了4部分:
1.BeginClass部分,负责类在lua中的初始化部分
2.RegFunction部分,负责将函数注册到lua中
3.RegVar部分,负责将变量和属性注册到lua中
4.EndClass部分,负责类结束注册的收尾工做lua
①用于建立类和类的元表,若是类的元表的元表(类的元表是承载每一个类方法和属性的实体,类的元表的元表就是类的父类)
②将类添加到loaded表中。
③设置每一个类的元表的通用的元方法和属性,__gc,name,ref,__cal,__index,__newindex。pwa
每个RefFunction作的事都很简单,将每一个函数转化为一个指针,而后添加到类的元表中去,与将一个c函数注册到lua中是同样的。指针
每个变量或属性或被包装成get_xxx,set_xxx函数注册添加到类的元表的gettag,settag表中去,用于调用和获取。code
作了两件事:
①设置类的元表
②把该类加到所在模块表明的表中(如将GameObject加入到UnityEngine表中)orm
因为构造函数,this[],get_xxx,set_xxx的原理都差很少,都是经过反射的信息生成的,因此放在一块儿用一个实例讲一下(使用GameObject的GetComponent函数进行说明)。blog
[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))] static int GetComponent(IntPtr L) { try { //获取栈中参数的个数 int count = LuaDLL.lua_gettop(L); //根据栈中元素的个数和元素的类型判断该使用那一个重载 if (count == 2 && TypeChecker.CheckTypes<string>(L, 2)) { //将栈底的元素取出来,这个obj在栈中是一个fulluserdata,须要先将这个fulluserdata转化成对应的c#实例,也就是调用这个GetComponent函数的GameObject实例 UnityEngine.GameObject obj = (UnityEngine.GameObject)ToLua.CheckObject(L, 1, typeof(UnityEngine.GameObject)); //将栈底的上一个元素取出来,也就是GetComponent(string type)的参数 string arg0 = ToLua.ToString(L, 2); //经过obj,arg0直接第调用GetCompent(string type)函数 UnityEngine.Component o = obj.GetComponent(arg0); //将调用结果压栈 ToLua.Push(L, o); //返回参数的个数 return 1; } //另外一个GetComponent的重载,跟上一个差很少,就不详细说明了 else if (count == 2 && TypeChecker.CheckTypes<System.Type>(L, 2)) { UnityEngine.GameObject obj = (UnityEngine.GameObject)ToLua.CheckObject(L, 1, typeof(UnityEngine.GameObject)); System.Type arg0 = (System.Type)ToLua.ToObject(L, 2); UnityEngine.Component o = obj.GetComponent(arg0); ToLua.Push(L, o); return 1; } //参数数量或类型不对,没有找到对应的重载,抛出错误 else { return LuaDLL.luaL_throw(L, "invalid arguments to method: UnityEngine.GameObject.GetComponent"); } } catch (Exception e) { return LuaDLL.toluaL_exception(L, e); } }
能够看到,GetComponent函数的内容,其实就是经过反射分析GetComponent的重载个数,每一个重载的参数个数,类型生成的。具体内容和lua调用c函数差很少。索引
假如说在lua中有这么一个调用:
local tempGameObject = UnityEngine.GameObject("temp") local transform = tempGameObject.GetComponent("Transform")
第二行代码对应的实际调用过程是:
1.先去tempGameObject的元表GameObject元表中尝试去取GetComponent函数,取到了。
2.调用取到的GetComponent函数,调用时会将tempGameObject,"Transform"做为参数先压栈,而后调用GetComponent函数。
3.接下来就进入GetComponent函数内部进行操做,由于生成了新的ci,因此此时栈中只有tempGameOjbect,"Transfrom"两个元素。
4.根据参数的数量和类型判断须要使用的重载。
5.经过tempGameObject表明的c#实例的索引,在objects表中找到对应的实例。同时取出"Transform"这个参数,准备进行真正的函数调用。
6.执行obj.GetComponent(arg0),将结果包装成一个fulluserdata后压栈,结束调用。
7.lua中的transfrom变量赋值为这个压栈的fulluserdata。
8.结束。
其中3-7的操做都在c#中进行,也就是wrap文件中的GetComponent函数。
使用GameObjectWrap进行举例
能够看到GameObject的全部功能都是经过一个元表实现的,经过这个元表能够调用GameObjectWrap文件中的各个函数来实现对GameObject实例的操做,这个元表对使用者来讲是不可见的,由于咱们平时只会在代码中调用GameObject类,GameObject实例,并不会直接引用到这个元表,接下来来分析一下GameObject类,GameObject实例与这个元表的关系:
①GameObject类:其实只是一个放在_G表中供人调用的一个充当索引的表,咱们经过它来触发GameObject元表的各类元方法,实现对c#类的使用。
②GameObject的实例:是一个fulluserdata,内容为一个整数,这个整数表明了这个实例在objects表中的索引(objects是一个用list实现的回收链表,lua中调用的c#类实例都存在这个里面,后面会讲这个objects表),每次在lua中调用一个c#实例的方法时,都会经过这个索引找到这个索引在c#中对应的实例,而后进行操做,最后将操做结果转化为一个fulluserdata(或lua的内建类型,如bool等)压栈,结束调用。
local tempGameObject = UnityEngine.GameObject("temp") local instanceID = tempGameObject.GetInstanceID()
在了解了GameObject元表后,这些只是一些基础的元表操做,就很少作解释。
前面说了每个c#实例在lua中是一个内容为整数索引的fulluserdata,在进行函数调用时,经过这个整数索引查找和调用这个索引表明的实例的函数和变量。
生成或使用一个表明c#实例的lua变量的过程大概是这样的。
还用这个例子来讲明:
local tempGameObject = UnityEngine.GameObject("temp") local transform = tempGameObject.GetComponent("Transform")
因此说lua中调用和建立的c#实例实际都是存在c#中的objects表中,lua中的变量只是一个持有该c#实例索引位置的fulluserdata,并无直接对c#实例进行引用。
对c#实例进行函数的调用和变量的修改都是经过元表调用操做wrap文件中的函数进行的。
以上就是c#类如何经过wrap类在lua中进行使用的原理。