cocos2d-x解析ccb及绑定到lua

要点一:C++解析ccb主要是这几个文件

CCBReader.h/cpp、CCNodeLoaderLibrary.cpp、CCNodeLoader.h/cpp等

CCBReader文件中的CCBReader::readNodeGraph方法是读取ccbi的节点(该方法应该看懂),该方法做的事有

1.读取该节点类型(自定义类型这里用到了)  
    /* Read class name. */
    std::string className = this->readCachedString();

2.节点名字memberVarAssignmentName

    // Read assignment type and name
    int memberVarAssignmentType = this->readInt(false);
    std::string memberVarAssignmentName;
    if(memberVarAssignmentType != kCCBTargetTypeNone) {
        memberVarAssignmentName = this->readCachedString();
    }
3.解析节点属性

    ...
    CCNodeLoader *ccNodeLoader = this->mCCNodeLoaderLibrary->getCCNodeLoader(className.c_str());
    if (! ccNodeLoader)
    {
        CCLog("no corresponding node loader for %s", className.c_str());
        returnNULL;
    }
    CCNode *node = ccNodeLoader->loadCCNode(pParent, this);
    ...
    // Read properties
    ccNodeLoader->parseProperties(node, pParent, this);
从上面3中可以得知mCCNodeLoaderLibrary为CCNodeLoaderLibrary.cpp类实例对象getCCNodeLoader(className.c_str())为获取对应的节点类型Loader实例对象。

要点:把CCB原有控件及事件绑定到Lua中(CCControlButton)

在CCNode * CCBReader::readNodeGraph(CCNode * pParent)方法最后加上以下代码

    //-------------------binding lua
    if (memberVarAssignmentName != "") {
        ZGLuaUtils::bindCCBAssign(memberVarAssignmentName.c_str(), className.c_str(), node);
    }
    //-------------------binding lua------end
我们看下C++中ZGLuaUtils类代码

void ZGLuaUtils::bindCCBAssign(constchar* assignmentName, constchar *className, CCNode* node) {
     lua_State *L = ((CCLuaEngine*)CCScriptEngineManager::sharedManager()->getScriptEngine())->getLuaStack()->getLuaState();
     lua_getglobal(L, "GF_setCCBBind");  
     /**//* Push PARAMETERS to STACK */
     tolua_pushstring(L, assignmentName);
     tolua_pushusertype(L, node, className);
     /**//* Call FUNCTION in LUA */ 
     int iError; 
     iError = lua_pcall(L,    //VMachine  
                        2,    //Argument Count  
                        LUA_MULTRET,    //Return Value Count  
                        0);  
     if (iError)  
     {  
         constchar* errorInfo = lua_tostring(L, 1);
         CCLOG("CCBReader::bindCCBAssign pcall FAILED, %s, %d", errorInfo, iError);
     }  
     /**//* Check Return Value Types */ 
}

上面调用 了Lua中的全局函数也即C++调用Lua拉,查看ZGCCBSupport.lua文件

GV_CCBVars = nil
function GF_setCCBBind(nodeKey, node)
     CCBLOG ("binding.."..nodeKey)
     GV_CCBVars[nodeKey] = node
end

lua中具体实现调用节点控件见LuaLayer.lua文件

function LuaLayer:createLayer()
     self:initForVars()
     self.layer = CCBReader:nodeGraphFromFile(self.ccbiName)
     self.ccbReader = GV_CCBReader
     self.ccbReader:retain()
     self:initUI()
     local function sceneEventHandler( eventType )
           if eventType == "enter" then
              self:onPreEnter()
           else
              self:onPreExit()
           end
     end
     self.layer:registerScriptHandler(sceneEventHandler)
     return self.layer
end
-- init ccb vars
function LuaLayer:initForVars( ) 
     self.ccbVars = {}
     self:initVarsForCCB()
     GV_CCBVars = self.ccbVars
end
经过以上操作Lua中就能操作该节点拉!下面实现绑定控件的事件,也即注册控件事件 (如CCControlButton)
1)修改CCNodeLoader.h中的BlockCCControlData加上std::string mSelectorName;

struct BlockCCControlData {
       SEL_CCControlHandler mSELCCControlHandler;
       CCObject * mTarget;
       std::string mSelectorName;
       int mControlEvents;
};
2)修改CCNodeLoader.cpp函数parsePropTypeBlockCCControl

BlockCCControlData * CCNodeLoader::parsePropTypeBlockCCControl(CCNode * pNode, CCNode * pParent, CCBReader * pCCBReader) {
      //-----直接传出带selectorName的BlockData
      std::string selectorName = pCCBReader->readCachedString();
      // 在返回之前,要把下面这个玩意读出来,否则的话会造成读取数据的错位
      pCCBReader->readInt(false);        // for int selectorTarget
      pCCBReader->readInt(false);         // for int controlEvents
      if(selectorName.length() > 0) {
           BlockCCControlData *blockData = newBlockCCControlData();
           blockData->mSelectorName = selectorName;
           return blockData;
      }
      //-----直接传出带selectorName的BlockData---end
      return NULL;
}
3)修改CCControlLoader.cpp中的事件函数onHandlePropTypeBlockCCControl

#include "CCControlLoader.h"
#include "ZGLuaUtils.h"
void CCControlLoader::onHandlePropTypeBlockCCControl(CCNode * pNode, CCNode * pParent, constchar * pPropertyName, BlockCCControlData * pBlockCCControlData, CCBReader * pCCBReader) {
     ZGLuaUtils::bindCCBFunctionForCCControl(pBlockCCControlData->mSelectorName.c_str(), "CCControlButton", pNode, false);
}
我们看下C++中ZGLuaUtils类代码

void ZGLuaUtils::bindCCBFunctionForCCControl(constchar *selectorName, constchar *className, cocos2d::CCNode *node, bool isRegistForTouch) {
     lua_State *L = getLuaState();
     lua_getglobal(L, "GF_setCCBCallback");
     /**//* Push PARAMETERS to STACK */
     tolua_pushusertype(L, node, className);
     tolua_pushstring(L, selectorName);
     tolua_pushboolean(L, isRegistForTouch);
     /**//* Call FUNCTION in LUA */
     int iError;
     iError = lua_pcall(L,    //VMachine
                        3,    //Argument Count
                        LUA_MULTRET,    //Return Value Count
                        0);
     if (iError)
     {
          constchar* errorInfo = lua_tostring(L, 1);
          CCLOG("CCBReader::bindCCBFunctionForCCControl pcall FAILED, %s, %d", errorInfo, iError);
     }
     /**//* Check Return Value Types */
}
上面调用 了Lua中的全局函数也即C++调用Lua拉,查看ZGCCBSupport.lua文件
   function GF_setCCBCallback(node, callbackKey, isRegistForTouch)
        CCBLOG ("binding callback.."..callbackKey)
        local value = GV_CCBVars[callbackKey]
        if isRegistForTouch then
        -- registe for CCControlButton and others
             node:registerScriptTouchHandler(GV_CCBVars[callbackKey])
        else
        -- registe for CCMenuItems
             GF_dump(node, "node")
             GF_dump(callbackKey, "callbackKey")
             node:registerScriptTapHandler(GV_CCBVars[callbackKey])
        end
   end
上面取得了CCBI中事件名称对应到Lua中相同名称的方法再注册节点脚本事件,但是上面貌似完美但是CCControlButton默认没有registerScriptTapHandler 方法,所以node:registerScriptTapHandler(GV_CCBVars[callbackKey])还是有问题所以还要第4步
4)修改CCControlButton.h文件增加以下代码
     // add lua touch support
     protected:
         int m_nScriptTapHandler;
     public:
         virtualvoid registerScriptTapHandler(int nHandler);
         virtualvoid unregisterScriptTapHandler(void);
         int getScriptTapHandler() { returnm_nScriptTapHandler; };
    // add lua touch support
修改CCControlButton.cpp文件,增加以下代码
    #include "script_support/CCScriptSupport.h"
    #include "ZGLuaUtils.h"
    voidCCControlButton::registerScriptTapHandler(int nHandler)
    {     
         unregisterScriptTapHandler();
         m_nScriptTapHandler = nHandler;
         LUALOG("[LUA] Add CCMenuItem script handler: %d", m_nScriptTapHandler);
    }

    voidCCControlButton::unregisterScriptTapHandler(void)
    {
         if (m_nScriptTapHandler)
         {
             CCScriptEngineManager::sharedManager()->getScriptEngine()->removeScriptHandler(m_nScriptTapHandler);
             LUALOG("[LUA] Remove CCMenuItem script handler: %d", m_nScriptTapHandler);
             m_nScriptTapHandler = 0;
         }
    } 
5)修改ZGToLua 加上以上二个方法以便在Lua中能访问(不能是tolua工具生成,参数是句柄而不是真正意义上的int)
说明:如果Lua在C++中注册回调函数。也即向C++的函数传的参是Lua函数,但C++函数形参却是int型(Lua函数的标号即句柄),在这里程序启动后,每注册一个回调则回调的函数标识是加1的,即句柄是自增长的int型
上面已经实现在Lua中绑定事件即Lua中注册事件但还要在Lua中实现响应事件
6)修改CCControlButton.cpp文件
   /**(i)在 CCControlButton::~CCControlButton()方法体内加上unregisterScriptTapHandler();
    **(ii)在 ccTouchEnded方法实现调用注册的事件即加上以下代码
    **if (m_nScriptTapHandler != 0) {
    **     ZGLuaUtils::getLuaEngine()->executeControlButtonEvent(this);
    **}
    **/ 
 
    void CCControlButton::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent)
    {
         m_isPushed = false;
         setHighlighted(false);
    
    
         if (isTouchInside(pTouch))
         {
             sendActionsForControlEvents(CCControlEventTouchUpInside);
             if (m_nScriptTapHandler != 0) {
                 ZGLuaUtils::getLuaEngine()->executeControlButtonEvent(this);
             }
         }
         else
         {
             sendActionsForControlEvents(CCControlEventTouchUpOutside);        
         }
    }
7)修改CCLuaEngin.h/ccp
     //修改.h文件增加以下代码
     #include "cocos-ext.h"
     virtual int executeControlButtonEvent(cocos2d::extension::CCControlButton *controlButton);
      
     //修改.cpp文件增加以下代码
     int CCLuaEngine::executeControlButtonEvent(cocos2d::extension::CCControlButton *controlButton) {
         int nHandler = controlButton->getScriptTapHandler();
         if (!nHandler) return0;
    
         m_stack->pushInt(controlButton->getTag());
         m_stack->pushCCObject(controlButton, "CCControlButton");
         returnm_stack->executeFunctionByHandler(nHandler, 2);
     }
要点:把CCB定义控件及事件绑定到Lua中(CCScrollLayerButton)
 1)CCLuaEngin.h/ccp 其它一些控件类会调用CCLuaEngin中的一些方法,而本类又是C++调用Lua堆栈,用处是处理控件和控件的事件响应
//修改.h文件,加上以下代码
#include "CCScrollLayerButton.h"
virtual int executeScrollLayerButtonEvent(CCScrollLayerButton *scrollLayerButton);
//修改.cpp文件,加上以下代码
int CCLuaEngine::executeScrollLayerButtonEvent(CCScrollLayerButton *scrollLayerButton) {
    int nHandle = scrollLayerButton->getScriptTapHandler();
    if (!nHandle) return 0;
    m_stack->pushInt(scrollLayerButton->getTag());
    m_stack->pushCCObject(scrollLayerButton, "CCScrollLayerButton");
    return m_stack->executeFunctionByHandler(nHandle, 2);
}
2)修改CCScrollLayerButton.h/cpp 。跟CCControlButton一样加上注册事件及删除事件及取得回调句柄
3)修改CCBReader.cpp  CCNode * CCBReader::readNodeGraph(CCNode * pParent)方法最后加上以下代码
     //-------------------binding lua
     if (memberVarAssignmentName != "") {
          //CCScrollLayerButton特殊处理
          if (strcmp(className.c_str(), "CCScrollLayerButton") == 0) {
               vector stringVec;
               ZGStringUtils::splitString(memberVarAssignmentName, "@", stringVec);
               string member = stringVec[0];
               string function = stringVec[1];
               //bind var name
               ZGLuaUtils::bindCCBAssign(member.c_str(), "CCScrollLayerButton", node);
               //bind function
               ZGLuaUtils::bindCCBFunctionForCCControl(function.c_str(), "CCScrollLayerButton", node, false);
          }else{
               ZGLuaUtils::bindCCBAssign(memberVarAssignmentName.c_str(), className.c_str(), node);
          }
     }
     //-------------------binding lua------end
3)修改ZGToLua.cpp

bindCCBFunctionForCCControl方法调用lua中的方法ZGCCBSupport.lua 文件中的GF_setCCBCallback方法,而方法又调用了节点的node:registerScriptTapHandler(GV_CCBVars[callbackKey]), 因为Lua调用c++代码且是自定义控件所以ZGToLua中加入CCScrollLayerButton的registerScriptTapHandler方法
4)但是由上面要点一第3小点可得CCBReader还会解析控件的属性所以必须加上以下代码,当然也要在项目上加上CCScrollLayerButtonLoader.h文件

    #include "CCScrollLayerButtonLoader.h"

     voidCCNodeLoaderLibrary::registerDefaultCCNodeLoaders() {
         this->registerCCNodeLoader("CCScrollLayerButton", CCScrollLayerButtonLoader::loader());
     }

你可能感兴趣的:(cocos2d-x)