教程:cocos2d-x 3.2 lua回调自定义函数的方法

昨天发的帖子:http://www.cocoachina.com/bbs/read.php?tid-293167.html 人缘太好一个没有回答的。可能太低级了。

今天我就针对没有过lua和c++交互经验的小白(就是我)写一边比较没有水准的教程;大神勿喷;

我用到的工具是:cocos2d-x 3.2 和 cocos IDE 1.20(最新版本);

第一:就是c++自定义类注册到lua中使用的方法已经在网上很全面了:http://cn.cocos2d-x.org/article/index?type=code-ide&url=/doc/cocos-docs-master/manual/code-ide/function-guides/add-3rd-party-lib/binding-custom-class-to-lua/zh.md网址如下,其实就是官方文档,写的非常好,但是,就没说cocosIDE1.20版本以前有个build runtime,1.20版本的规则就是在修改c++代码以后要重新构建一下(cocos工具->构建自定义模拟器);然后就可以使用了。

第二:就是我们要注册一个lua函数让c++调用,这个就是今天的主题了。我是自己看代码研究的;这里写的都只代表我个人的想法,不受法律保护啊。若有不对的地方请指正。

我直接上代码这样大家可以看得更清楚:

我是这么研究的:

 
   if(kScriptTypeNone != _scriptType)
    {
        BasicScriptData data(this,(void*)acc);
        ScriptEvent event(kAccelerometerEvent,&data);
        ScriptEngineManager::getInstance()->getScriptEngine()->sendEvent(&event);
    }

这段代码就是cocos自己的回调,我想他都能自己实现,肯定会给我们开发者留条路,BasicScriptData 就是我们需要传入的数据结构,ScriptEvent 这个就是中介了,接着往下看sendEvent:


int LuaEngine::sendEvent(ScriptEvent* evt)
{
    if (NULL == evt)
        return 0;
    
    switch (evt->type)
    {
        case kNodeEvent:
            {
               return handleNodeEvent(evt->data);
            }
            break;
        case kMenuClickedEvent:
            {
                return handleMenuClickedEvent(evt->data);
            }
            break;
        case kCallFuncEvent:
            {
                return handleCallFuncActionEvent(evt->data);
            }
            break;
        case kScheduleEvent:
            {
                return handleScheduler(evt->data);
            }
            break;
        case kTouchEvent:
            {
                return handleTouchEvent(evt->data);
            }
            break;

好这我们看到了里面是个switch…case 当我看到这里的时候我轻轻的微笑了一下(你懂得!!),接着往下看我们随便点击一个进去;


int LuaEngine::handleCallFuncActionEvent(void* data)
{
    if (NULL == data)
        return 0;
    
    BasicScriptData* basicScriptData = static_cast<BasicScriptData*>(data);
    if (NULL == basicScriptData->nativeObject)
        return 0;
        
    int handler =ScriptHandlerMgr::getInstance()->getObjectHandler(basicScriptData->nativeObject, ScriptHandlerMgr::HandlerType::CALLFUNC);
    
    if (0 == handler)
        return 0;
    
    Ref* target = static_cast<Ref*>(basicScriptData->value);
    if (NULL != target)
    {
        _stack->pushObject(target, "cc.Node");
    }
    int ret = _stack->executeFunctionByHandler(handler, target ? 1 : 0);
    _stack->clean();
    return ret;
}

看到这里我想大家都明白了,首先 BasicScriptData* basicScriptData = static_cast<BasicScriptData*>(data);这里就是转出来我们自定义的数据结构; int handler =ScriptHandlerMgr::getInstance()->getObjectHandler(basicScriptData->nativeObject, ScriptHandlerMgr::HandlerType::CALLFUNC);这个是什么呢?我们点进去接着看;


int  ScriptHandlerMgr::getObjectHandler(void* object,ScriptHandlerMgr::HandlerType handlerType)
{
    if (NULL == object ||   _mapObjectHandlers.empty() )
        return 0;
    
    auto iter = _mapObjectHandlers.find(object);
    
    if (_mapObjectHandlers.end() != iter)
    {
        auto iterVec = (iter->second).begin();
        for (; iterVec != (iter->second).end(); iterVec++)
        {
            if (iterVec->first == handlerType)
            {
                return iterVec->second;
            }
        }
    }
    
    return 0;
}

里面原来是个map;然后根据类型进行的匹配;看到这里的第一反应就去找这个map add的地方;果然找到了addObjectHandler(void* object,int handler,ScriptHandlerMgr::HandlerType handlerType)好;看到和我心里想的一样那肯定有调用的地方了,我一搜有好多地方,不过我们进去看看代码,大致代码如下,都差不多。


static int tolua_Cocos2d_ScriptHandlerMgr_registerScriptHandler00(lua_State* tolua_S)
{
#ifndef TOLUA_RELEASE
    tolua_Error tolua_err;
    if (!tolua_isusertype(tolua_S,1,"ScriptHandlerMgr",0,&tolua_err) ||
        !tolua_isusertype(tolua_S, 2, "cc.Ref", 0, &tolua_err) ||
        !toluafix_isfunction(tolua_S, 3, "LUA_FUNCTION", 0, &tolua_err) ||
        !tolua_isnumber(tolua_S, 4, 0, &tolua_err) ||
        !tolua_isnoobj(tolua_S,5,&tolua_err) )
        goto tolua_lerror;
    else
#endif
    {
        cocos2d::ScriptHandlerMgr* scriptHanlderMgr = static_cast<cocos2d::ScriptHandlerMgr*>(tolua_tousertype(tolua_S,1,0));
#ifndef TOLUA_RELEASE
        if (nullptr == scriptHanlderMgr)
        {
            tolua_error(tolua_S,"invalid 'scriptHanlderMgr' in function 'tolua_Cocos2d_ScriptHandlerMgr_registerScriptHandler00'\n", NULL);
            return 0;
        }
#endif
        LUA_FUNCTION handler =  toluafix_ref_function(tolua_S,3,0);
        ScriptHandlerMgr::HandlerType handlerType = (ScriptHandlerMgr::HandlerType)(int)tolua_tonumber(tolua_S, 4, 0);
        scriptHanlderMgr->addObjectHandler(tolua_tousertype(tolua_S, 2, 0), handler,handlerType);
    }
    return 1;
#ifndef TOLUA_RELEASE
tolua_lerror:
    tolua_error(tolua_S,"#ferror in function 'registerScriptHandler'.",&tolua_err);
    return 0;
#endif
}

分析一下:cocos2d::ScriptHandlerMgr* scriptHanlderMgr = static_castcocos2d::ScriptHandlerMgr*(tolua_tousertype(tolua_S,1,0)); 这就话的意思就是把栈索引为1的地方的这个usertype类型的数据拿出来进项转化;
toluafix_ref_function(tolua_S,3,0);这句话的意思有点大他就是个映射机制用这个返回值去映射了function。
ScriptHandlerMgr::HandlerType handlerType = (ScriptHandlerMgr::HandlerType)(int)tolua_tonumber(tolua_S, 4, 0);
scriptHanlderMgr->addObjectHandler(tolua_tousertype(tolua_S, 2, 0), handler,handlerType);这俩句就不用解释了,可以解释的就是我们知道这个map是怎么增加的来源的了。
关键的地方来了,ScriptHandlerMgr这个类其实就是帮助我们去和lua交互的一个管理器了,所以我们可以看到的是tolua_Cocos2d_ScriptHandlerMgr_registerScriptHandler00 当中的这个是个方法用来在lua中注册的registerScriptHandler,百度了一下果然有其用法,
所以这全部都水到渠成了。我接下来粘贴一下我自己的测试代码:



int zw::CDemo::add(int a,int b)
{
    LogicScriptData data(this,  a, b);
    ScriptEvent scriptEvent(KLogicEvent, &data);
    return ScriptEngineManager::getInstance()->getScriptEngine()->sendEvent(&scriptEvent);
    
}

以上是add是lua中调用这个函数然后这个函数再去调用lua中的函数。
接下来是lua代码:


local function onMessage(a, b)
    print(a, b)
end
local test = zw.CDemo:create()
ScriptHandlerMgr:getInstance():registerScriptHandler(test,onMessage,cc.Handler.EVENT_CUSTOM_LOGIC)
local i = test:add(1,2)

还差一部分就是事件注册:ScriptEventType 这个枚举里面要注册自己的类型就是上面我说道switch…case的地方;
还有就是CCScriptSupport.h这个文件里面定义自己的数据结构。参照类似的一下就可以;
还有就是ScriptHandlerMgr 这个类里面有个枚举HandlerType 也要注册一下自己的;cocos都给我们留好了 EVENT_CUSTOM_BEGAN = 10000,
从这里往后写就行了我的是EVENT_CUSTOM_LOGIC = EVENT_CUSTOM_BEGAN+1,然后还有就是要在lua中使用这个需要去frameworks\cocos2d-x\cocos\scripting\lua-bindings\script下面的Cocos2dConstant.lua里面去添加一下。我的是cc.Handler.EVENT_CUSTOM_LOGIC = 10001;然后就都搞定了。

接下来就是睡觉了。俩点了。以上就是全部我在lua回调函数方面的理解了,如有错误请指出,适用人群 cocos2d-x、cocosIDE和lua初期使用者;

mark@@@@@@@@@@@@@@@@@@2