要点一:C++解析ccb主要是这几个文件
CCBReader.h/cpp、CCNodeLoaderLibrary.cpp、CCNodeLoader.h/cpp等
CCBReader文件中的CCBReader::readNodeGraph方法是读取ccbi的节点(该方法应该看懂),该方法做的事有
/* 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)
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步
// 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)
/**(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)
//修改.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一样加上注册事件及删除事件及取得回调句柄
//-------------------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());
}