标签:错误 NPU 数字 交互 alt img height main cas
本文使用版本及环境: cocos3.10 Lua5.1.4 Visual Studio 2013
简介
Lua作为一种脚本语言(https://www.lua.org/),它提供了很多的 C API使得C/C++与Lua之间进行通信交互。在cocos2d-x中lua与C++的交互,主要借助于第三方工具tolua++来实现,该工具会将C++按照Lua C API指定格式生成一些绑定代码,以便于Lua通过这些绑定代码更快捷的访问C++下的类及方法相关。
而对于tolua++生成的绑定代码从实质上来说就是C++代码按照Lua支持的C语言API进行的一层包装,通过extern "C" 可以看出来。
一般脚本语言的运行需要宿主的存在,且要有对应的虚拟机。在cocos中,我们可以认为C/C++就是lua的宿主,而虚拟机说白了就是要提供一个lua运行的环境,该环境下需要保存Lua脚本运行的内存空间,全局变量,库文件等。而这个环境在Lua中叫做Lua_State。
在Lua_State环境下,若实现Lua与C/C++的数据交互,我们需要有个容器来对数据进行传递,这个容器就是Lua虚拟栈。
Lua虚拟栈
栈的特点是先进后出的,在Lua的虚拟栈中,栈中数据通过索引值进行定位,索引值可为正数,也可为负数。但是通俗的来说,正数为1的永远表示栈底,负数为-1的永远表示栈顶。
(来源:https://blog.csdn.net/zhuzhuyule/article/details/41086745)
假设我们的C++想访问lua文件中的数据:
-- 文件命名为:test.lua str = "Get Lua Data Sucess!!!" function Add(num1, num2) return num1 + num2 end
以C++获取lua变量str的数据为例,其简单的通信流程:
1. C/C++将参数str放入Lua堆栈(栈顶)中
2. Lua从堆栈中获取参数str,并将栈顶置为空
3. Lua从全局表中查找参数str对应的数据
4. 全局表将参数str的数据反馈给Lua
5. Lua将参数str的返回值放入堆栈中,此时返回值位于栈顶
6. C++从堆栈中获取返回值
/* 环境配置 1. 新建项目,选择Empty Project,在项目的Source Files新增.cpp文件 2. 若有Lua的相关环境,可将Lua/5.1目录下的include,lib文件夹拷贝到与.cpp文件同目录下 若无,则推荐LuaForWindows 其网址为:http://files.luaforge.net/releases/luaforwindows/luaforwindows 它会自动配置lua的环境,并安装SciTE工具相关,以后就可以在控制台,SciTE输入lua相关代码进行调试 属性配置,打开项目属性: 1. C/C++ -> General -> Additional Include Directories 将include目录添加进去 2. Linker -> General -> Additional Library Directories 将lib目录添加进去 3. 再通过Linker -> Input -> Additional Dependencies 添加lua5.1.lib, lua51.lib */ #include <iostream> #include <string.h> extern "C" { #include "lua.h" // 提供了Lua的基本函数,在lua.h中的函数均已"lua_"为前缀 #include "lualib.h" // 定义lua的标准库函数,比如table, io, math等 #include "lauxlib.h" // 提供了辅助库相关,以"luaL_"为前缀 } void main(){ // 创建lua环境,并加载标准库 lua_State* pL = lua_open(); luaL_openlibs(pL); // 加载lua文件,返回0表示成功 int code = luaL_loadfile(pL, "test.lua"); if (code != 0){ return; } // 执行lua文件,参数分别为,lua环境,输入参数个数,返回值个数 lua_call(pL, 0, 0); // 重置栈顶索引,设置为0表示栈清空 lua_settop(pL, 0); // ------------- 读取变量 ------------- //lua_getglobal 主要做了这么几件事: 将参数压入栈中,lua获取参数的值后再将返回的结果压入栈中 lua_getglobal(pL, "str"); // 判定栈顶值类型是否为string,返回1表示成功,0表示失败 int isStr = lua_isstring(pL, 1); if (isStr == 1) { // 获取栈顶值,并将lua值转换为C++类型 std::string str = lua_tostring(pL, 1); std::cout << "str = " << str.c_str() << std::endl; } // ------------- 读取函数 ------------- lua_getglobal(pL, "Add"); // 将函数所需要的参数入栈 lua_pushnumber(pL, 1); // 压入第一个参数 lua_pushnumber(pL, 2); // 压入第二个参数 /* lua_pcall与lua_call类似,均用于执行lua文件,其方法分别为: void lua_call(lua_State *L, int nargs, int nresults); int lua_pcall(lua_State *L, int nargs, int nresults, int errfunc); 两者的区别在于: 前者在出现错误,程序会崩溃。后者多了一个errfunc索引,用于准确定位错误处理函数。 函数执行成功返回0,失败后可通过获取栈顶信息获取错误数据 两者的共同之处在于: 会根据nargs将参数按次序入栈,并根据nresults将返回值按次序填入栈中 若返回值结果数目大于nresults时,多余的将被丢弃;若小于nresults时,则按照nil补齐。 */ int result = lua_pcall(pL, 2, 1, 0); if (result != 0) { const char *pErrorMsg = lua_tostring(pL, -1); std::cout << "ERROR:" << pErrorMsg << std::endl; lua_close(pL); return; } /* 此处的栈中情况: ------------- 栈顶 ------------- 正索引 负索引 类型 返回值 2 -1 number 3 1 -2 string "Get Lua Data Sucess!!!" ------------- 栈底 ------------- 因此如下的索引获取数字索引可以使用-1或者2 */ int isNum = lua_isnumber(pL, -1); if (isNum == 1) { double num = lua_tonumber(pL, -1); std::cout << "num = " << num << std::endl; } // 关闭state环境,即销毁Lua_State对象,并释放Lua动态分配的空间 lua_close(pL); system("pause"); }
在如上的代码中我们发现:C++在获取不同文件下的方法时,是通过include引用后,然后就直接调用;而我们的lua却是在通过luaL_loadfile进行加载,然后再通过lua_call或者lua_pcall进行执行后才能获取对应的变量或者函数返回值。其原因在于lua的脚本若为执行在其全局变量表中是不会存在相关数据的,这一点千万要注意。
接下来我们介绍一些lua C API常用方法:
/* 获取栈顶索引即栈中元素的个数,因为栈底为1,所以栈顶索引为多少,就代表有多少个元素 */ int lua_gettop(lua_State *L); /* 将栈顶索引设置为指定的数值 若设置的index比原栈顶高,则以nil补足。若index比原栈顶低,高出的部分舍弃。 比如: 栈中有8个元素,若index为7,则表示删除了一个栈顶的元素。若index为0,表示清空栈 注意,index可为正数也可为负数,但若index为正数表示相对于栈底设置的,若为负数则相对于栈顶而设置的 */ void lua_settop(lua_State *L, int index); /* 将栈中索引元素的副本压入栈顶 比如:从栈底到栈顶,元素状态为10,20,30,40;若索引为3则元素状态为:10,20,30,40,30 类似的还有: lua_pushnil: 压入一个nil值 lua_pushboolean: 压入一个bool值
lua_pushnumber: 压入一个number值 */ void lua_pushvalue(lua_State *L, int index); /* 删除指定索引元素,并将该索引之上的元素填补空缺 比如:从栈底到栈顶,元素状态为10,20,30,40;若索引为-3则元素状态为10,30,40 */ void lua_remove(lua_State *L, int index); /* 将栈顶元素替换索引位置的的元素 比如:从栈底到栈顶,元素状态为10,20,30,40,50;若索引为2则,元素状态为10,50,30,40 即索引为2的元素20被栈顶元素50替换 */ void lua_replace(lua_State *L, int index); /* 获取栈中指定索引元素的类型,若失败返回类型LUA_TNONE 其它类型有: LUA_TBOOLEAN, LUA_TNUMBER, LUA_TSTRING, LUA_TTABLE LUA_TFUNCTION, LUA_USERDATA等 */ int lua_type(lua_State *L, int idx); /* 检测栈中元素是否为某个类型,成功返回1,失败返回0 类似的还有: lua_isnumber, lua_isstring, lua_iscfunction, lua_isuserdata */ int lua_isXXX(lua_State *L, int index); /* 将栈中元素转换为C语言指定类型 */ lua_Number lua_tonumber(lua_State *L, int idx); lua_Integer lua_tointeger(lua_State *L, int idx); int lua_toboolean(lua_State *L, int idx); const char* lua_tolstring(lua_State *L, int idx, size_t *len); lua_CFunction lua_tocfunction(lua_State *L, int idx); void* lua_touserdata(lua_State *L, int idx);
如上仅仅简单介绍了下C/C++与Lua交互的基本原理,我们真正的目的是为了了解Lua是如何调用cocos引擎对应的类方法的。
cocos Lua框架
前面说过,cocos引擎通过tolua++工具,将C++按照Lua C API指定格式生成绑定代码,以便于Lua通过这些绑定代码更快捷的访问C++下的类及方法相关。此处我们并不再介绍关于tolua++的语法,也不再介绍关于cocos引擎中关于tolua工具的使用。
仅仅介绍lua在cocos引擎中的封装相关及使用相关,我们先以Lua中setPositionX为例,其绑定接口为:lua_cocos2dx_Node_setPositionX
// lua_cocos2dx_auto.cpp 第7079行 int lua_cocos2dx_Node_setPositionX(lua_State* tolua_S) { int argc = 0; cocos2d::Node* cobj = nullptr; bool ok = true; #if COCOS2D_DEBUG >= 1 tolua_Error tolua_err; #endif #if COCOS2D_DEBUG >= 1 // 获取lua栈中数据是否为cocos指定的对象类型 if (!tolua_isusertype(tolua_S,1,"cc.Node",0,&tolua_err)) goto tolua_lerror; #endif // 将lua栈中数据转换为对应的Node指针对象 cobj = (cocos2d::Node*)tolua_tousertype(tolua_S,1,0); #if COCOS2D_DEBUG >= 1 if (!cobj) { // 获取失败,报错提示:无效的方法对象 tolua_error(tolua_S,"invalid ‘cobj‘ in function ‘lua_cocos2dx_Node_setPositionX‘", nullptr); return 0; } #endif // 获取栈顶索引,即参数个数,之所以减1,是因为对象类型也在栈中 argc = lua_gettop(tolua_S)-1; if (argc == 1) { double arg0; // 获取参数值 ok &= luaval_to_number(tolua_S, 2,&arg0, "cc.Node:setPositionX"); if(!ok) { // 获取失败,报错提示:无效的参数 tolua_error(tolua_S,"invalid arguments in function ‘lua_cocos2dx_Node_setPositionX‘", nullptr); return 0; } // 调用Node中的setPositionX方法 cobj->setPositionX(arg0); // 设置lua的虚拟栈 lua_settop(tolua_S, 1); return 1; } // 获取参数个数失败,报错提示:错误的参数个数 luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.Node:setPositionX",argc, 1); return 0; }
上面的代码虽然看似使用了很多tolua的接口,但其实质上来说,无非还是这么几步:
1. 从lua栈中获取C++对象类型,并转换
2. 从lua栈中获取C++参数,校验后然后转换
3. 调用对象的类方法接口
libluacocos2d:
接下来我们看下Lua在cocos引擎封装相关,它主要被放置在cocos引擎的libluacocos2d中
auto: 使用tolua++工具自动生成的C++代码相关
manual:放置了cocos扩展的一些功能,比如事件,加密解密,节点,Lua封装以及Lua调用Java和Object-C的接口等
luajit: 高效版的lua库,额外添加了lua没有的cocos库,并在对浮点计算,循环等进行了优化
luasocket: 网络库相关
tolua: tolua++库相关
xxtea: 加密相关
Lua环境初始化
cocos2d-x对Lua相关API的封装主要通过LuaEngine和LuaStack进行了管理,其中LuaStack用于对Lua_State进行了封装,而LuaEngine则是一个管理LuaStack的单例。在我们游戏启动前,我们可以看下其流程:
bool AppDelegate::applicationDidFinishLaunching() {// 初始化LuaEngine,用于构建Lua环境并注册cocos相关的API到lua环境中 auto engine = LuaEngine::getInstance(); ... }
在调用LuaEngine::getInstance()的过程中,代码会最后进入LuaStack::init中,我们看下代码:
bool LuaStack::init(void) { // 初始化Lua环境并打开标准库 _state = lua_open(); // #define lua_open() luaL_newstate() luaL_openlibs(_state); toluafix_open(_state); // 注册全局函数print到lua中,它会覆盖lua库中的print方法 const luaL_reg global_functions [] = { {"print", lua_print}, {"release_print",lua_release_print}, {nullptr, nullptr} }; // 注册全局变量 luaL_register(_state, "_G", global_functions); // 注册cocos2d-x引擎的API到lua环境中 g_luaType.clear(); register_all_cocos2dx(_state); ... return true; }
我们再看下register_all_cocos2dx接口
TOLUA_API int register_all_cocos2dx(lua_State* tolua_S) { tolua_open(tolua_S); tolua_module(tolua_S,"cc",0); tolua_beginmodule(tolua_S,"cc"); lua_register_cocos2dx_Node(tolua_S); // ...
tolua_endmodule(tolua_S);
}
而lua_register_cocos2dx_Node则封装了Node类对应的方法相关
int lua_register_cocos2dx_Node(lua_State* tolua_S) { tolua_usertype(tolua_S,"cc.Node"); tolua_cclass(tolua_S,"Node","cc.Node","cc.Ref",nullptr); tolua_beginmodule(tolua_S,"Node"); tolua_function(tolua_S,"setPositionX",lua_cocos2dx_Node_setPositionX); ... tolua_endmodule(tolua_S); std::string typeName = typeid(cocos2d::Node).name(); g_luaType[typeName] = "cc.Node"; g_typeCast["Node"] = "cc.Node"; return 1; }
到此处就首尾连接上了。
本人能力有限,以后会慢慢补充!!!
标签:错误 NPU 数字 交互 alt img height main cas
原文地址:https://www.cnblogs.com/SkyflyBird/p/11938373.html