http://www.tuicool.com/articles/zQRbia
Cocos2d-x 3.0 中的lua binding
How it work
cocos2d-x 发展到了3.0,发生了非常大的变化。脚本的绑定也由过去简单的tolua++生成为主,然后通过luafix修正传递回调,到现在更加智能的方式去做。
新的绑定方式简单来讲是分为三步:
使用llvm前端clang对c++代码进行分析,获取AST(抽象语法树)
利用预先写好的tolua代码作为模版,使用AST信息去填写模版
一些需要fix的回调,通过manual的方式进行包装(这样c++代码就不用专门提供脚本注册函数registerScript(int functionId)之类的接口),然后再回调到正确的lua函数。
优点:
- 一套c++多套统绑定
- 脚本系统透明化,c++代码不必提供脚本接口
缺点:
生成绑定
在$(EngineDir)/tools/tolua/目录下,执行genbindings.sh生成绑定代码,前提是你已经配置好环境,完成ini文件,如何编写以及配置,具体请看github上的文档 ,通常有误都是头文件目录错误,注意查看使用的是哪个版本的头文件。
扩展绑定
新版本的回调函数绑定步骤如下:
- 增加函数对目标回调接口传递lua function
- 在该回调lua_CFunction里面,把lua function注册到事件管理器,同时增加自己的事件类型
- 在目标触发回调的地方,派发事件,传递自己的数据
- 把数据传递到注册起来的函数中
第一、二步如下代码:
static int lua_cocos2dx_AssetsManager_setDelegate(lua_State* L)
{
if (nullptr == L)
return 0;
int argc = 0;
AssetsManager* self = nullptr;
#if COCOS2D_DEBUG >= 1
tolua_Error tolua_err;
if (!tolua_isusertype(L,1,"AssetsManager",0,&tolua_err))goto tolua_lerror;
#endif
self = (AssetsManager*) tolua_tousertype(L,1,0);
#if COCOS2D_DEBUG >= 1
if (nullptr == self)
{
tolua_error(L,"invalid 'self' in function 'lua_cocos2dx_AssetsManager_setDelegate'\n",nullptr);
return 0;
}
#endif
argc = lua_gettop(L) - 1;
if (2 == argc)
{
#if COCOS2D_DEBUG >= 1
if (!toluafix_isfunction(L, 2,"LUA_FUNCTION", 0,&tolua_err) ||
!tolua_isnumber(L, 3,&tolua_err) )
{
goto tolua_lerror;
}
#endif
LuaAssetsManagerDelegateProtocol* delegate = dynamic_cast<LuaAssetsManagerDelegateProtocol*>( self->getDelegate());
if (nullptr == delegate)
{
delegate = new LuaAssetsManagerDelegateProtocol();
if (nullptr == delegate)
return 0;
self->setUserObject(delegate);
self->setDelegate(delegate);
delegate->release();
}
LUA_FUNCTION handler = toluafix_ref_function(L, 0);
ScriptHandlerMgr::HandlerType handlerType = (ScriptHandlerMgr::HandlerType) ((int)tolua_tonumber(L,3,0) + (int)ScriptHandlerMgr::HandlerType::ASSETSMANAGER_PROGRESS);
ScriptHandlerMgr::getInstance()->addObjectHandler((void*)delegate,handler,handlerType);
return 0;
}
CCLOG("'setDelegate' function of AssetsManager has wrong number of arguments: %d,was expecting %d\n",argc, 2);
return 0;
#if COCOS2D_DEBUG >= 1
tolua_lerror:
tolua_error(L,"#ferror in function 'setDelegate'.",&tolua_err);
return 0;
#endif
}
第三步:
int LuaEngine::handleAssetsManagerEvent(ScriptHandlerMgr::HandlerType type,void* data)
{
if (nullptr == data)
return 0;
BasicScriptData* eventData = static_cast<BasicScriptData*>(data);
if (nullptr == eventData->nativeObject ||nullptr == eventData->value)
return 0;
LuaAssetsManagerEventData* assetsManagerData = static_cast<LuaAssetsManagerEventData*>(eventData->value);
int handler = ScriptHandlerMgr::getInstance()->getObjectHandler((void*)eventData->nativeObject,type);
if (0 == handler)
return 0;
int ret = 0;
switch (type)
{
case ScriptHandlerMgr::HandlerType::ASSETSMANAGER_PROGRESS:
case ScriptHandlerMgr::HandlerType::ASSETSMANAGER_ERROR:
{
_stack->pushInt(assetsManagerData->value);
ret = _stack->executeFunctionByHandler(handler,1);
}
break;
case ScriptHandlerMgr::HandlerType::ASSETSMANAGER_SUCCESS:
{
ret = _stack->executeFunctionByHandler(handler,0);
}
break;
default:
break;
}
return ret;
}
第四步:
int LuaStack::executeFunctionByHandler(int nHandler,int numArgs)
{
int ret = 0;
if (pushFunctionByHandler(nHandler))/* L: ... arg1 arg2 ... func */
{
if (numArgs > 0)
{
lua_insert(_state,-(numArgs + 1));/* L: ... func arg1 arg2 ... */
}
ret = executeFunction(numArgs);
}
lua_settop(_state, 0
return ret;
}