【cocos2d-x 3.x 学习与应用总结】2: 在cocos2d-x中使用ccbi
前言本文以3.9版本的cocos2d-x为例,总结了如何在代码中解析、加载ccbi文件。给出一个最简单的使用ccbi实现的helloworld的例子、一个加强版的HelloWorld示例、以及一个最贴近实际使用情况的ccbi使用示例,并结合示例分析ccbi的解析过程。 官方示例程序ccbi功能支持的源代码cocos对ccbi的支持是在extensions这个模块里面,以v3.9为例,解析ccbi的代码的物理路径是放在cocos2d/cocos/editor-support/cocosbuilde这个路径下面,在vs的解决方案管理器中如下图所示: 官方示例源代码cocos官方的ccbi使用示例代码是在(vs中):TestCpp/ExtensionsTest/CocosBuilderTest这个分组下面, #include "CocosBuilderTest.h"
#include "../../testResource.h"
#include "HelloCocosBuilder/HelloCocosBuilderLayerLoader.h"
USING_NS_CC;
USING_NS_CC_EXT;
using namespace cocosbuilder;
CocosBuilderTests::CocosBuilderTests()
{
ADD_TEST_CASE(CocosBuilderTestScene);
}
bool CocosBuilderTestScene::init() {
if (TestCase::init())
{
/* Create an autorelease NodeLoaderLibrary. */
auto nodeLoaderLibrary = NodeLoaderLibrary::newDefaultNodeLoaderLibrary();
nodeLoaderLibrary->registerNodeLoader("HelloCocosBuilderLayer",HelloCocosBuilderLayerLoader::loader());
/* Create an autorelease CCBReader. */
cocosbuilder::CCBReader * ccbReader = new cocosbuilder::CCBReader(nodeLoaderLibrary);
/* Read a ccbi file. */
auto node = ccbReader->readNodeGraphFromFile("ccb/HelloCocosBuilder.ccbi",this);
ccbReader->release();
if (node != nullptr) {
this->addChild(node);
}
return true;
}
return false;
}
示例代码中包括了动画、UI按钮、菜单、标签、粒子、Scrollview等相关示例,这里不贴代码了,需要的话去cpptests工程里看就行了。 下面以我的实际使用经验总结一下在实际项目中该如何使用ccbi,先从一个HelloWorld说起。 使用ccbi的HelloWorld使用cocosbuilder创建一个简单的ccbi在cocosbuilder中新建一个hello.ccb,注意:不要勾选js controll.(在Document菜单项下最后一个选项) 添加一个Label,名字叫mLabel: 添加一个MenuItemImage名字叫mBtn,selector名字叫onClick.: 保存,发布为hello.ccbi. 创建一个最简单的class,来使用创建出来的ccbi文件:CcbiHelloWorld.h: 定义了一个class继承自Node,用来挂载ccbi #ifndef PLAYING_WITH_COCOS3D_PAGE_CCB_HELLOWORLD_H
#define PLAYING_WITH_COCOS3D_PAGE_CCB_HELLOWORLD_H
#include "cocos2d.h"
#include "cocosbuilder/CocosBuilder.h"
class CcbiHelloWorld : public cocos2d::Node
{
public:
CREATE_FUNC(CcbiHelloWorld);
bool init() override
{
using cocosbuilder::NodeLoaderLibrary;
using cocosbuilder::CCBReader;
// 第一步: 创建一个NodeLoaderLibrary
auto loaderLib = NodeLoaderLibrary::newDefaultNodeLoaderLibrary();
// 第二步: 创建CCBReader
auto ccbReader = new CCBReader(loaderLib);
// 第三步: 调用CCBReader的readNodeGraphFromFile的方法,传入ccbi名字
auto node = ccbReader->readNodeGraphFromFile("ccbi/hello.ccbi",this);
ccbReader->release();
// 解析完毕,可以使用Node了。
if ( node )
{
addChild(node);
}
return true;
}
};
#endif //PLAYING_WITH_COCOS3D_PAGE_CCB_HELLOWORLD_H
在AppDelegate的applicationDidFinishLaunching()方法结尾处,添加启动代码: auto node = CcbiHelloWorld::create();
auto scene = Scene::create();
scene->addChild(node);
director->runWithScene(scene);
编译运行,一个最简单的使用ccbi的例子就跑起来了,如下图所示: 从上面编写HelloWorld的过程可以看出,要想把ccbi当做节点添加到界面是很简单的事情,从CcbiHelloWorld类的init方法中看到,几句关键代码加起来不超过10行。 不过,这个例子仅仅是把cocosbuilder当做了画板来用,所有的东西加到界面上都是死的,点按钮也没有反应,我不能获取某个节点,做一些动作和属性修改的操作。有点像静态网站和动态网站的区别,游戏又不是美术作品,肯定是要动起来的。因此我需要能够获取ccbi界面上的元素,并且对它做一些操作。下面的第二个例子是展示了如何绑定ccbi上的变量和menu回调。 绑定ccbi上的变量和回调,加强版HelloWorld在上一个例子的基础上,来实现加强版HelloWorld, CcbiHelloWorldEnhanced.h: #ifndef PLAYING_WITH_COCOS3D_PAGE_CCB_HELLOWORLD_ENHANCED_H
#define PLAYING_WITH_COCOS3D_PAGE_CCB_HELLOWORLD_ENHANCED_H
#include "cocos2d.h"
#include "cocosbuilder/CocosBuilder.h"
#include "cocosbuilder/CCBMemberVariableAssigner.h"
#include "cocosbuilder/CCBSelectorResolver.h"
class CcbiHelloWorldEnhanced
: public cocos2d::Node,public cocosbuilder::CCBMemberVariableAssigner // 绑定ccbi上的变量,public cocosbuilder::CCBSelectorResolver // 绑定ccbi上的回调
{
public:
// ------------------ create方法和init方法相对于第一个例子都没有修改 -------------------
CREATE_FUNC(CcbiHelloWorldEnhanced);
bool init() override
{
using cocosbuilder::NodeLoaderLibrary;
using cocosbuilder::CCBReader;
// 第一步: 创建一个NodeLoaderLibrary
auto loaderLib = NodeLoaderLibrary::newDefaultNodeLoaderLibrary();
// 第二步: 创建CCBReader
auto ccbReader = new CCBReader(loaderLib);
// 第三步: 调用CCBReader的readNodeGraphFromFile的方法,传入ccbi名字
auto node = ccbReader->readNodeGraphFromFile("ccbi/hello.ccbi",this);
ccbReader->release();
// 解析完毕,可以使用Node了。
if (node)
{
addChild(node);
}
return true;
}
// ============ 重写 CCBMemberVariableAssigner 的方法,绑定ccbi上的变量 ==========
// 这个函数是在CCBReader在解析过程中,遇到有名字的变量就会回调过来
// 在这里来把我在ccbi上定义的两个变量"mLabel"和"mBtn"两个控件绑定到两个成员变量label_和menuItemImage_上.
bool onAssignCCBMemberVariable( cocos2d::Ref* target,const char* memberVariableName,cocos2d::Node* node) override
{
// ccbi上的变量名
std::string name(memberVariableName);
if (name == "mLabel") // 是那个"mLabel"Label吗?
{
node->retain();
label_ = dynamic_cast<cocos2d::Label*>(node);
if (!label_)
{
node->release(); // 类型不匹配的话,就release掉,避免内存泄露
}
return true;
}
else if (name == "mBtn") // 是那个"mBtn"MenuItemImage吗?
{
node->retain();
menuItemImage_ = dynamic_cast<cocos2d::MenuItemImage*>(node);
if (!menuItemImage_)
{
node->release(); // 类型不匹配的话,就release掉,避免内存泄露
}
return true;
}
return false;
}
// ========== 重写 CCBSelectorResolver 的方法实现回调绑定 ================================
cocos2d::SEL_MenuHandler onResolveCCBCCMenuItemSelector(
cocos2d::Ref * pTarget,const char* pSelectorName) override
{
if (std::string(pSelectorName) == "onClick") // 是mBtn声明的那个onClick回调吗?
{
return CC_MENU_SELECTOR(CcbiHelloWorldEnhanced::onClicked); // 返回一个Ref::(*)(Ref*)类型的成员函数指针
}
return nullptr;
}
// 忽略 CCBSelectorResolver 的control回调绑定
cocos2d::extension::Control::Handler onResolveCCBCCControlSelector(
cocos2d::Ref * pTarget,const char* pSelectorName) override
{
return nullptr;
}
// 被绑定到"onClick"的回调函数,类型是Ref::(*)(Ref*),其中sender参数就是ccbi上的那个MenuItemImage的指针
void onClicked(cocos2d::Ref *sender)
{
// sender 就是从ccbi上解析出来的那个MenuItemImage
CCAssert(menuItemImage_ == sender,"sender should be the item binded");
if (label_)
{
label_->runAction(cocos2d::RotateBy::create(0.5,360));
}
}
// 构造,析构,注意初始化和release.
CcbiHelloWorldEnhanced() : label_(nullptr),menuItemImage_(nullptr) {}
~CcbiHelloWorldEnhanced()
{
CC_SAFE_RELEASE(label_);
CC_SAFE_RELEASE(menuItemImage_);
}
cocos2d::Label *label_;
cocos2d::MenuItemImage *menuItemImage_;
};
#endif //PLAYING_WITH_COCOS3D_PAGE_CCB_HELLOWORLD_ENHANCED_H
修改AppDelegate的applicationDidFinishLaunching方法: auto node = CcbiHelloWorldEnhanced::create();
auto scene = Scene::create();
scene->addChild(node);
director->runWithScene(scene);
编译运行,可以看到如下效果, 并且 这个例子已经让ccbi的使用更充分了,游戏动了起来,而且也能绑定到ccbi上的变量了。但是有两个问题还很明显: 问题1:在绑定控件变量的时候 在这个ccbi上,只有两个控件,因此勉强可以接受上面例子那样挨个判断 问题2:同理,回调的绑定 下面着手解决这两个问题,不需要定义那么多成员变量,也不需要定义那么多回调函数,让ccbi的使用更简单、自然。 一个更加实用的ccbi使用方法,避免频繁的
|