最近在学习cocos2dx和Lua的交互,这里笔记下学习心得。
CONTENTS
备注:
1 作者对于Lua的使用较少,纯粹从使用出发,对它的理解较浅,可能有错误,还请路过的各位大牛多多指正。
2 本笔记代码部分参考cocos2dx2.2.3代码,代码版权归原作者所有。
3 由于作者时间,经验,能力有限,笔记可能不完整,以后随用随补充吧。
CCLuaStack代码较多,里面实现了大部分Lua与C++代码的交互细节,这里一点点的笔记吧。
lua_print:
打印栈中的所有的内容。参考网址如下:【 http://116.62.110.235/blog/lua-basic-notes-stack/ 】
namespace { int lua_print(lua_State * luastate) { int nargs = lua_gettop(luastate); std::string t; for (int i=1; i <= nargs; i++) { if (lua_istable(luastate, i)) t += "table"; else if (lua_isnone(luastate, i)) t += "none"; else if (lua_isnil(luastate, i)) t += "nil"; else if (lua_isboolean(luastate, i)) { if (lua_toboolean(luastate, i) != 0) t += "true"; else t += "false"; } else if (lua_isfunction(luastate, i)) t += "function"; else if (lua_islightuserdata(luastate, i)) t += "lightuserdata"; else if (lua_isthread(luastate, i)) t += "thread"; else { const char * str = lua_tostring(luastate, i); if (str) t += lua_tostring(luastate, i); else t += lua_typename(luastate, lua_type(luastate, i)); } if (i!=nargs) t += "\t"; } CCLOG("[LUA-print] %s", t.c_str()); return 0; } } // namespace {
创建与初始化:
代码虽然很简短,但是函数调用很多。这里先笔记下最上层的函数调用,底层的函数实现分别笔记。
//创建一个新的lua运行环境 CCLuaStack *CCLuaStack::create(void) { CCLuaStack *stack = new CCLuaStack(); stack->init(); stack->autorelease(); return stack; } //将一个已有的lua运行环境附到一个新的LuaStacck上。 CCLuaStack *CCLuaStack::attach(lua_State *L) { CCLuaStack *stack = new CCLuaStack(); stack->initWithLuaState(L); stack->autorelease(); return stack; } //初始化一个新的lua运行环境 bool CCLuaStack::init(void) { m_state = lua_open(); //新建一个lua环境 luaL_openlibs(m_state); //将lua标准库注册到lua环境中 tolua_Cocos2d_open(m_state); //将cocos2dx的函数注册到lua环境中,后续笔记 toluafix_open(m_state); // Register our version of the global "print" function const luaL_reg global_functions [] = { {"print", lua_print}, {NULL, NULL} }; luaL_register(m_state, "_G", global_functions); //将自定义的print函数注册到全局table中,后续笔记 tolua_CocoStudio_open(m_state); //将cocosstudio的函数注册到lua环境中,后续笔记 #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC) CCLuaObjcBridge::luaopen_luaoc(m_state); #endif //将剩余的一些函数和变量注册到lua环境中,后续笔记 register_all_cocos2dx_manual(m_state); register_all_cocos2dx_extension_manual(m_state); register_all_cocos2dx_studio_manual(m_state); // add cocos2dx loader addLuaLoader(cocos2dx_lua_loader); return true; } //将一个已有的lua运行环境附到本LuaStack上,直接赋值即可。 bool CCLuaStack::initWithLuaState(lua_State *L) { m_state = L; return true; }
修改变量package[“path”]:
将新的lua文件路径添加到Lua保存的路径中。参考网址如下:【 http://116.62.110.235/blog/lua-basic-notes-stack-variable-exchange/ 】
void CCLuaStack::addSearchPath(const char* path) { lua_getglobal(m_state, "package"); /* L: package */ lua_getfield(m_state, -1, "path"); /* get package.path, L: package path */ const char* cur_path = lua_tostring(m_state, -1); lua_pushfstring(m_state, "%s;%s/?.lua", cur_path, path); /* L: package path newpath */ lua_setfield(m_state, -3, "path"); /* package.path = newpath, L: package path */ lua_pop(m_state, 2); /* L: - */ }
addLuaLoader:
将自定义的loader方法添加到Lua环境中。
void CCLuaStack::addLuaLoader(lua_CFunction func) { if (!func) return; // stack content after the invoking of the function // get loader table lua_getglobal(m_state, "package"); /* L: package */ lua_getfield(m_state, -1, "loaders"); /* L: package, loaders */ // insert loader into index 2 lua_pushcfunction(m_state, func); /* L: package, loaders, func */ for (int i = lua_objlen(m_state, -2) + 1; i > 2; --i) { lua_rawgeti(m_state, -2, i - 1); /* L: package, loaders, func, function */ // we call lua_rawgeti, so the loader table now is at -3 lua_rawseti(m_state, -3, i); /* L: package, loaders, func */ } lua_rawseti(m_state, -2, 2); /* L: package, loaders */ // set loaders into package lua_setfield(m_state, -2, "loaders"); /* L: package */ lua_pop(m_state, 1); }
在Lua中删掉与某个Object有关的注册信息:
void CCLuaStack::removeScriptObjectByCCObject(CCObject* pObj) { toluafix_remove_ccobject_by_refid(m_state, pObj->m_nLuaID); } void CCLuaStack::removeScriptHandler(int nHandler) { toluafix_remove_function_by_refid(m_state, nHandler); }
在C中调用Lua脚本:
参考网址如下:【 http://116.62.110.235/blog/lua-basic-notes-c-call-lua/ 】
//执行Lua脚本字符串 int CCLuaStack::executeString(const char *codes) { //首先把字符串载入到Lua环境中 luaL_loadstring(m_state, codes); return executeFunction(0);//以0个入参执行该脚本。 } //执行Lua脚本文件 int CCLuaStack::executeScriptFile(const char* filename) { #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) std::string code("require \""); code.append(filename); code.append("\""); return executeString(code.c_str()); #else //首先根据文件名找到文件的绝对路径 std::string fullPath = CCFileUtils::sharedFileUtils()->fullPathForFilename(filename); ++m_callFromLua; //执行该文件 int nRet = luaL_dofile(m_state, fullPath.c_str()); --m_callFromLua; //通过对m_callFromLua的判断,来确认lua脚本是正常执行完毕的。 CC_ASSERT(m_callFromLua >= 0); // lua_gc(m_state, LUA_GCCOLLECT, 0); if (nRet != 0) {//如果lua脚本文件有错误,这里把错误放到栈顶,这里打印出来。 CCLOG("[LUA ERROR] %s", lua_tostring(m_state, -1)); lua_pop(m_state, 1);//弹出栈顶的错误信息,保证栈恢复到以前。 return nRet; } //执行成功,忽略掉脚本返回值,直接返回0 return 0; #endif } //执行注册在Lua环境中的函数 int CCLuaStack::executeGlobalFunction(const char* functionName) { //首先在Lua环境中找到该函数,如果找到了,那么该函数会放到栈顶 lua_getglobal(m_state, functionName); /* query function by name, stack: function */ if (!lua_isfunction(m_state, -1)) {//如果栈顶不是函数,说明没有找到,直接日志,清理栈,返回。 CCLOG("[LUA ERROR] name '%s' does not represent a Lua function", functionName); lua_pop(m_state, 1); return 0; } //找到了,执行 return executeFunction(0); } //执行函数,默认此时栈已经OK,即函数被先压栈,参数以正序压栈, //numArgs表示入参的数量 int CCLuaStack::executeFunction(int numArgs) { //根据入参的数量,获取到函数在栈中的位置 int functionIndex = -(numArgs + 1); if (!lua_isfunction(m_state, functionIndex))//检查该位置是否是函数 { //不是,直接返回,并且清理栈 CCLOG("value at stack [%d] is not function", functionIndex); lua_pop(m_state, numArgs + 1); // remove function and arguments return 0; } //在Lua环境中找到函数__G__TRACKBACK__ int traceback = 0; lua_getglobal(m_state, "__G__TRACKBACK__"); /* L: ... func arg1 arg2 ... G */ if (!lua_isfunction(m_state, -1)) {//如果没有找到__G__TRACKBACK__,那么就把栈顶清理掉 lua_pop(m_state, 1); /* L: ... func arg1 arg2 ... */ } else {//找到了,就把__G__TRACKBACK__向后放,放到func后面 lua_insert(m_state, functionIndex - 1); /* L: ... G func arg1 arg2 ... */ traceback = functionIndex - 1; } int error = 0; ++m_callFromLua; //调用lua_pcall,注意这里设置了1个返回值,而且设置了错误处理函数索引traceback error = lua_pcall(m_state, numArgs, 1, traceback); /* L: ... [G] ret */ --m_callFromLua; if (error) { //如果有错误,那么根据是否有traceback函数来清理栈 if (traceback == 0) { CCLOG("[LUA ERROR] %s", lua_tostring(m_state, - 1)); /* L: ... error */ lua_pop(m_state, 1); // remove error message from stack } else /* L: ... G error */ { lua_pop(m_state, 2); // remove __G__TRACKBACK__ and error message from stack } return 0; } //没有错误,就处理返回值 // get return value int ret = 0; if (lua_isnumber(m_state, -1)) { ret = lua_tointeger(m_state, -1); } else if (lua_isboolean(m_state, -1)) { ret = lua_toboolean(m_state, -1); } // remove return value from stack lua_pop(m_state, 1); /* L: ... [G] */ //如果有错误处理函数,在弹一次栈。使栈回复原样。 if (traceback) { lua_pop(m_state, 1); // remove __G__TRACKBACK__ from stack /* L: ... */ } //返回 函数的返回值 return ret; } //根据注册函数的ID执行该函数 int CCLuaStack::executeFunctionByHandler(int nHandler, int numArgs) { int ret = 0; //首先在Lua环境中找到这个函数 if (pushFunctionByHandler(nHandler)) /* L: ... arg1 arg2 ... func */ { //找到之后,把该函数在栈中的位置调到入参下面,以便符合函数执行的要求 if (numArgs > 0) { lua_insert(m_state, -(numArgs + 1)); /* L: ... func arg1 arg2 ... */ } //执行函数 ret = executeFunction(numArgs); } //清理栈,并返回结果 lua_settop(m_state, 0); return ret; } //人工产生一个错误信息 bool CCLuaStack::handleAssert(const char *msg) { if (m_callFromLua == 0) return false; //将错误信息放到栈顶 lua_pushfstring(m_state, "ASSERT FAILED ON LUA EXECUTE: %s", msg ? msg : "unknown"); //人工产生一个Lua错误。 lua_error(m_state); return true; } int CCLuaStack::reallocateScriptHandler(int nHandler) { LUA_FUNCTION nNewHandle = -1; if (pushFunctionByHandler(nHandler)) { nNewHandle = toluafix_ref_function(m_state,lua_gettop(m_state),0); } /* toluafix_get_function_by_refid(m_state,nNewHandle); if (!lua_isfunction(m_state, -1)) { CCLOG("Error!"); } lua_settop(m_state, 0); */ return nNewHandle; } //和executeFunction类似,不过这里的Lua函数会返回多个值。这里用pResultArray来保存返回值。 int CCLuaStack::executeFunctionReturnArray(int nHandler,int nNumArgs,int nNummResults,CCArray* pResultArray) { if (NULL == pResultArray) return 0; //首先在Lua环境中找到这个函数 if (pushFunctionByHandler(nHandler)) /* L: ... arg1 arg2 ... func */ { if (nNumArgs > 0) { //调整栈的顺序,以符合Lua的调用规则 lua_insert(m_state, -(nNumArgs + 1)); /* L: ... func arg1 arg2 ... */ int functionIndex = -(nNumArgs + 1); if (!lua_isfunction(m_state, functionIndex)) { CCLOG("value at stack [%d] is not function", functionIndex); lua_pop(m_state, nNumArgs + 1); // remove function and arguments return 0; } //获取错误处理函数 int traceback = 0; lua_getglobal(m_state, "__G__TRACKBACK__"); /* L: ... func arg1 arg2 ... G */ if (!lua_isfunction(m_state, -1)) { lua_pop(m_state, 1); /* L: ... func arg1 arg2 ... */ } else { lua_insert(m_state, functionIndex - 1); /* L: ... G func arg1 arg2 ... */ traceback = functionIndex - 1; } int error = 0; ++m_callFromLua; //执行函数调用,这里设置了需要返回nNummResults个返回值 error = lua_pcall(m_state, nNumArgs, nNummResults, traceback); /* L: ... [G] ret1 ret2 ... retResults*/ --m_callFromLua; if (error) { //有错误则进行错误处理 if (traceback == 0) { CCLOG("[LUA ERROR] %s", lua_tostring(m_state, - 1)); /* L: ... error */ lua_pop(m_state, 1); // remove error message from stack } else /* L: ... G error */ { lua_pop(m_state, 2); // remove __G__TRACKBACK__ and error message from stack } return 0; } // get return value,don't pass LUA_MULTRET to numResults, if (nNummResults <= 0) return 0; //执行完毕,查询栈获取返回值,并吧返回值加到pResultArray中。 for (int i = 0 ; i < nNummResults; i++) { if (lua_type(m_state, -1) == LUA_TBOOLEAN) { bool value = lua_toboolean(m_state, -1); pResultArray->addObject(CCBool::create(value)) ; }else if (lua_type(m_state, -1) == LUA_TNUMBER) { double value = lua_tonumber(m_state, -1); pResultArray->addObject(CCDouble::create(value)); }else if (lua_type(m_state, -1) == LUA_TSTRING) { const char* value = lua_tostring(m_state, -1); pResultArray->addObject(CCString::create(value)); }else{ pResultArray->addObject(static_cast(tolua_tousertype(m_state, -1, NULL))); } // remove return value from stack lua_pop(m_state, 1); /* L: ... [G] ret1 ret2 ... ret*/ } /* L: ... [G]*/ if (traceback) { lua_pop(m_state, 1); // remove __G__TRACKBACK__ from stack /* L: ... */ } } } lua_settop(m_state, 0); return 1; }
其中,关于【 __G__TRACKBACK__ 】,可以查看一下HelloLua工程中的hello.lua文件,有部分内容如下:
-- for CCLuaEngine traceback function __G__TRACKBACK__(msg) cclog("----------------------------------------") cclog("LUA ERROR: " .. tostring(msg) .. "\n") cclog(debug.traceback()) cclog("----------------------------------------") end
基本类型压栈的处理:
//清空栈 void CCLuaStack::clean(void) { lua_settop(m_state, 0); } //以下为各种压栈 void CCLuaStack::pushInt(int intValue) { lua_pushinteger(m_state, intValue); } void CCLuaStack::pushFloat(float floatValue) { lua_pushnumber(m_state, floatValue); } void CCLuaStack::pushBoolean(bool boolValue) { lua_pushboolean(m_state, boolValue); } void CCLuaStack::pushString(const char* stringValue) { lua_pushstring(m_state, stringValue); } void CCLuaStack::pushString(const char* stringValue, int length) { lua_pushlstring(m_state, stringValue, length); } void CCLuaStack::pushNil(void) { lua_pushnil(m_state); }
复杂类型压栈的处理:
//把ccobject压入堆栈 void CCLuaStack::pushCCObject(CCObject* objectValue, const char* typeName) { toluafix_pushusertype_ccobject(m_state, objectValue->m_uID, &objectValue->m_nLuaID, objectValue, typeName); } //吧CCLuaValue压入堆栈,这里根据value的类型进行不同的操作。 void CCLuaStack::pushCCLuaValue(const CCLuaValue& value) { const CCLuaValueType type = value.getType(); if (type == CCLuaValueTypeInt) { return pushInt(value.intValue()); } else if (type == CCLuaValueTypeFloat) { return pushFloat(value.floatValue()); } else if (type == CCLuaValueTypeBoolean) { return pushBoolean(value.booleanValue()); } else if (type == CCLuaValueTypeString) { return pushString(value.stringValue().c_str()); } else if (type == CCLuaValueTypeDict) { pushCCLuaValueDict(value.dictValue()); } else if (type == CCLuaValueTypeArray) { pushCCLuaValueArray(value.arrayValue()); } else if (type == CCLuaValueTypeCCObject) { pushCCObject(value.ccobjectValue(), value.getCCObjectTypename().c_str()); } } //把一个字典压入堆栈。 void CCLuaStack::pushCCLuaValueDict(const CCLuaValueDict& dict) { lua_newtable(m_state); /* L: table */ for (CCLuaValueDictIterator it = dict.begin(); it != dict.end(); ++it) { lua_pushstring(m_state, it->first.c_str()); /* L: table key */ pushCCLuaValue(it->second); /* L: table key value */ lua_rawset(m_state, -3); /* table.key = value, L: table */ } } //把一个数组压入堆栈 void CCLuaStack::pushCCLuaValueArray(const CCLuaValueArray& array) { lua_newtable(m_state); /* L: table */ int index = 1; for (CCLuaValueArrayIterator it = array.begin(); it != array.end(); ++it) { pushCCLuaValue(*it); /* L: table value */ lua_rawseti(m_state, -2, index); /* table[index] = value, L: table */ ++index; } } //把一个函数压入堆栈,这里由于函数已经注册到了Lua环境中,这里只要把它放到栈顶即可 bool CCLuaStack::pushFunctionByHandler(int nHandler) { toluafix_get_function_by_refid(m_state, nHandler); /* L: ... func */ if (!lua_isfunction(m_state, -1)) { CCLOG("[LUA ERROR] function refid '%d' does not reference a Lua function", nHandler); lua_pop(m_state, 1); return false; } return true; }
发表评论