思路
像所有语言一样,绑定回调主要是执行的任务执行到特定情形的时候,调用对用回调方法。 本文也一样,Lua注册回调到C++的核心思路是,当C代码执行到特定特定情形的时候,调用Lua的方法。
我这里使用的是用lua_stack直接调用lua的方法,没有使用Cocos2d-x封装的那个dispatcher,因为熟悉那个格式太墨迹了。
主要步骤如下
缓存Lua函数在Lua环境中的引用
在C代码的地方用C的方式设置好回调
在C代码回调函数执行的时候,调用lua函数
实现
C代码绑定回调,调用Lua函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | void ArmatureNode::registerMovementEventHandler( int handler) { unregisterMovementEventHandler(); //移除之前注册的监听 _movementHandler = handler; //缓存lua函数的引用 这个后边说 auto dispatcher = getCCEventDispatcher(); auto f = [ this ](cocos2d::EventCustom *event) //注册c代码形式的回调 这里用function做 { auto eventData = (dragonBones::EventData*)(event->getUserData()); auto type = ( int ) eventData->getType(); auto movementId = eventData->animationState->name; auto lastState = eventData->armature->getAnimation()->getLastAnimationState(); auto stack = cocos2d::LuaEngine::getInstance()->getLuaStack(); stack->pushObject( this , "db.ArmatureNode" ); stack->pushInt(type); stack->pushString(movementId.c_str(), movementId.size()); //通过LuaStack调用lua里的函数 最后一个参数设置参数个数 stack->executeFunctionByHandler(_movementHandler, 3); }; dispatcher->addCustomEventListener(dragonBones::EventData::COMPLETE, f); } void ArmatureNode::unregisterMovementEventHandler( void ) { if (0 != _movementHandler) { cocos2d::LuaEngine::getInstance()->removeScriptHandler(_movementHandler); //移除lua函数的绑定 _movementHandler = 0; } } |
提供Lua函数绑定到C的方法
上边的这个函数直接用cocos里的genbinding.py 是无法正确生成Lua里可调用的接口的,需要手动编写绑定方法.
说这个得用到Cocos2d-x中提供的一个方法:toluafix_ref_function会把一个Lua栈中的方法转成一个int,以便C++中调用。我会在最后面说这个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | int tolua_db_DBCCArmature_registerMovementEventHandler(lua_State* tolua_S) { if (NULL == tolua_S) return 0; int argc = 0; dragonBones::ArmatureNode* self = nullptr; self = static_cast <dragonBones::ArmatureNode*>(tolua_tousertype(tolua_S,1,0)); //第一个参数 就是lua里的self argc = lua_gettop(tolua_S) - 1; if (1 == argc) { //第二个参数,就是Lua里的function 这里要通过toluafix_ref_function这个函数映射成一个Int值 int handler = (toluafix_ref_function(tolua_S,2,0)); self->registerMovementEventHandler(handler); return 0; } return 0; } |
将绑定方法绑定到Lua环境里
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | int extends_ArmatureNode(lua_State* tolua_S) { lua_pushstring(tolua_S, "db.ArmatureNode" ); //之前db.ArmatureNode是通过脚本绑定在lua里。这里只做扩展 lua_rawget(tolua_S, LUA_REGISTRYINDEX); if (lua_istable(tolua_S,-1)) { lua_pushstring(tolua_S, "registerMovementEventHandler" ); lua_pushcfunction(tolua_S,tolua_db_DBCCArmature_registerMovementEventHandler); lua_rawset(tolua_S,-3); } lua_pop(tolua_S, 1); return 0; } |
Lua里设置回调到C++
1 2 3 4 5 6 7 8 | local arm = db.ArmatureNode:create( "Dragon" ) local animation = arm:getAnimation() animation:gotoAndPlay( "walk" ) arm:registerMovementEventHandler( function(...) print(...) end ) |
-测试
打印回调输出,测试通过 userdata 8 walk
其他
toluafix_ref_function 以及 toluafix_get_function_by_refid
这 两个方法是相互对应的 toluafix_ref_function这个方法在注册表上将一个lua的function与一个function_id生成映射 toluafix_get_function_by_refid 方法可以通过前一个方法生成的function_id来讲绑定的lua function放到栈顶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | // TOLUA_API int toluafix_ref_function(lua_State* L, int lo, int def) { if (!lua_isfunction(L, lo)) return 0; s_function_ref_id++; //function_id 加1 lua_pushstring(L, TOLUA_REFID_FUNCTION_MAPPING); //在注册表上,存放luafunction 映射table 的key压栈 lua_rawget(L, LUA_REGISTRYINDEX); //获取方法映射表,放在栈顶 lua_pushinteger(L, s_function_ref_id); //function_id压栈 lua_pushvalue(L, lo); //lo有效处索引处是lua方法,lua方法拷贝,压栈 lua_rawset(L, -3); //生成映射 lua_pop(L, 1); return s_function_ref_id; } TOLUA_API void toluafix_get_function_by_refid(lua_State* L, int refid) { lua_pushstring(L, TOLUA_REFID_FUNCTION_MAPPING); //存放luafunction 映射table 的key压栈 lua_rawget(L, LUA_REGISTRYINDEX); //获取方法映射表,放在栈顶 lua_pushinteger(L, refid); //function_id压栈 lua_rawget(L, -2); //获取到的luafunction 放到栈顶 lua_remove(L, -2); // } |
executeFunctionByHandler
executeFunctionByHandler 这个方法只是通过toluafix_get_function_by_refid 获取到function然后通过lua_pcall 方法调用,代码就不写了。