网上关于Cocos2dx开发过程中Lua的使用以及原理教程已经很多了,结合我的开发经验,我在这里稍微整理下。
可以说Cocos2dx-Lua提供了一种很轻便的开发模式,省去了冗长的编译时间,同时让热更成为了很容易的一件事情,不仅仅是在Android上,iOS上也轻易绕开了官方的审查,毕竟Lua在iOS系统看来都只是资源,就像txt文档。
然而Cocos2d-x是用C++开发的,那么,Lua究竟是怎么和C++通信的呢?在发布iOS平台的时候Lua有时候又需要和Object C交互,又应该怎么处理呢?
C/C++和Lua的通讯是通过维护一个Lua堆栈和Lua全局表进行的。Lua堆栈也是满足堆栈先进后出的特性,同时也支持使用索引进行访问,索引方式可以是正数也可以是负数,索引正数1表示栈底,索引负数-1表示栈顶。Lua启动或调用C语言时,Lua堆栈至少会有20个空闲槽位。Lua堆栈不是一个全局性的结构,每个函数都有自己的局部私有堆栈,当Lua调用C/C++函数的时候第一个参数总是这个局部栈的索引1。当C/C++函数把返回值压入Lua堆栈之后,该栈会被自动清空。
3 栈顶 -1 |
2 -2 |
1 栈底 -3 |
Lua全局表是一个类似于Map哈希表的结构,可以通过Key读取到对应的Value,比如Lua中定义有一个变量:
id = 10086
这就相当于在Lua全局表中存放了一个Key为'id',Value为‘10086’的映射关系,之后我们可以通过Key在Lua全局表中查到对应的Value‘10086’。
那么C\C++和Lua拥有不同的数据类型,要实现两者之间的数据通信该怎么办?Lua虚拟机提供Lua_State这样一种数据结构。任何一种数据从C\C++传入Lua虚拟机中,Lua都会将这类数据转换为一种通用的结构lua_TValue,并且将数据复制一份,将其压入Lua堆栈中。在Lua中,number、boolean、nil、light userdata四种类型的值是直接存在栈上元素里的,和Lua的垃圾回收无关,string、table、closure、userdata、hread存在栈上元素里的只是指针,他们都会在生命周期结束后被Lua垃圾回收。Lua有自己的GC,C\C++由自己申请和释放内存,所以两者之间的内存管理是独立的。从C\C++中传递数据到Lua虚拟机会发生数据拷贝,从Lua虚拟机中传递出来是直接从虚拟栈中取值或者地址,所以数据从虚拟栈中pop之后,是否依然是有效引用需要额外注意。
了解了上面的基础知识之后,接下来了解下Lua提供了哪些C API来对Lua堆栈进行操作,以实现C\C++和Lua的通讯。首先了解下操作Lua堆栈的基本步骤,主要有一下几步:
- 创建Lua实例:luaL_newstate();
- 加载Lua文件:luaL_loadfile(L,"hello.lua");
- 运行Lua文件:lua_pcall(L,参数个数,返回值个数,错误处理函数索引);
- 操作Lua堆栈:(参考Lua 5.1 参考手册)
lua_getglobal(L,“name”) | 把全局变量name压入堆栈 |
lua_setglobal(L,“name”) | 从堆栈上弹出一个值并将其设置到全局变量name中 |
lua_getfield(L,index,“key”) | 把t[key]的值压入栈,t是有效索引 index 指向的值 |
lua_gettable(L,index) | 把value出栈并把t[value]值压入栈,t是有效索引 index 指向的值,value是栈顶存放的值 |
lua_tonumber(L,index) | 把有效索引 index 指向的栈值转换成C类型的lua_Number值 |
lua_tostring(L,index) | 把有效索引 index 指向的栈值转换成C字符串 |
lua_pushnumber(L,num) | 把数字num压栈 |
lua_pushstring(L,str) | 把指针str指向的以零结尾的字符串做一次内存拷 |