本文主要讲述怎样根据Cocosbuilder生成的ccb文件自动生成相应的代码,主要以生成cocos2dx对应的C++文件为例子来说明。
cocosbuilder是一个非常好用的开源工具,可以用来编辑ui布局,粒子系统,简单的帧动画等。在编辑器中可以设置UI回调函数名,绑定编辑对象到Owner或者Doc root上以及为自定义的对象设置自定义的属性。以上三点都需要相应的代码来支持。因此在编辑完成游戏场景后,一定要写相应的代码。但是写相关的代码是比较繁琐的,很容易出现错误。
如果在场景编辑完成后,能根据场景文件自动生成代码框架将会节省不少工作量,并且减少出错的概率。下面分三个部分来一一说明如何实现自动生成代码,第一部分将会大体描述如何绑定代码,第二部分分析如何自动生成代码,第三部分给出代码实现
一 场景编辑器中控件与代码绑定
1.菜单回调函数,如下图设置,可以指定一个Selector和Target,在编辑器中设置后,将对应的target对象的某一个拥有特定签名的函数与selector指定的字符串绑定起来后,当菜单按下时,就会回调的相应的函数。
我们先来研究一下,在Cocos2dx中是如何将MenuItem的回调绑定到相应代码中的。
a.假设Document root对应的类为TestAutoGenLayer,为了实现绑定TestAutoGenLayer必须继承一个
cocos2d::extension::CCBSelectorResolver,然后实现 函数
virtual cocos2d::SEL_MenuHandler onResolveCCBCCMenuItemSelector(cocos2d::CCObject * pTarget, cocos2d::CCString * pSelectorName);
在该函数中完成代码绑定。
b.绑定代码如下:
[cpp]
SEL_MenuHandler TestAutoGenLayer::onResolveCCBCCMenuItemSelector(cocos2d::CCObject *pTarget, cocos2d::CCString *pSelectorName)
{
CCB_SELECTORRESOLVER_CCMENUITEM_GLUE(this,"BackPressed",TestAutoGenLayer::BackPressed);
return false;
}
SEL_MenuHandler TestAutoGenLayer::onResolveCCBCCMenuItemSelector(cocos2d::CCObject *pTarget, cocos2d::CCString *pSelectorName)
{
CCB_SELECTORRESOLVER_CCMENUITEM_GLUE(this,"BackPressed",TestAutoGenLayer::BackPressed);
return false;
}
上面代码将selector指定的值”BackPressed“与TestAutoGenLayer::的BackPressed函数绑定起来。(注:Selector的值可以函数名不相同)
c. BackPressed的代码框架如下
[cpp]
void TestAutoGenLayer::BackPressed(cocos2d::CCObject * pSender)
{
//在这里实现回调功能
}
void TestAutoGenLayer::BackPressed(cocos2d::CCObject * pSender)
{
//在这里实现回调功能
}
2.ControlButton回调
假设一个ControlButton在cocosbuild中有如下设置,
Document root还是TestAutoGenLayer类,在该类中实现CCBSelectorResolver的onResolveCCBCCControlSelector函数,假设代码如下
[cpp]
SEL_CCControlHandler TestAutoGenLayer::onResolveCCBCCControlSelector(cocos2d::CCObject *pTarget, cocos2d::CCString *pSelectorName)
{
CCB_SELECTORRESOLVER_CCCONTROL_GLUE(this,"DoNotPress",TestAutoGenLayer::DoNotPress);
return false;
}
SEL_CCControlHandler TestAutoGenLayer::onResolveCCBCCControlSelector(cocos2d::CCObject *pTarget, cocos2d::CCString *pSelectorName)
{
CCB_SELECTORRESOLVER_CCCONTROL_GLUE(this,"DoNotPress",TestAutoGenLayer::DoNotPress);
return false;
}
DonotPressMe的代码框架如下
[cpp]
void TestAutoGenLayer::DoNotPress(CCObject * pSender, cocos2d::extension::CCControlEvent pCCControlEvent)
{
//在这里实现控件回调功能
}
void TestAutoGenLayer::DoNotPress(CCObject * pSender, cocos2d::extension::CCControlEvent pCCControlEvent)
{
//在这里实现控件回调功能
}
3.将场景中的对象赋值给指定的对象,选择一个对象后,修改code connection属性,如下图
可以选择将一个对象赋值给 Document root或者是owner,指定一个绑定名字即可,下面绑定到Document root为例,假设root是TestAutoGenLayer类,被绑定的对象为CClabelTTF,为了实现绑定,a.TestAutoGenLayer必须承继于cocos2d::extension::CCBMemberVariableAssigner,并且实现方法onAssignCCBMemberVariable,b.TestAutoGenLayer 必须定一个类型为CCLabelTTF的成员变量
绑定代码段如下
[cpp]
bool TestAutoGenLayer::onAssignCCBMemberVariable(cocos2d::CCObject *pTarget, cocos2d::CCString *pMemberVariableName, cocos2d::CCNode *pNode)
{CCB_MEMBERVARIABLEASSIGNER_GLUE(this,"m_String2",CCLabelTTF*,this->m_String2);
return false;
}
bool TestAutoGenLayer::onAssignCCBMemberVariable(cocos2d::CCObject *pTarget, cocos2d::CCString *pMemberVariableName, cocos2d::CCNode *pNode)
{ CCB_MEMBERVARIABLEASSIGNER_GLUE(this,"m_String2",CCLabelTTF*,this->m_String2);
return false;
}
4.为自定义类型设置custom properties,具体在编辑中如何设置custom propertyies就不在些描述了,下面只专主如何将自定义属性绑定到代码中,cocosbuilder支持的自定义类型有4种:int,float,string,bool。假设在cocosbuilder中设置了4个自定义类型的变量,分别是
int 类型的m_intVal;
float类型的m_floatVal;
string类型的m_stringVal;
bool类型的m_boolVar;
为了实现绑定到类TestAutoGenLayer,
a.该类必须实现类cocos2d::extension::CCBMemberVariableAssigner的接口onAssignCCBCustomProperty
b.定义与自定义类型相关的变量
绑定代码如下:
[cpp]
bool TestAutoGenLayer::onAssignCCBCustomProperty(cocos2d::CCObject *pTarget, const char *pMemberVariableName, cocos2d::extension::CCBValue *pCCBValue)
{
if (pTarget == this)
{
if (0 == strcmp(pMemberVariableName, "m_intvar")){
m_intvar = pCCBValue->getIntValue();
return true;
}
if (0 == strcmp(pMemberVariableName, "m_floatVar")){
m_floatVar = pCCBValue->getFloatValue();
return true;
}
if (0 == strcmp(pMemberVariableName, "m_stringVar")){
m_stringVar = pCCBValue->getStringValue();
return true;
}
if (0 == strcmp(pMemberVariableName, "m_bool")){
m_bool = pCCBValue->getBoolValue();
return true;
}
}
return false;
}
bool TestAutoGenLayer::onAssignCCBCustomProperty(cocos2d::CCObject *pTarget, const char *pMemberVariableName, cocos2d::extension::CCBValue *pCCBValue)
{
if (pTarget == this)
{
if (0 == strcmp(pMemberVariableName, "m_intvar")){
m_intvar = pCCBValue->getIntValue();
return true;
}
if (0 == strcmp(pMemberVariableName, "m_floatVar")){
m_floatVar = pCCBValue->getFloatValue();
return true;
}
if (0 == strcmp(pMemberVariableName, "m_stringVar")){
m_stringVar = pCCBValue->getStringValue();
return true;
}
if (0 == strcmp(pMemberVariableName, "m_bool")){
m_bool = pCCBValue->getBoolValue();
return true;
}
}
return false;
}
二. 如何实现自动生成代码
根据上面的分析,我们知道一共有四种类型的绑定代码需要生成,每种类型的绑定都需要生成声明和实现(.h文件与.cpp文件),另外还要生成类的定义、构造函数及文件的尾部。下面来一一分析需要生成代码。
1.MenuItem回调
a)头文件:生成Selector对应的函数声明
b)cpp文件,生成空的Selector函数体,
c)cpp文件 生成绑定代码
2.ControlButton回调
a)头文件:生成Selector对应的函数声明
b)cpp文件,生成空的Selector函数体,
c)cpp文件 生成绑定代码
3.场景对象赋值到宿主
a)头文件,生成变量声明
b)cpp文件,生成绑定代码
c)构造函数,变量赋初值
4.自定义变量赋值到宿主
a)头文件,生成变量声明
b)cpp文件,生成绑定代码
c)构造函数,变量赋初值
5.杂项
a)类定义生成
b)构造函数生成
c)头文件尾部生成
要生成这些代码需要些什么信息呢,为了方便说明,直接上代码
[cpp]
struct TypeName {
TypeName(const std::string& t="",const std::string& n="")
:type(t),name(n){}
std::string type;
std::string name;
};
std::stringm_className;//要生成的类名
std::string m_baseClass;//生成类的父类
std::vector<TypeName>m_listAssignMember;//需要赋值的场景对象
std::vector<TypeName>m_listCutomProperty;//需要赋值的自定义变量
std::vector<std::string>m_listCContorlCallBack;//menu回调函数名
std::vector<std::string>m_listMenuCallBack;//control回调函数名
struct TypeName {
TypeName(const std::string& t="",const std::string& n="")
:type(t),name(n){}
std::string type;
std::string name;
};
std::stringm_className;//要生成的类名
std::string m_baseClass;//生成类的父类
std::vector<TypeName>m_listAssignMember;//需要赋值的场景对象
std::vector<TypeName>m_listCutomProperty;//需要赋值的自定义变量
std::vector<std::string>m_listCContorlCallBack;//menu回调函数名
std::vector<std::string>m_listMenuCallBack;//control回调函数名
这些信息从什么地方可以获取呢,只用解析cocosbuilder生成的ccb文件就可以得到了。
有了上述的信息,我们再定义一些代码模板,替换和复制代码模板就可以生成相关代码了。 以类的定义生成来说明。类的代码模板如下
[cpp]
//%class_name%.h
//
//
//this code is auto generate by the toolcreated by neo. Email andsonliang@gmail.com
//
//
#ifndef __autogen_ccbuilder__%class_name%__
#define__autogen_ccbuilder__%class_name%__
#include"cocos2d.h"
#include"cocos-ext.h"
class %class_name%
:publiccocos2d::%base_class%
, publiccocos2d::extension::CCBSelectorResolver
, publiccocos2d::extension::CCBMemberVariableAssigner
, publiccocos2d::extension::CCNodeLoaderListener
{
public:
%class_name%()
:%base_class%()
//%class_name%.h
//
//
//this code is auto generate by the toolcreated by neo. Email andsonliang@gmail.com
//
//
#ifndef __autogen_ccbuilder__%class_name%__
#define__autogen_ccbuilder__%class_name%__
#include"cocos2d.h"
#include"cocos-ext.h"
class %class_name%
:publiccocos2d::%base_class%
, publiccocos2d::extension::CCBSelectorResolver
, publiccocos2d::extension::CCBMemberVariableAssigner
, publiccocos2d::extension::CCNodeLoaderListener
{
public:
%class_name%()
:%base_class%()
在生成代码时,读入代码模板,将%class_name%替换为m_className,%base_class%替换为m_baseClass就可以了。