提交 06a6c7ee 编写于 作者: J jayatubi

doc格式文档转markdown(初版),仅格式转换

上级 bc809f58
## C# API
### LuaEnv类
#### object[] DoString(string chunk, string chunkName = "chuck", LuaTable env = null)
描述:
执行一个代码块。
参数:
chunk: Lua代码;
chunkName: 发生error时的debug显示信息中使用,指明某某代码块的某行错误;
env :为这个代码块;
返回值:
代码块里return语句的返回值;
比如:return 1, “hello”,DoString返回将包含两个object, 一个是double类型的1, 一个是string类型的“hello”
例如:
LuaEnv luaenv = new LuaEnv();
object[] ret = luaenv.DoString("print(‘hello’)\r\nreturn 1")
UnityEngine.Debug.Log("ret="+ret[0]);
luaenv.Dispose()
#### T LoadString<T>(string chunk, string chunkName = "chunk", LuaTable env = null)
描述:
加载一个代码块,但不执行,只返回类型可以指定为一个delegate或者一个LuaFunction
参数:
chunk: Lua代码;
chunkName: 发生error时的debug显示信息中使用,指明某某代码块的某行错误;
env :为这个代码块;
返回值:
代表该代码块的delegate或者LuaFunction类;
#### LuaTable Global;
描述:
代表lua全局环境的LuaTable
### void Tick()
描述:
清除Lua的未手动释放的LuaBase(比如,LuaTable, LuaFunction),以及其它一些事情。
需要定期调用,比如在MonoBehaviour的Update中调用。
### void AddLoader(CustomLoader loader)
描述:
增加一个自定义loader
参数:
loader:就一个回调,其类型为delegate byte[] CustomLoader(ref string filepath),当一个文件被require时,这个loader会被回调,其参数是require的参数,如果该loader找到文件,可以将其读进内存,返回一个byte数组。如果需要支持调试的话,而filepath要设置成IDE能找到的路径(相对或者绝对都可以)
#### void Dispose()
描述:
Dispose该LuaEnv。
> LuaEnv的使用建议:全局就一个实例,并在Update中调用GC方法,完全不需要时调用Dispose
### LuaTable类
#### T Get<T>(string key)
描述:
获取在key下,类型为T的value,如果不存在或者类型不匹配,返回null;
#### T GetInPath<T>(string path)
描述:
和Get的区别是,这个会识别path里头的“.”,比如var i = tbl.GetInPath<int>(“a.b.c”)相当于在lua里头执行i = tbl.a.b.c
#### void SetInPath<T>(string path, T val)
描述:
和GetInPaht<T>对应的setter;
#### void Get<TKey, TValue>(TKey key, out TValue value)
描述:
上面的API的Key都只能是string,而这个API无此限制;
#### void Set<TKey, TValue>(TKey key, TValue value)
描述:
对应Get<TKey, TValue>的setter;
#### T Cast<T>()
描述:
把该table转成一个T指明的类型,可以是一个加了CSharpCallLua声明的interface,一个有默认构造函数的class或者struct,一个Dictionary,List等等。
#### void SetMetaTable(LuaTable metaTable)
描述:
设置metaTable为table的metatable
### LuaFunction类
> 注意:用该类访问Lua函数会有boxing,unboxing的开销,为了性能考虑,需要频繁调用的地方不要用该类。建议通过table.Get<ABCDelegate>获取一个delegate再调用(假设ABCDelegate是C#的一个delegate)。在使用使用table.Get<ABCDelegate>之前,请先把ABCDelegate加到代码生成列表。
#### object[] Call(params object[] args)
描述:
以可变参数调用Lua函数,并返回该调用的返回值。
#### object[] Call(object[] args, Type[] returnTypes)
描述:
调用Lua函数,并指明返回参数的类型,系统会自动按指定类型进行转换。
#### void SetEnv(LuaTable env)
描述:
相当于lua的setfenv函数。
## Lua API
### CS对象
#### CS.namespace.class(...)
描述:
新建一个C#对象实例
例如:
local v1=CS.UnityEngine.Vector3(1,1,1)
#### CS.namespace.class.field
描述:
访问一个C#静态成员
例如:
Print(CS.UnityEngine.Vector3.one)
#### CS.namespace.enum.field
描述:
访问一个枚举值
#### typeof函数
描述:
类似C#里头的typeof关键字,返回一个Type对象,比如GameObject.AddComponent其中一个重载需要一个Type参数
例如:
newGameObj:AddComponent(typeof(CS.UnityEngine.ParticleSystem))
#### 无符号64位支持
##### uint64.tostring
描述:
无符号数转字符串。
##### uint64.divide
描述:
无符号数除法。
##### uint64.compare
描述:
无符号比较,相对返回0,大于返回正数,小于返回负数。
##### uint64.remainder
描述:
无符号数取模。
##### uint64.parse
描述:
字符串转无符号数。
#### xlua.structclone
描述:
克隆一个c#结构体
#### cast函数
描述:
指明以特定的接口访问对象,这在实现类无法访问的时候(比如internal修饰)很有用,这时可以这么来(假设下面的calc对象实现了C#的PerformentTest.ICalc接口)
例如:
cast(calc, typeof(CS.PerformentTest.ICalc))
然后就木有其它API了
访问csharp对象和访问一个table一样,调用函数跟调用lua函数一样,也可以通过操作符访问c#的操作符,下面是一个例如:
local v1=CS.UnityEngine.Vector3(1,1,1)
local v2=CS.UnityEngine.Vector3(1,1,1)
v1.x = 100
v2.y = 100
print(v1, v2)
local v3 = v1 + v2
print(v1.x, v2.x)
print(CS.UnityEngine.Vector3.one)
print(CS.UnityEngine.Vector3.Distance(v1, v2))
## 类型映射
### 基本数据类型
|C#类型|Lua类型|
|-|-|
|sbyte,byte,short,ushort,int,uint,double,char,float|number|
|decimal|userdata|
|long,ulong|userdata/lua_Integer(lua53)|
|bytes[]|string|
|bool|boolean|
|string|string|
### 复杂数据类型
|C#类型|Lua类型|
|-|-|
|LuaTable|table|
|LuaFunction|function|
|class或者 struct的实例|userdata,table|
|method,delegate|function|
#### LuaTable:
C#侧指明从Lua侧输入(包括C#方法的输入参数或者Lua方法的返回值)LuaTable类型,则要求Lua侧为table。或者Lua侧的table,在C#侧未指明类型的情况下转换成LuaTable。
#### LuaFunction:
C#侧指明从Lua侧输入(包括C#方法的输入参数或者Lua方法的返回值)LuaFunction类型,则要求Lua侧为function。或者Lua侧的function,在C#侧未指明类型的情况下转换成LuaFunction。
#### LuaUserData:
对应非C# Managered对象的lua userdata。
#### class或者 struct的实例:
从C#传一个class或者struct的实例,将映射到Lua的userdata,并通过__index访问该userdata的成员
C#侧指明从Lua侧输入指定类型对象,Lua侧为该类型实例的userdata可以直接使用;如果该指明类型有默认构造函数,Lua侧是table则会自动转换,转换规则是:调用构造函数构造实例,并用table对应字段转换到c#对应值后赋值各成员。
#### method, delegate:
成员方法以及delegate都是对应lua侧的函数。
C#侧的普通参数以及引用参数,对应lua侧函数参数;C#侧的返回值对应于Lua的第一个返回值;引用参数和out参数则按序对应于Lua的第2到第N个参数。
\ No newline at end of file
## What&Why
XLua目前内置的扩展库:
* 针对luajit的64位整数支持;
* 函数调用耗时以及内存泄漏定位工具;
* 用于支持ZeroBraneStudio的luasocket库;
* tdr 4 lua;
随着使用项目的增加以及项目使用的深入程度,仅有这几个扩展已经没法满足项目组了,而由于各个项目对扩展差异化比较大,以及手机平台对安装包大小的敏感,XLua是无法通过预集成去满足这些需求,这也是这篇教程的由来。
这篇教程,将以lua-rapidjson为例,一步步的讲述怎么往xLua添加c/c++扩展,当然,会添加了,自然删除也就会了,项目组可以自行删除不需要用到的预集成扩展。
## How
分三步
1. 修改build文件、工程设置,把要集成的扩展编译到XLua Plugin里头;
2. 调用xLua的C# API,使得扩展可以被按需(在lua代码里头require的时候)加载;
3. 可选,如果你的扩展里头需要用到64位整数,你可以通过XLua的64位扩展库来实现和C#的配合。
### 一、添加扩展&编译
准备工作
1. 把xLua的C源码包解压到你Unity工程的Assets同级目录下。
下载lua-rapidjson代码,按你的习惯放置。本教程是把rapidjson头文件放到$UnityProj\build\lua-rapidjson\include目录下,而扩展的源码rapidjson.cpp放到$UnityProj\build\lua-rapidjson\source目录下(注:$UnityProj指的是你工程的目录)
2. 在CMakeLists.txt加入扩展
xLua的各平台Plugins编译使用cmake编译,好处是所有平台的编译都写在一个makefile,大部分编译处理逻辑是跨平台的。
xLua配套的CMakeLists.txt为第三方扩展提供了扩展点(都是list):
1. THIRDPART_INC:第三方扩展的头文件搜索路径。
2. THIRDPART_SRC:第三方扩展的源代码。
3. THIRDPART_LIB:第三方扩展依赖的库。
如下是rapidjson的加法
#begin lua-rapidjson
set (RAPIDJSON_SRC lua-rapidjson/source/rapidjson.cpp)
set_property(
SOURCE ${RAPIDJSON_SRC}
APPEND
PROPERTY COMPILE_DEFINITIONS
LUA_LIB
)
list(APPEND THIRDPART_INC lua-rapidjson/include)
set (THIRDPART_SRC ${THIRDPART_SRC} ${RAPIDJSON_SRC})
#end lua-rapidjson
完整代码请见附件。
3. 各平台编译
所有编译脚本都是按这个方式命名:make_平台_lua版本.后缀。
比如windows 64位lua53版本是make_win64_lua53.bat,android的luajit版本是make_android_luajit.sh,要编译哪个版本就执行相应的脚本即可。
执行完编译脚本会自动拷贝到plugin_lua53或者plugin_luajit目录,前者是lua53版本放置路径,后者是luajit。
配套的android脚本是在linux下使用的,脚本开头的NDK路径要根据实际情况修改。
### 二、C#侧集成
所有lua的C扩展库都会提供个luaopen_xxx的函数,xxx是动态库的名字,比如lua-rapidjson库该函数是luaopen_rapidjson,这类函数由lua虚拟机在加载动态库时自动调用,而在手机平台,由于ios的限制我们加载不了动态库,而是直接编译进进程里头。
为此,XLua提供了一个API来替代这功能(LuaEnv的成员方法):
public void AddBuildin(string name, LuaCSFunction initer)
参数:
name:buildin模块的名字,require时输入的参数;
initer:初始化函数,原型是这样的public delegate int lua_CSFunction(IntPtr L),必须是静态函数,而且带MonoPInvokeCallbackAttribute属性修饰,这个api会检查这两个条件。
我们以luaopen_rapidjson的调用来看看怎么使用。
扩展LuaDLL.Lua类,用pinvoke把luaopen_rapidjson导出到C#,然后写一个符合lua_CSFunction定义的静态函数,你可以在里头做写初始化工作,比如luaopen_rapidjson的调用,以下是完整代码:
namespace LuaDLL
{
public partial class Lua
{
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern int luaopen_rapidjson(System.IntPtr L);
[MonoPInvokeCallback(typeof(LuaDLL.lua_CSFunction))]
public static int LoadRapidJson(System.IntPtr L)
{
return luaopen_rapidjson(L);
}
}
}
然后调用AddBuildin:
luaenv.AddBuildin("rapidjson", LuaDLL.Lua.LoadRapidJson);
然后就ok了,在lua代码中试试该扩展:
local rapidjson = require('rapidjson')
local t = rapidjson.decode('{"a":123}')
print(t.a)
t.a = 456
local s = rapidjson.encode(t)
print('json', s)
### 三、64位改造
把i64lib.h文件include到需要64位改造的文件里头。
该头文件的API就以下几个:
//往栈上放一个int64/uint64
void lua_pushint64(lua_State* L, int64_t n);
void lua_pushuint64(lua_State* L, uint64_t n);
//判断栈上pos位置是否是int64/uint64
int lua_isint64(lua_State* L, int pos);
int lua_isuint64(lua_State* L, int pos);
//从栈上pos位置取一个int64/uint64
int64_t lua_toint64(lua_State* L, int pos);
uint64_t lua_touint64(lua_State* L, int pos);
这些API的使用依情况而定,可以看看本文附带的附件(rapidjson.cpp文件)
编译工程相关修改
\ No newline at end of file
## 复杂值类型的gc问题
xLua复杂值类型(struct)的默认传递方式是引用传递,这种方式要求先对值类型boxing,传递给lua,lua使用后释放该引用。由于值类型每次boxing将产生一个新对象,当lua侧使用完毕释放该对象的引用时,则产生一次gc。
为此,xLua实现了一套struct的gc优化方案,您只要通过简单的配置,则可以实现满足条件的struct传递到lua侧无gc。
## struct需要满足什么条件?
1. struct允许嵌套其它struct,但它以及它嵌套的struct只能包含这几种基本类型:byte、sbyte、short、ushort、int、uint、long、ulong、float、double;例如UnityEngine定义的大多数值类型:Vector系列,Quaternion,Color。。。均满足条件,或者用户自定义的一些struct
2. 该struct配置了GCOptimize属性(对于常用的UnityEngine的几个struct,Vector系列,Quaternion,Color。。。均已经配置了该属性),这个属性可以通过配置文件或者C# Attribute实现;
3. 使用到该struct的地方,需要添加到生成代码列表;
## 如何配置?
`XLua的配置.md`的GCOptimize章节。
\ No newline at end of file
XLua内置两个小工具进行性能方面问题的分析:一个是Lua函数,Lua调用C#函数的时长(不一定等同于CPU耗时,比如协程yield出去那段时间也会被算入调用时间)分析工具;一个是内存泄漏定位工具。
## 函数调用时长分析工具
### 典型使用案例:
说明:
api很简单,就三个,start和stop都是无参数,也很好理解,分别是统计开始以及结束。在start以及stop之间可以多次调用report(也可以考虑不调stop)。每次report会得到从start到调用report为止的函数时长统计报告(以字符串返回)。report函数只有一个可选参数,可以指明按照总时间(参数是字符串的”TOTAL”,这个是默认值),平均每次调用时间(“AVERAGE”),以及调用次数(“CALLED”)来排序。
典型的一个时长统计报告如下:
第一列是函数名
第二列是源代码,如果是lua文件将会统计到文件,行号,如果是C#的导出代码,将会标注[C#],如果是C函数,标准为[C]。
后面几列分别是总时间,平均每次调用时间,占总统计时间的百分比,以及调用次数。
## 内存泄漏定位工具
### 典型使用案例:
说明:
api就两个,total获取lua虚拟机的内存占用,单位是Kbytes,以lua number返回。而snapshot返回当前内存快照信息,一个典型的快照报告如下:
第一列是table变量名;
第二列是table的大小,如果该table下有子table也会被统计到;
第三列是变量的类型,UPVALUE代表是闭包里头的变量(内层函数对外层local变量的引用),GLOBAL是全局变量,REGISTRY是C侧(包含虚拟机内部的)的一些私有数据。一般主要关注前面两种;
第四列是变量的ID,其实就是内存指针,如果两次报告间没被重新分配,该ID是不变的,便于识别是否同一table;
最后一列是一些附加信息,比如闭包变量可能存在很多同名的,这里会通过有那些函数引用了该变量来协助定位。
如何定位内存泄漏:通过memory.total检测出内存的持续增长,然后通过memory.snapshot定位出哪里泄漏(lua测的内存泄漏都是表现为往某table添加了数据忘了删除)。
\ No newline at end of file
## xLua教程
### Lua文件加载
1. 执行字符串
最基本是直接用LuaEnv.DoString执行一个字符串,当然,字符串得符合Lua语法
比如:
luaenv.DoString("print('hello world')")
完整代码见XLua\Tutorial\LoadLuaScript\ByString目录
> 但这种方式并不建议,更建议下面介绍这种方法。
2. 加载Lua文件
用lua的require函数即可
比如:
DoString("require 'byfile'")
完整代码见XLua\Tutorial\LoadLuaScript\ByFile目录
require实际上是调一个个的loader去加载,有一个成功就不再往下尝试,全失败则报文件找不到。
目前xLua除了原生的loader外,还添加了从Resource加载的loader,需要注意的是因为Resource只支持有限的后缀,放Resources下的lua文件得加上txt后缀(见附带的例子)。
建议的加载Lua脚本方式是:整个程序就一个DoString("require 'main'"),然后在main.lua加载其它脚本(类似lua脚本的命令行执行:lua main.lua)。
有童鞋会问:要是我的Lua文件是下载回来的,或者某个自定义的文件格式里头解压出来,或者需要解密等等,怎么办?问得好,xLua的自定义Loader可以满足这些需求。
3. 自定义Loader
在xLua加自定义loader是很简单的,只涉及到一个接口:
public delegate byte[] CustomLoader(ref string filepath);
public void LuaEnv.AddLoader(CustomLoader loader)
通过AddLoader可以注册个回调,该回调参数是字符串,lua代码里头调用require时,参数将会透传给回调,回调中就可以根据这个参数去加载指定文件,如果需要支持调试,需要把filepath修改为真实路径传出。该回调返回值是一个byte数组,如果为空表示该loader找不到,否则则为lua文件的内容。
有了这个就简单了,用IIPS的IFS?没问题。写个loader调用IIPS的接口读文件内容即可。文件已经加密?没问题,自己写loader读取文件解密后返回即可。。。
完整示例见XLua\Tutorial\LoadLuaScript\Loader
### C#访问Lua
这里指的是C#主动发起对Lua数据结构的访问。
本章涉及到的例子都可以在XLua\Tutorial\CSharpCallLua下找到。
1. 获取一个全局基本数据类型
访问LuaEnv.Global就可以了,上面有个模版Get方法,可指定返回的类型。
luaenv.Global.Get<int>("a")
luaenv.Global.Get<string>("b")
luaenv.Global.Get<bool>("c")
2. 访问一个全局的table
也是用上面的Get方法,那类型要指定成啥呢?
1. 映射到普通class或struct
定义一个class,有对应于table的字段的public属性,而且有无参数构造函数即可,比如对于{f1 = 100, f2 = 100}可以定义一个包含public int f1;public int f2;的class。
这种方式下xLua会帮你new一个实例,并把对应的字段赋值过去。
table的属性可以多于或者少于class的属性。可以嵌套其它复杂类型。
要注意的是,这个过程是值拷贝,如果class比较复杂代价会比较大。而且修改class的字段值不会同步到table,反过来也不会。
这个功能可以通过把类型加到GCOptimize生成降低开销,详细可参见配置介绍文档。
那有没有引用方式的映射呢?有,下面这个就是:
2. 映射到一个interface
这种方式依赖于生成代码(如果没生成代码会抛InvalidCastException异常),代码生成器会生成这个interface的实例,如果get一个属性,生成代码会get对应的table字段,如果set属性也会设置对应的字段。甚至可以通过interface的方法访问lua的函数。
3. 更轻量级的by value方式:映射到Dictionary<>,List<>
不想定义class或者interface的话,可以考虑用这个,前提table下key和value的类型都是一致的。
4. 另外一种by ref方式:映射到LuaTable类
这种方式好处是不需要生成代码,但也有一些问题,比如慢,比方式2要慢一个数量级,比如没有类型检查。
3. 访问一个全局的function
仍然是用Get方法,不同的是类型映射。
1. 映射到delegate
这种是建议的方式,性能好很多,而且类型安全。缺点是要生成代码(如果没生成代码会抛InvalidCastException异常)。
delegate要怎样声明呢?
对于function的每个参数就声明一个输入类型的参数。
多返回值要怎么处理?从左往右映射到c#的输出参数,输出参数包括返回值,out参数,ref参数。
参数、返回值类型支持哪些呢?都支持,各种复杂类型,out,ref修饰的,甚至可以返回另外一个delegate。
delegate的使用就更简单了,直接像个函数那样用就可以了。
2. 映射到LuaFunction
这种方式的优缺点刚好和第一种相反。
使用也简单,LuaFunction上有个变参的Call函数,可以传任意类型,任意个数的参数,返回值是object的数组,对应于lua的多返回值。
4. 使用建议
1. 访问lua全局数据,特别是table以及function,代价比较大,建议尽量少做,比如在初始化时把要调用的lua function获取一次(映射到delegate)后,保存下来,后续直接调用该delegate即可。table也类似。
2. 如果lua测的实现的部分都以delegate和interface的方式提供,使用方可以完全和xLua解耦:由一个专门的模块负责xlua的初始化以及delegate、interface的映射,然后把这些delegate和interface设置到要用到它们的地方。
### Lua调用C#
> 本章节涉及到的实例均在XLua\Tutorial\LuaCallCSharp下
#### new C#对象
你在C#这样new一个对象:
var newGameObj = new UnityEngine.GameObject();
对应到Lua是这样:
local newGameObj = CS.UnityEngine.GameObject()
基本类似,除了:
1. lua里头没有new关键字;
2. 所有C#相关的都放到CS下,包括构造函数,静态成员属性、方法;
如果有多个构造函数呢?放心,xlua支持重载,比如你要调用GameObject的带一个string参数的构造函数,这么写:
local newGameObj2 = CS.UnityEngine.GameObject('helloworld')
#### 访问C#静态属性,方法
##### 读静态属性
CS.UnityEngine.Time.deltaTime
##### 写静态属性
CS.UnityEngine.Time.timeScale = 0.5
##### 调用静态方法
CS.UnityEngine.GameObject.Find('helloworld')
小技巧:如果需要经常访问的类,可以先用局部变量引用后访问,除了减少敲代码的时间,还能提高性能:
local GameObject = CS.UnityEngine.GameObject
GameObject.Find('helloworld')
#### 访问C#成员属性,方法
##### 读成员属性
testobj.DMF
##### 写成员属性
testobj.DMF = 1024
##### 调用成员方法
注意:调用成员方法,第一个参数需要传该对象,建议用冒号语法糖,如下
testobj:DMFunc()
##### 父类属性,方法
xlua支持(通过派生类)访问基类的静态属性,静态方法,(通过派生类实例)访问基类的成员属性,成员方法
##### 参数的输入输出属性(out,ref)
Lua调用测的参数处理规则:C#的普通参数算一个输入形参,ref修饰的算一个输入形参,out不算,然后从左往右对应lua 调用测的实参列表;
Lua调用测的返回值处理规则:C#函数的返回值(如果有的话)算一个返回值,out算一个返回值,ref算一个返回值,然后从左往右对应lua的多返回值。
##### 重载方法
直接通过不同的参数类型进行重载函数的访问,例如:
testobj:TestFunc(100)
testobj:TestFunc('hello')
将分别访问整数参数的TestFunc和字符串参数的TestFunc。
注意:xlua只一定程度上支持重载函数的调用,因为lua的类型远远不如C#丰富,存在一对多的情况,比如C#的int,float,double都对应于lua的number,上面的例子中TestFunc如果有这些重载参数,第一行将无法区分开来,只能调用到其中一个(生成代码中排前面的那个)
##### 操作符
支持的操作符有:+,-,*,/,==,一元-,<,<=, %,[]
##### 参数带默认值的方法
和C#调用有默认值参数的函数一样,如果所给的实参少于形参,则会用默认值补上。
##### 可变参数方法
对于C#的如下方法:
void VariableParamsFunc(int a, params string[] strs)
可以在lua里头这样调用:
testobj:VariableParamsFunc(5, 'hello', 'john')
##### 使用Extension methods
在C#里定义了,lua里就能直接使用。
##### 泛化(模版)方法
不直接支持,可以通过Extension methods功能进行封装后调用。
##### 枚举类型
枚举值就像枚举类型下的静态属性一样。
testobj:EnumTestFunc(CS.Tutorial.TestEnum.E1)
上面的EnumTestFunc函数参数是Tutorial.TestEnum类型的
另外,如果枚举类加入到生成代码的话,枚举类将支持__CastFrom方法,可以实现从一个整数或者字符串到枚举值的转换,例如:
CS.Tutorial.TestEnum.__CastFrom(1)
CS.Tutorial.TestEnum.__CastFrom('E1')
##### delegate使用(调用,+,-)
C#的delegate调用:和调用普通lua函数一样
+操作符:对应C#的+操作符,把两个调用串成一个调用链,右操作数可以是同类型的C# delegate或者是lua函数。
-操作符:和+相反,把一个delegate从调用链中移除。
> Ps:delegate属性可以用一个luafunction来赋值。
##### event
比如testobj里头有个事件定义是这样:public event Action TestEvent;
增加事件回调
testobj:TestEvent('+', lua_event_callback)
移除事件回调
testobj:TestEvent('-', lua_event_callback)
##### 64位整数支持
Lua53版本64位整数(long,ulong)映射到原生的64未整数,而luaji版本t,相当于lua5.1的标准,本身不支持64位,xlua做了个64位支持的扩展库,C#的long和ulong都将映射到userdata:
支持在lua里头进行64位的运算,比较,打印
支持和lua number的运算,比较
要注意的是,在64扩展库中,实际上只有int64,ulong也会先强转成long再传递到lua,而对ulong的一些运算,比较,我们采取和java一样的支持方式,提供一组API,详情请看API文档。
##### C#复杂类型和table的自动转换
对于一个有无参构造函数的C#复杂类型,在lua侧可以直接用一个table来代替,该table对应复杂类型的public字段有相应字段即可,支持函数参数传递,属性赋值等,例如:
C#下B结构体(class也支持)定义如下:
public struct A
{
public int a;
}
public struct B
{
public A b;
public double c;
}
某个类有成员函数如下:
void Foo(B b)
在lua可以这么调用
obj:Foo({b = {a = 100}, c = 200})
##### 获取类型(相当于C#的typeof)
比如要获取UnityEngine.ParticleSystem类的Type信息,可以这样
typeof(CS.UnityEngine.ParticleSystem)
##### “强”转
lua没类型,所以不会有强类型语言的“强转”,但有个有点像的东西:告诉xlua要用指定的生成代码去调用一个对象,这在什么情况下能用到呢?有的时候第三方库对外暴露的是一个interface或者抽象类,实现类是隐藏的,这样我们无法对实现类进行代码生成。该实现类将会被xlua识别为未生成代码而用反射来访问,如果这个调用是很频繁的话还是很影响性能的,这时我们就可以把这个interface或者抽象类加到生成代码,然后指定用该生成代码来访问:
cast(calc, typeof(CS.Tutorial.Calc))
上面就是指定用CS.Tutorial.Calc的生成代码来访问calc对象。
## xLua的配置
xLua所有的配置都支持三种方式:打标签;静态列表;动态列表。
### 打标签
xLua用白名单来指明生成哪些代码,而白名单通过attribute来配置,比如你想从lua调用c#的某个类,希望生成适配代码,你可以为这个类型打一个LuaCallCSharp标签:
[LuaCallCSharp]
public class A
{
}
> 该方式方便,但在il2cpp下会增加不少的代码量,不建议使用。
### 静态列表
有时我们无法直接给一个类型打标签,比如系统api,没源码的库,或者实例化的泛化类型,这时你可以在一个静态类里声明一个静态字段,该字段的类型除BlackList和AdditionalProperties之外只要实现了IEnumerable<Type>就可以了(这两个例外后面具体会说),然后为这字段加上标签:
[LuaCallCSharp]
public static List<Type> mymodule_lua_call_cs_list = new List<Type>()
{
typeof(GameObject),
typeof(Dictionary<string, int>),
};
> 这个字段需要放到一个静态类里头,建议放到Editor目录。
### 动态列表
声明一个静态属性,打上相应的标签即可。
[Hotfix]
public static List<Type> by_property
{
get
{
return (from type in Assembly.GetExecutingAssembly().GetTypes()
where type.Namespace == "XXXX"
select type).ToList();
}
}
Getter是代码,你可以实现很多效果,比如按名字空间配置,按程序集配置等等。
这个属性需要放到一个静态类里头,建议放到Editor目录。
### XLua.LuaCallCSharp
一个C#类型加了这个配置,xLua会生成这个类型的适配代码(包括构造该类型实例,访问其成员属性、方法,静态属性、方法),否则将会尝试用性能较低的反射方式来访问。
一个类型的扩展方法(Extension Methods)加了这配置,也会生成适配代码并追加到被扩展类型的成员方法上。
xLua只会生成加了该配置的类型,不会自动生成其父类的适配代码,当访问子类对象的父类方法,如果该父类加了LuaCallCSharp配置,则执行父类的适配代码,否则会尝试用反射来访问。
反射访问除了性能不佳之外,在il2cpp下还有可能因为代码剪裁而导致无法访问,后者可以通过下面介绍的ReflectionUse标签来避免。
### XLua.ReflectionUse
一个C#类型类型加了这个配置,xLua会生成link.xml阻止il2cpp的代码剪裁。
对于扩展方法,必须加上LuaCallCSharp或者ReflectionUse才可以被访问到。
建议所有要在Lua访问的类型,要么加LuaCallCSharp,要么加上ReflectionUse,这才能够保证在各平台都能正常运行。
### XLua.CSharpCallLua
如果希望把一个lua函数适配到一个C# delegate(一类是C#侧各种回调:UI事件,delegate参数,比如List<T>:ForEach;另外一类场景是通过LuaTable的Get函数指明一个lua函数绑定到一个delegate)。或者把一个lua table适配到一个C# interface,该delegate或者interface需要加上该配置。
### XLua.GCOptimize
一个C#纯值类型(注:指的是一个只包含值类型的struct,可以嵌套其它只包含值类型的struct)或者C#枚举值加上了这个配置。xLua会为该类型生成gc优化代码,效果是该值类型在lua和c#间传递不产生(C#)gc alloc,该类型的数组访问也不产生gc。各种无GC的场景,可以参考05_NoGc例子。
除枚举之外,包含无参构造函数的复杂类型,都会生成lua table到该类型,以及改类型的一维数组的转换代码,这将会优化这个转换的性能,包括更少的gc alloc。
### XLua.AdditionalProperties
这个是GCOptimize的扩展配置,有的时候,一些struct喜欢把field做成是私有的,通过property来访问field,这时就需要用到该配置(默认情况下GCOptimize只对public的field打解包)。
标签方式比较简单,配置方式复杂一点,要求是Dictionary<Type, List<string>>类型,Dictionary的Key是要生效的类型,Value是属性名列表。可以参考XLua对几个UnityEngine下值类型的配置,SysGCOptimize类。
### XLua.BlackList
如果你不要生成一个类型的一些成员的适配代码,你可以通过这个配置来实现。
标签方式比较简单,对应的成员上加就可以了。
由于考虑到有可能需要把重载函数的其中一个重载列入黑名单,配置方式比较复杂,类型是List<List<string>>,对于每个成员,在第一层List有一个条目,第二层List是个string的列表,第一个string是类型的全路径名,第二个string是成员名,如果成员是一个方法,还需要从第三个string开始,把其参数的类型全路径全列出来。
例如下面是对GameObject的一个属性以及FileInfo的一个方法列入黑名单:
[BlackList]
public static List<List<string>> BlackList = new List<List<string>>() {
new List<string>(){"UnityEngine.GameObject", "networkView"},
new List<string>(){"System.IO.FileInfo", "GetAccessControl", "System.Security.AccessControl.AccessControlSections"},
};
*下面是生成期配置,必须放到Editor目录下*
### CSObjectWrapEditor.GenPath
配置生成代码的放置路径,类型是string。默认放在“Assets/XLua/Gen/”下。
### CSObjectWrapEditor.GenCodeMenu
该配置用于生成引擎的二次开发,一个无参数函数加了这个标签,在执行“XLua/Generate Code”菜单时会触发这个函数的调用。
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册