zoukankan      html  css  js  c++  java
  • [cocos2dx] lua注册回调到c++

    思路

    像所有语言一样,绑定回调主要是执行的任务执行到特定情形的时候,调用对用回调方法。 这里也一样。核心思路是,当c代码执行到特定特定情形的时候,调用lua的方法

    我这里使用的是用lua_stack直接调用lua的方法,没有使用cocos2dx封装的那个dispatcher,因为熟悉那个格式太墨迹了

    主要步骤如下

    • 缓存lua函数在lua环境中的引用
    • 在c代码的地方用c的方式设置好回调
    • 在c代码回调函数执行的时候,调用lua函数

    实现

    • c代码绑定回调,调用lua函数
    
    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里可调用的接口的,需要手动编写绑定方法

          说这个得用到cocos2dx中提供的一个方法 toluafix_ref_function 会把一个lua栈中的方法转成一个int,以便c++中调用。我会在最后面说这个

     
    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环境里
    
    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++
    
     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放到栈顶

    
    //
    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 方法调用 代码就不写了


    疑惑

    包括cocos2dx里的所有lua扩展(不是通过脚本直接生成lua接口的)都是通过注册表里扩展的 lua_rawget(tolua_S, LUA_REGISTRYINDEX); 我没完全看完lua里的userdata绑定过程,封装的太深了。

    疑惑是绑定了以后也是userdata,但是扩展的时候拿到都是table。按我已看到的代码是userdata绑定到global表里的。这里的实现机制怎么回事,知道的,望不吝指点

    转载请注明出处(http://www.cnblogs.com/boliu/p/4091274.html)

  • 相关阅读:
    POJ3311Hie with the Pie(floyd传递+DP,状态压缩)
    POJ1185炮兵阵地(DP状态压缩)
    POJ3254Corn Fields (状态压缩or插头DP)
    eBPF Tracing 入门教程与实例
    因为 Java 和 Php 在获取客户端 cookie 方式不同引发的 bug
    DRDS 数据恢复重磅发布,全方位保障您的数据安全
    前沿 | 全球最具影响力开源数据库峰会开幕在即 阿里云精彩议题先睹为快
    MaxCompute 预付费标准版VS套餐版
    DTCC 2019 | 深度解码阿里数据库实现 数据库内核——基于HLC的分布式事务实现深度剖析
    从 Apache ORC 到 Apache Calcite | 2019大数据技术公开课第一季《技术人生专访》
  • 原文地址:https://www.cnblogs.com/boliu/p/4091274.html
Copyright © 2011-2022 走看看