什么是wrap文件

每个wrap文件都是对一个c#类的包装,在lua中,通过对wrap类中的函数调用,间接的对c#实例进行操作。

wrap类文件生成和使用的总体流程

生成一个wrap文件的流程



这部分主要通过分析类的反射信息完成。

wrap文件内容解析

使用UnityEngine_GameObjectWrap.cs进行举例。

注册部分

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部分,负责类结束注册的收尾工作

BeginClass部分

①用于创建类和类的元表,如果类的元表的元表(类的元表是承载每个类方法和属性的实体,类的元表的元表就是类的父类)

②将类添加到loaded表中。

③设置每个类的元表的通用的元方法和属性,__gc,name,ref,__cal,__index,__newindex。

RegFunction部分

每一个RefFunction做的事都很简单,将每个函数转化为一个指针,然后添加到类的元表中去,与将一个c函数注册到lua中是一样的。

RegVar部分

每一个变量或属性或被包装成get_xxx,set_xxx函数注册添加到类的元表的gettag,settag表中去,用于调用和获取。

EndClass部分

做了两件事:

①设置类的元表

②把该类加到所在模块代表的表中(如将GameObject加入到UnityEngine表中)

每个函数的实体部分

由于构造函数,this[],get_xxx,set_xxx的原理都差不多,都是通过反射的信息生成的,所以放在一起用一个实例讲一下(使用GameObject的GetComponent函数进行说明)。

[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函数。

一个类通过wrap文件注册进lua虚拟机后是什么样子的

使用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等)压栈,结束调用。

在lua中调用一个c#实例中的函数或变量的过程

local tempGameObject = UnityEngine.GameObject("temp")
local instanceID = tempGameObject.GetInstanceID()



在了解了GameObject元表后,这些只是一些基础的元表操作,就不多做解释。

lua中c#实例的真正存储位置

前面说了每一个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中进行使用的原理。

tolua之wrap文件的原理与使用的更多相关文章

  1. 【Unity游戏开发】tolua之wrap文件的原理与使用

    本文内容转载自:https://www.cnblogs.com/blueberryzzz/p/9672342.html .非常感谢原作者慷慨地授权转载,比心!@blueberryzzz 是位大神,欢迎 ...

  2. 【转载】tolua之wrap文件的原理与使用

    什么是wrap文件 每个wrap文件都是对一个c#类的包装,在lua中,通过对wrap类中的函数调用,间接的对c#实例进行操作. wrap类文件生成和使用的总体流程 生成一个wrap文件的流程 这部分 ...

  3. Java提高篇——JVM加载class文件的原理机制

    在面试java工程师的时候,这道题经常被问到,故需特别注意. 1.JVM 简介 JVM 是我们Javaer 的最基本功底了,刚开始学Java 的时候,一般都是从“Hello World ”开始的,然后 ...

  4. [分享]Host文件的原理解释及应用说明

    Host文件的原理解释及应用说明   Host文件位置及打开方式:   Window系统中有个Hosts文件(没有后缀名), Windows 98系统下该文件在Windows目录,在Windows 2 ...

  5. 关于hosts文件的原理与制作

    由于需要整理的关于hosts的文件 关于hosts文件的原理与制作1.什么是hosts文件hosts文件是一个没有扩展名的系统文件,hosts文件用于存储计算机网络中节点信息的文件,它是可以将主机名映 ...

  6. 软硬链接、文件删除原理、linux中的三种时间、chkconfig优化

    第1章 软硬链接 1.1 硬链接 1.1.1 含义 多个文件拥有相同的inode号码 硬链接即文件的多个入口 1.1.2 作用 防止你误删除文件 1.1.3 如何创建硬链接 ln 命令,前面是源文件  ...

  7. Linux 文件删除原理_009

    ***了解Linux文件删除原理先了解一下文件inode索引节点,每个文件在Linux系统里都有唯一的索引节点(身份证号) inode.如果文件存在硬链接,那这个文件和这个文件的硬链接的inode是相 ...

  8. JVM加载class文件的原理机制(转)

    JVM加载class文件的原理机制 1.Java中的所有类,必须被装载到jvm中才能运行,这个装载工作是由jvm中的类装载器完成的,类装载器所做的工作实质是把类文件从硬盘读取到内存中 2.java中的 ...

  9. Web上传文件的原理及实现

    现在有很多Web程序都有上传功能,实现上传功能的组件或框架也很多,如基于java的Commons FileUpload.还有Struts1.x和Struts2中带的上传文件功能(实际上,Struts2 ...

随机推荐

  1. Cs231n-assignment 2作业笔记

    assignment 2 assignment2讲解参见: https://blog.csdn.net/BigDataDigest/article/details/79286510 http://ww ...

  2. A Deep Learning-Based System for Vulnerability Detection(一)

    接着上一篇,讨论讨论具体步骤实现方法.步骤1-3分别在下面进行阐述,步骤4,6都是标准的,步骤5类似于步骤1-3. 结合这个图进行讨论详细步骤: 步骤1:提取库/API函数调用和程序片段 1.1将库/ ...

  3. Python 位操作运算符

    & 按位与运算符:参与运算的两个值,如果两个相应位都为1,则该位的结果为1,否则为0 (a & b) 输出结果 12 ,二进制解释: 0000 1100 | 按位或运算符:只要对应的二 ...

  4. jvisualvm 连接 jstatd 远程监控 jvm 或 Visual GC提示"不受此JVM支持“

    Visual GC提示"不受此JVM支持",可以使用此方法解决. 一.添加配置文件 jstatd.all.policy [root@localhost /]# cd /usr/lo ...

  5. kaptcha验证码的使用

    使用kaptcha可以方便的配置: 验证码的字体 验证码字体的大小 验证码字体的字体颜色 验证码内容的范围(数字,字母,中文汉字!) 验证码图片的大小,边框,边框粗细,边框颜色 验证码的干扰线(可以自 ...

  6. Docker(3):Dockerfile配置详解

    FROM  : 指定base镜像 MAINTAINER :设置镜像的作者,可以是任意的字符串 COPY :将文件从build context 复制到镜像   COPY 支持两种形式:COPY src ...

  7. python之shutil

    ''' shutil 用来处理 文件 文件夹 压缩包 的模块 ''' import shutil # 拷贝文件内容 shutil.copyfileobj(open('old.xml', 'r'), o ...

  8. FileMode文件模式(转载)

    FileMode指定操作系统打开文件的方式. Append 6 若存在文件,则打开该文件并查找到文件尾,或者创建一个新文件. 这需要 Append 权限. FileMode.Append 只能与 Fi ...

  9. VisualStudio2017下ASP.NET CORE的TagHelper智能提示不能使用的解决办法

    之前在VS2017RC中就发现该问题,安装了依赖,但是前段一直点不出来asp-for,后来查了发行说明, 才知道在VS2017rc中暂时无法解决,所以一直等到VS2017正式版的发布,急冲冲的装好, ...

  10. flask token认证

    在前后端分离的项目中,我们现在多半会使用token认证机制实现登录权限验证. token通常会给一个过期时间,这样即使token泄露了,危害期也只是在有效时间内,超过这个有效时间,token过期了,就 ...