Lua注册回调到C++

Lua注册回调到C++

http://cn.cocos2d-x.org/tutorial/show?id=1896

思路

像所有语言一样,绑定回调主要是执行的任务执行到特定情形的时候,调用对用回调方法。 本文也一样,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 方法调用,代码就不写了。

你可能感兴趣的:(Lua注册回调到C++)