2014-08-28 00:29:25 stalendp 阅读数 16846
  • Cocos2d-x 游戏开发详解

    本课程通过20章125节课 详细讲述了Cocos2d-x开发的详细过程,内容包括:基础编程:开发环境搭建、核心类、标签、菜单、精灵、事件处理、动作、定时器、GUI、数据结构;高级开发:音效、网络编程、数据存储、Tiled Map、粒子系统、物理引擎、常用工具和编辑器和项目实战。

    7323 人正在学习 去看看 郭宏志

转发,请保持地址:http://blog.csdn.net/stalendp/article/details/38880997

很长时间没有碰cocos2dx了,最近又用起来了,花了好几个小时重新熟悉了一下,发现很多新的特性值得用来写文章。好吧,先从最常用的开始。最近用windows,使用cocosStudio,就从这个开始吧,顺带介绍一下工程的创建,以及c++11的特性。以前开发使用cocosBuilder开发界面(相关集成见这篇文章)。

准备工作

1)准备NDK、andrdoid-SDK,VisualStudio等工具。并设置环境变量NDK_ROOT,ANDROID_SDK_ROOT,Path等

2)安装Python 2.7.x (不是3.x.x);

3)下载cocos2d-x-3.2压缩包

创建游戏项目

打开命令行工具,切换到cocos2d-x-3.2目录下,执行命令:

1)  setup.py, 用来设置环境变量

2)cocos new circleTheLine -p com.evilgame.circleTheLine -l cpp -d x:\your\project\path , 用来创建项目

在2)命令执行完后, x:\your\project\path目录下,会出现如下内容;点击proj.win32下的visualStudio工程,就可以打开项目。

使用CocosStudio创建界面

我这里只是做一个页面,所以只选择UI Editor来制作。


制作的界面如下:


然后把该项目导出,得到NewUi_1.json,test.png, test.plist文件。把这三个文件拷贝到游戏根目录下的Resources目录下。

集成cocos2dx

现在的目标是,当用户点击“测试按钮”后,改变界面上的文字。

1)加入相应的lib工程。

因为cocosStudio的使用,需要依赖于libCocosStudio, libGUI, libExtensions三个库,具体路径如下(D:\Program Files\cocos2d-x-3.2\为根目录)

libCocosStudio D:\Program Files\cocos2d-x-3.2\cocos\editor-support\cocostudio
libGUI D:\Program Files\cocos2d-x-3.2\cocos\ui\proj.win32
libExtensions D:\Program Files\cocos2d-x-3.2\extensions\proj.win32

需要修改visualStudio,对它们进行引用,具体如下:

1a) 把上面的三个工程加入“解决方案”中。

右击项目,在弹出的菜单中选择 添加/现有项,如下:


1b) 添加引用依赖:(解决链接的问题)

右击项目,在弹出的菜单中选择 属性,在弹出的对话框中做如下操作:


1c)添加头文件:(解决编译的问题)

#include "cocostudio/CocoStudio.h"
#include "ui/CocosGUI.h"

USING_NS_CC;
using namespace cocostudio;
using namespace ui;
注意,命名空间ui,其实是cocos2d的子命名空间;所以需要写在USING_NS_CC下面,或者写成:

using namespace cocos2d::ui;

如果上面路径无法找到,请按照下面的方法,查看是否包含了相应的路径。

右击项目,在弹出的菜单中选择 属性,在弹出的对话框中做如下操作:


其中@{EngineRoot}是宏变量,用来指定cocos2dx的根目录(我这里是D:\Program Files\cocos2d-x-3.2\);头文件就是相对于这里的“附加包含目录”的设置的。所以做相应的调整,确保路径正确。

2)显示该界面:

        node = GUIReader::shareReader()->widgetFromJsonFile("NewUi_1.json");
        // 这里的node是类变量
	if (node == nullptr)
	{
		return true;
	}
	this->addChild(node);

// 当然读取场景文件,可以用以下的语句
// CCNode *pNode = SceneReader::sharedSceneReader()->createNodeWithSceneFile("SceneEditorTest/SceneEditorTest.json"); 

3)绑定按钮事件:

	Button* button = static_cast<Button*>(node->getChildByName("btnTest"));
	button->addTouchEventListener([&](Ref* sender, Widget::TouchEventType type) {
		if (type == Widget::TouchEventType::ENDED) {
			// 注意node的生命周期的问题
			TextBMFont* nn = static_cast<TextBMFont*>(node->getChildByName("displayMsg")); 
			nn->setString("msg changed!!");
		}
	});
上面的代码使用了C++11的lambda表达式特性,下面将做介绍。

C++11特性--lambda表达式

c++终于可以使用lambda表达式了!这将为编码带来极大的方便,上面的例子代码,很好地体现了。
1) 三种方法的对比:
写法1(未用lambda表达式):
auto closeItem = MenuItemImage::create(  
                                        "CloseNormal.png",  
                                        "CloseSelected.png",  
                                        CC_CALLBACK_1(HelloWorld::menuCloseCallback,this));  
                                          
void HelloWorld::menuCloseCallback(Object* sender)  
{  
    Director::getInstance()->end();  
}  
写法2(lambda函数指针):
auto callEnd = [](Object* sender)  
{  
    Director::getInstance()->end();//直接在这里添加按钮要调用的代码   
};    
auto closeItem = MenuItemImage::create(  
                                        "CloseNormal.png",  
                                        "CloseSelected.png",  
                    allEnd);     
写法3(lambda表达式):
auto closeItem = MenuItemImage::create(  
                                        "CloseNormal.png",  
                                        "CloseSelected.png",  
                                        [](Object* sender)  
                    {  
                        Director::getInstance()->end();//直接在这里添加按钮要调用的代码  
                    });  
如果要设定表达式能够访问外部变量,可以在 [] 内写入 & 或者 = 加上变量名,其中 & 表示按引用访问,= 表示按值访问,变量之间用逗号分隔,比如 [=factor, &total] 表示按值访问变量 factor,而按引用访问 total。如果是引用的话,需要注意该对象的生命周期,回调函数的生命周期和控件相同。

另外更多技术分享和讨论,请加群:385121586


参考:

C++11特性:http://blog.csdn.net/star530/article/details/19913611
官方例子中的ExtensionTest/CocoStudioSceneTest/SceneEditorTest.cpp
2013-08-13 17:32:38 stalendp 阅读数 7119
  • Cocos2d-x 游戏开发详解

    本课程通过20章125节课 详细讲述了Cocos2d-x开发的详细过程,内容包括:基础编程:开发环境搭建、核心类、标签、菜单、精灵、事件处理、动作、定时器、GUI、数据结构;高级开发:音效、网络编程、数据存储、Tiled Map、粒子系统、物理引擎、常用工具和编辑器和项目实战。

    7323 人正在学习 去看看 郭宏志

转发,请保持地址:http://blog.csdn.net/stalendp/article/details/9951187

在Cocos2dx中已经有三种字体工具了,几本上能够满足游戏中对字体的要求。其中CCLabelTTF使用系统的ttf文件,CCLabelBMFont字体工具制作,CCLabelAtlas使用贴图,不过字体的大小要相同等约束。CCLabelTTF最为方便,速度最慢,效果最少;CCLabelBMFont方便性速度效果等都居中,CCLabelAtlas不太方便,不过性能和效果最优,可以使用美工制作的字体。我在这里要使用美工提供的字体,所以好像只能够用CCLabelAtlas了,不过感觉还是不很方便,所以自己定义了一个字体工具,分享给大家。实现方式和CCLabelBMFont、CCLabelAtlas相似。不过可以把字体图片打包到spreetSheet中,然后提供一个映射就可以了,使用方式如下:

const char* map[] = {
	"0", "0.png",
	"1", "1.png",
	"2", "2.png",
	"3", "3.png",
	"4", "4.png",
	"5", "5.png",
	"6", "6.png",
	"7", "7.png",
	"8", "8.png",
	"9", "9.png",
	",", ",.png",
	":", "colon.png",
	"/", "dghh.png",
	".", "dot.png",
};
//http://stackoverflow.com/questions/2236197/c-easiest-way-to-initialize-an-stl-vector-with-hardcoded-elements
std::vector<const char*> mm(map, map+sizeof(map)/sizeof(map[0])); // parameter: fist, last
// 注意这里少了加载plist的步骤,这个和CCSpriteBatchNode的用法是一样的,因为MyLabel就是其子类,我以后再加上
MyLabel* mylabel = MyLabel::create("font.png", 10, mm, .9f);  //font.png是通过texturePacker之类工具生成的文件
mylabel->setString("12.231");

下面是实现(其中MyRGBAProtocolAdapter请参考上一片文章):

class MyLabel : public CCSpriteBatchNode , public CCLabelProtocol, public MyRGBAProtocolAdapter {
private:
	std::map<char, CCSprite*> words;
	std::vector<ccV3F_C4B_T2F_Quad> quads;
	float mlWidth, mlHeight, scale;
	const char* label;
	
public:
	
	static MyLabel* create(const char* fileImage, unsigned int capacity, std::vector<const char*>& map, float scale /*字体间距*/) {
		MyLabel* pRet = new MyLabel();
		if (pRet && pRet->doMyInit(fileImage, capacity, map, scale)) {
			pRet->autorelease();
			return pRet;
		}
		CC_SAFE_DELETE(pRet);
		return NULL;
	}
	
	
	// save the words to map, in order to access them quickly
	bool doMyInit(const char* fileImage, unsigned int capacity, std::vector<const char*>& map, float _scale = 1.f /*字体间距*/)  {
		this->scale = _scale;
		int size = map.size();
		if (!initWithFile(fileImage, capacity) || size%2!=0) {
			return false;
		}
		size /= 2;
		for (int i=0; i<size; i++) {
			char key = map[2*i][0];
			const char* fn = map[2*i+1];
			CCSprite* sprite = CCSprite::createWithSpriteFrameName(fn);
			if (!sprite) {
				CCLog("[MyLabel] The sprite with name '%s' is not exists!", fn);
				return false;
			}
			words[key] = sprite;  // insert the sprites
			sprite->retain();  // keep sprite from deleting
		}
		return true;
	}
	
	virtual ~MyLabel() { // delete the sprite
		for (std::map<char, CCSprite*>::iterator it = words.begin(); it!=words.end(); it++) {
			CC_SAFE_DELETE(it->second);
		}
	}
	
	const char* getString() {
		return label;
	}
	
	void setString(const char* _label) {
		this->label = _label;
		_setString(label);
	}
	
	void _setString(std::string str) {
		// clean and make enough room for new str
		m_pobTextureAtlas->removeAllQuads();
		quads.resize(str.length());
		int index = 0;
		mlWidth = 0;
		mlHeight = 0;
		
		if (str.length() > m_pobTextureAtlas->getCapacity()) {
			increaseAtlasCapacity();
		}
		
		for (std::string::iterator it = str.begin(); it!=str.end(); it++, index++) {
			CCSprite * sprite = NULL;
			
			// make sure the character is in the map
			std::map<char, CCSprite*>::iterator wit = words.find(*it);
			if (wit==words.end()) {
				CCLog("The character '%c' is not found in the map, please check!", *it);
				return;
			} else {
				sprite = wit->second;
			}
			
			// calculate the position of the sprite;
			CCSize size = sprite->getContentSize();
			sprite->setPosition(ccp(mlWidth + size.width/2*scale, 0));
			mlWidth += size.width * scale;
			mlHeight = size.height > mlHeight ? size.height : mlHeight;
			
			// insert the quote, the sprite->updateTransform() will do the following job
			sprite->setBatchNode(this);
			sprite->setAtlasIndex(index);
			sprite->setDirty(true);
			sprite->updateTransform();
			
			quads[index] = sprite->getQuad();
		}
		// correct the size of batch
		setContentSize(ccp(mlWidth, mlHeight));
	}
	
protected:
	// ========= MyRGBAProtocolAdapter begin ============
	CCArray* getRGBAChildren() {
		return getChildren();
	}
	
	CCNode* getRGBAParent() {
		return getParent();
	}
	
	void updateDisplayedColor(const ccColor3B& parentColor) {
		_displayedColor.r = _realColor.r * parentColor.r/255.0;
		_displayedColor.g = _realColor.g * parentColor.g/255.0;
		_displayedColor.b = _realColor.b * parentColor.b/255.0;
		
		_updateMLColor();
	}
	
	virtual void updateDisplayedOpacity(GLubyte parentOpacity) {
		_displayedOpacity = _realOpacity * parentOpacity/255.0;
		
		_updateMLColor();
	}
	
	
	void _updateMLColor() {
		ccColor4B color4 = {
			_displayedColor.r * _displayedOpacity/255.0f,
			_displayedColor.g * _displayedOpacity/255.0f,
			_displayedColor.b * _displayedOpacity/255.0f,
			_displayedOpacity };
		int idx = 0;
		for (std::vector<ccV3F_C4B_T2F_Quad>::iterator it = quads.begin(); it!=quads.end(); it++, idx++) {
			it->bl.colors = color4;
			it->br.colors = color4;
			it->tl.colors = color4;
			it->tr.colors = color4;
			m_pobTextureAtlas->updateQuad(&(*it), idx);
		}
	}
	
	// ========= MyRGBAProtocolAdapter end ============
	
};


2013-04-03 15:47:09 stalendp 阅读数 5275
  • Cocos2d-x 游戏开发详解

    本课程通过20章125节课 详细讲述了Cocos2d-x开发的详细过程,内容包括:基础编程:开发环境搭建、核心类、标签、菜单、精灵、事件处理、动作、定时器、GUI、数据结构;高级开发:音效、网络编程、数据存储、Tiled Map、粒子系统、物理引擎、常用工具和编辑器和项目实战。

    7323 人正在学习 去看看 郭宏志

转发,请保持地址:http://blog.csdn.net/stalendp/article/details/8755958

最近在接触CocosBuilder之后,发现这是一款非常优秀的软件,可以加快cocos2dx的开发速度。我在研究的过程中,写了一个demo,现在陆续分享出来,希望得到大家的支持。

这个demo介绍了在Cocos2dx中集成CocosBuilder实现动画等技巧,还结合Box2d进行物理模拟,简化Box2d的编码过程。这片文章先大致介绍这个demo。

(一)开始界面,按钮移入特效,星星闪动特效


(二)play界面,涉及到Box2d的集成,并对复杂的机器人进行box2d化。其中演示了怎么使用CCSpriteBatchNode,CCPhysicsSprite的进行编码来提高效率。

点击Play按钮的时候,会把一个冲量(Impulse)作用于apple和机器人,使其运动。


OK,Demo的大致内容介绍结束,下一片文章将介绍CocosBuilder的使用和集成。


[声明]这个demo中使用了一款游戏中的图片资源,这里只用于学习目的,如果涉及到版权问题(请联系我:stalendp@gmail.com)


源代码下载: http://vdisk.weibo.com/s/BSjUaUQYZZ_MR

2014-05-12 15:48:27 guggy 阅读数 3343
  • Cocos2d-x 游戏开发详解

    本课程通过20章125节课 详细讲述了Cocos2d-x开发的详细过程,内容包括:基础编程:开发环境搭建、核心类、标签、菜单、精灵、事件处理、动作、定时器、GUI、数据结构;高级开发:音效、网络编程、数据存储、Tiled Map、粒子系统、物理引擎、常用工具和编辑器和项目实战。

    7323 人正在学习 去看看 郭宏志

(转载请注明原文:http://blog.csdn.net/while0/article/details/25615685)


本文章特指使用C++作为编程语言,基于cocos2dx游戏引擎开发游戏。


在cocos2dx中,sprite作为精灵类是使用最为频繁的类,与其它类相比,如:Node, Layer或Scene,Sprite最大的不同是它包含一个纹理,通过OpenGL的渲染,在游戏中呈现出来。游戏中的主角,怪物,背景,或是精灵的血条等都是通过Sprite来实现的。


在cocos2dx中,关于创建Sprite的类,根据输入参数的不同有以下几个工厂函数,

    static Sprite* create()
    static Sprite* create(const std::string& filename)
    static Sprite* create(const std::string& filename, const Rect& rect)
    static Sprite* createWithTexture(Texture2D *texture)
    static Sprite* createWithTexture(Texture2D *texture, const Rect& rect, bool rotated=false)
    static Sprite* createWithSpriteFrame(SpriteFrame *spriteFrame)
    static Sprite* createWithSpriteFrameName(const std::string& spriteFrameName)


顺便提醒,工厂函数中Sprite的实例都调用了autorelease(),所以由工厂函数返回的实例一般立即通过addChild到加入到父节点中,这样它的引用计数加一,表示有人在使用这个实例,这个实例不能被释放。当然,如果你不想立即加入到父节点中,也可以调用retain()函数直接对其引用计数加1,不然在主循环的下一次,就会将其回收,这是cocos2dx内存自动回收的机制决定的。


在游戏的开发过程中,使用Sprite最频繁的编码就是三部曲 (1)创建Sprite (2)加入layer中 (3)设置精灵位置:

Sprite *sprite = Sprite::create("hero.png");
this->addChild(sprite);
sprite->setPosition(Point(100, 200));


第一行创建Sprite的实例,也就是创建了一个类Sprite的对象,这个是在工厂函数中实现的,

    Sprite *sprite = new Sprite();
    if (sprite && sprite->initWithFile(filename))
    {
        sprite->autorelease();
        return sprite;
    }
    CC_SAFE_DELETE(sprite);
    return nullptr;

我们重点关注第一句:

Sprite *sprite = new Sprite();

它决定了工厂函数创建的实例是Sprite类型,所以当在编码中调用Sprite的虚函数时,调用的就是Sprite类中的相应函数。

可能你会有这样一个问题:如果我想重载一个虚函数,在这个函数中实现自己需要的一些特殊行为,该怎么做?

答案是:必须继承Sprite创建一个你自己精灵类,在类中重载这个虚函数,这样还不够,必须定义这个新的继承类的工厂函数,在这个工厂函数中创建这个类的对象,

class MySprite : public Sprite
{
    static MySprite * create(const char *pszFileName);
}

MySprite * MySprite ::create(const char *filename)
{
    MySprite *sprite = new MySprite();
    if (sprite && sprite->initWithFile(filename)) 
    {
        sprite->autorelease(); return sprite; 
    }
    CC_SAFE_DELETE(sprite); 
    return nullptr;
}


在程序中使用MySprite的代码与Sprite类似,

MySprite *sprite = MySprite::create("hero.png");
this->addChild(sprite);
sprite->setPosition(Point(100, 200));


如果输入参数不同,可以参考Sprite类的实现,创建不同输入参数的工厂函数。
所以,按照目前cocos2dx的设计,在Sprite的继承类中,必须重新实现新类的工厂函数,在工厂函数中创建新类的实例,这样新类中重载的虚函数就会被调用到。


使用C++编程,经常需要继承Sprite来定制和封装游戏特定的精灵。有时候可能需要在Sprite的基础上进行多次继承,来达到类重用的目的。
本文下面提供两种方法,提供给读者参考。在这两种方法中,不需要在继承类中重新实现工厂函数。


  1. 在工厂函数中,实例作为输入参数传入
MySprite * MySprite ::create(Sprite* sprite, const char *filename)
{
    if (sprite && sprite->initWithFile(filename))
    {
        sprite->autorelease();
        return sprite;
    }
    CC_SAFE_DELETE(sprite);
    return nullptr;
}


调用MySprite的代码:
MySprite *sprite = new MySprite();
MySprite::create(sprite, "hero.png");
this->addChild(sprite);
sprite->setPosition(Point(100, 200));


在MySprite的基础上再做继承时,就不需要实现工厂函数了。

例如:从MySprite继承新类MySprite2:
class MySprite2 : public MySprite
{
    ...
}


调用MySprite2的代码:
MySprite2 *sprite = new MySprite2();
MySprite::create(sprite, "hero.png");
this->addChild(sprite);
sprite->setPosition(Point(100, 200));


    2. 创建newInstance的虚函数

第二种方法,我们可以通过虚函数newInstance()来避免重复创建工厂函数。在newInstance中创建新类的对象。

class MySprite : public Sprite
{
    static MySprite * create(const char *filename);
    virtual Sprite* newInstance();
}
Sprite* MySprite::newInstance()
{
    return new MySprite();
}

MySprite * MySprite ::create(const char *filename)
{
    MySprite *sprite = newInstance();
    if (sprite && sprite->initWithFile(filename)) 
    {
        sprite->autorelease(); return sprite; 
    }
    CC_SAFE_DELETE(sprite); 
    return nullptr;
}


调用MySprite的代码:

MySprite::create("hero.png");
this->addChild(sprite);
sprite->setPosition(Point(100, 200));

第二种方法的好处是在使用MySprite时与Sprite的完全相同,但需要在继承类中实现虚函数newInstance()。

本文抛砖引玉,在您的实现过程中也许会有更好的方法,欢迎讨论,共同进步。

2013-04-03 18:34:11 stalendp 阅读数 20682
  • Cocos2d-x 游戏开发详解

    本课程通过20章125节课 详细讲述了Cocos2d-x开发的详细过程,内容包括:基础编程:开发环境搭建、核心类、标签、菜单、精灵、事件处理、动作、定时器、GUI、数据结构;高级开发:音效、网络编程、数据存储、Tiled Map、粒子系统、物理引擎、常用工具和编辑器和项目实战。

    7323 人正在学习 去看看 郭宏志

转发,请保持地址:http://blog.csdn.net/stalendp/article/details/8756467

本文主要侧重于CocosBuilder在cocos2dx中的集成。对CocosBuilder使用的介绍比较简单。详细的使用请参考官方教程

CocosBuilder部分

1)创建工程

首先创建Cocos2dx工程“ShootTheApple”,然后在其Resources目录下新建ccb,并在ccb中创建CocosBuilder工程。目录结构如下图:


CocosBuilder工程会产生3类文件,后缀分别为ccbproj, ccb, ccbi(其中前两个文件使工程文件--ccb.ccbproj, apple.ccb。      ccbi文件,是导出的文件,被cocos2dx程序使用,如上图的apple.ccbi);

2)配置cocosBuilder属性

点击菜单File/Publish Setting..., 弹出如下对话框:

按上图进行配置,其中最为重要的是选择导出ccbi的目录,这里将设置为cocos2dx的Resources目录,就是cocosBuilder的上级目录。然后勾选最后的“Flatten paths when publishing”和“Only publish ccb-files”选项。

观察ccb/Resources目录,其中有个ccbResources文件,这里面放ccb使用的图片等资源。在ccb发布成ccbi后,我们还需要把此ccbResources拷贝到cocos2dx的resources目录下,这样ccbi才可以正确的读取。

3)创建ccb文件。

菜单File/New/New File..., 弹出如下的对话框:


这里可以创建4类Object,CCLayer, CCNode, CCParticleSystemQuad和CCSprite;这里还可以选择游戏运行的device的类型,保持默认,点击create后,给定名称(这里为MainScene.ccb),选择ccb/Resources为其根目录,就生成了一个ccb文件。操作界面如下图:

点击界面下方时间轴中的物体,其构成了树状结构(这个正是cocos2dx中对CCNode的构成结构);选择其中一项,比如CCLayer,其右方将显示其相关属性。比如Position, ContentSize,AnchorPoint等信息。当cocosBuilder生成ccbi的时候,将导出这些信息(文件为二进制形式,其中还包含动画信息和回调函数的信息;动画信息在操作界面的下方定义)。cocos2dx在载入这个ccbi文件的时候,将调用相关解析器(CCBReader)来解析这个文件,生成相应的cocos2dx对象,然后呈现出来,并播放相应的动画。

4)编辑ccb文件--按钮

在cocosBuilder的编辑,都是“可见即可得”的。cocosBuilder兼容SpriteSheet(可以使用TexturePacker进行编辑),编辑的时候,直接把图片拖动到模拟器就可以了。

创建按钮有两种方式(和cocos2dx中一样),这里只介绍使用Menu的方式。先创建Menu,点击工具栏中的Menu按钮(左边),如下图:

在当前的Menu中,然后点击Menu按钮的右边5下,就创建了5个MenuItem。类的层次结构,可以在时间轴边上看到。如下图:

创建的按钮如下图:

这时需要在右侧的设置属性,如下图:

设置事件响应函数的名称(如btnPlay,target为Document root);并设置CCMenuItem在三种不同情况下的呈现的图片。完成的按钮如下:


配置完5个按钮,调整位置和缩放等,如下:



4)编辑ccb文件--动画

这里将运用cocosBuilder来制作背景中的一个闪烁的星星,具体使用的是Rotate和Scale动画。
将一颗星星拖入到模拟器中,并移动到右上角,如下图:
选中这个物体,先缩放其为0.1f; 然后在事件轴中的0s和2s处,分别按快捷键v, r和s,创建Visible,Rotate和Scale的关键帧(对应菜单Animations/Insert Keyframe中的相关选项),在1s处,创建scale关键帧;如下图:

点击Scale在1s处的关键帧,做适当的放大和旋转操作。
点击播放按钮,或者手工拖动,可以参看效果。


目前动画只会播放一次,在cocosBuilder中设置循环播放,很简单,在时间轴的下方选择chain TimeLine为自身就可以了,如下图:

这个原理就是,当播放完当前的TimeLime之后,接着再播放此timeline,这样就循环起来的了。

关于timeLine,还可以设置其播放时间(默认为10s),点击时间轴上方的时间:

在弹出的对话框中设置就可以了:


还可以创建多个时间轴,


按照上面的循环动画的方式,也可以串起来做复杂的动画。这些动画也可以在cocos2dx代码中被调用。十分的灵活方便。


代码的集成

当cocosBuilder完成对ccb的编辑之后,点击菜单File/Publish, 导出ccb成为ccbi文件到cocos2dx的Resources文件目录下了(请参考上面的“配置cocosBuilder属性”)。
然后在xcode中把ccbResources目录和相应的ccbi文件加入到工程中,这样就可以进行代码编辑了。
代码的集成,其实就是解析ccbi文件的过程,cocos2dx中针对cocosBuilder有相应的解析器CCBReader(位于extensions/CCBreader目录下,CCBReader和CocosBuilder的版本有关系,请参考文末的“版本说明”);
由于C++没有像Object-C的动态特性,所以集成CCBReader的代码并没有像cocos2d中那么简单。不过理解其原理,理清思路后,其实也不是件困难的事情。

所以,先讲CCBReader的原理。ccbi文件其实对应了4类cocos2dx对象,一个ccbi文件中,包含了此对象的一些属性信息。CCBReader读取了这个ccbi文件,就清楚了这个对象的一切。这个时候,要把这些信息和我们的代码联系起来,就需要用CCNodeLoaderLibrary来联系自定义的类,接着把这个信息抛给CCBReader,由其进行联系。具体代码如下:

        CCNodeLoaderLibrary * ccNodeLoaderLibrary = CCNodeLoaderLibrary::newDefaultCCNodeLoaderLibrary();
        ccNodeLoaderLibrary->registerCCNodeLoader("Intro", IntroLoader::loader());
        
        /* Create an autorelease CCBReader. */
        cocos2d::extension::CCBReader * ccbReader = new cocos2d::extension::CCBReader(ccNodeLoaderLibrary);
        
        /* Read a ccbi file. */
        CCNode * node = ccbReader->readNodeGraphFromFile("MainScene.ccbi");
        ccbReader->release();
        
        CCScene* scene = CCScene::create();
        scene->addChild(node);

要理解上面的代码,还需补充一点上面没有提示的信息。默认情况下,ccbi对应了cocos2dx的4个类型之一,如果要对应我们自定义的类型(从4个类型中派生出来的),我们要在cocosBuilder中的根对象命名,如下图:

选择CCLayer,在右侧的Custom class中填写Intro。(如果是Custom class显示为 JS Controller,点击菜单Document/JavaScipt Controlled, 去掉其选项)。这个就可以理解上面的代码了:
        CCNodeLoaderLibrary * ccNodeLoaderLibrary = CCNodeLoaderLibrary::newDefaultCCNodeLoaderLibrary();
        ccNodeLoaderLibrary->registerCCNodeLoader("Intro", IntroLoader::loader());
IntroLoader对象负责加载相关的类Intro。完整代码如下:
#ifndef ShootTheApple_Intro_h
#define ShootTheApple_Intro_h

#include "PhysicsSprite.h"
#include "CcbBase.h"

#include "Playground.h"

USING_NS_CC;
USING_NS_CC_EXT;

class Intro: public CcbBase {
public:
    CCB_STATIC_NEW_AUTORELEASE_OBJECT_WITH_INIT_METHOD(Intro, create);
    
    Intro(){ }
    
    virtual ~Intro() { }
    
    void btnPlay(cocos2d::CCObject *pSender) {  //这个是回调函数,
        CCDirector::sharedDirector()->replaceScene(PlaygroundLoader::loadCcbi());
    }
    
    virtual cocos2d::SEL_MenuHandler onResolveCCBCCMenuItemSelector(cocos2d::CCObject * pTarget, const char * pSelectorName) {
        CCB_SELECTORRESOLVER_CCMENUITEM_GLUE(this, "btnPlay", Intro::btnPlay);  // 把btnPlay对应回调函数,btnPlay的设置在“编辑ccb文件--按钮”中介绍了
    }

};




/* Forward declaration. */
class CCBReader;

class IntroLoader : public cocos2d::extension::CCLayerLoader {
public:
    static CCScene* loadCcbi() {
        CCNodeLoaderLibrary * ccNodeLoaderLibrary = CCNodeLoaderLibrary::newDefaultCCNodeLoaderLibrary();
        ccNodeLoaderLibrary->registerCCNodeLoader("Intro", IntroLoader::loader());
        
        /* Create an autorelease CCBReader. */
        cocos2d::extension::CCBReader * ccbReader = new cocos2d::extension::CCBReader(ccNodeLoaderLibrary);
        
        /* Read a ccbi file. */
        CCNode * node = ccbReader->readNodeGraphFromFile("MainScene.ccbi");
        ccbReader->release();
        
        CCScene* scene = CCScene::create();
        scene->addChild(node);
        
        return scene;
    }
public:
    CCB_STATIC_NEW_AUTORELEASE_OBJECT_METHOD(IntroLoader, loader);
    
protected:
    CCB_VIRTUAL_NEW_AUTORELEASE_CREATECCNODE_METHOD(Intro);
};

#endif

#ifndef ShootTheApple_CcbBase_h
#define ShootTheApple_CcbBase_h

#include "cocos2d.h"
#include "cocos-ext.h"
#include "Box2D.h"

USING_NS_CC;
USING_NS_CC_EXT;

class CcbBase: public cocos2d::CCLayer
, public cocos2d::extension::CCBSelectorResolver
, public cocos2d::extension::CCBMemberVariableAssigner
, public cocos2d::extension::CCNodeLoaderListener
{
public:
    virtual cocos2d::SEL_MenuHandler onResolveCCBCCMenuItemSelector(cocos2d::CCObject * pTarget, const char * pSelectorName) {
        // do nothing
        //CCB_SELECTORRESOLVER_CCMENUITEM_GLUE(this, "onPlay", Playground::btnPlay);
        return NULL;
    }
    virtual cocos2d::extension::SEL_CCControlHandler onResolveCCBCCControlSelector(cocos2d::CCObject * pTarget, const char * pSelectorName) {
        // do nothing
        return NULL;
    }
    virtual bool onAssignCCBMemberVariable(cocos2d::CCObject * pTarget, const char * pMemberVariableName, cocos2d::CCNode * pNode) {
        // do nothing
        //CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "apple", CCSprite *, this->apple);
        return NULL;
    }
    virtual bool onAssignCCBCustomProperty(CCObject* pTarget, const char* pMemberVariableName, cocos2d::extension::CCBValue* pCCBValue) {
        // do nothing
        return false;
    }
    virtual void onNodeLoaded(cocos2d::CCNode * pNode, cocos2d::extension::CCNodeLoader * pNodeLoader) {
        // do nothing
    }
};


#endif

CCReader进行ccbi和自定义类的联系的过程如下,CCBReader一边解析ccbi文件,一边调用自定义类的onResolveCCBCCMenuItemSelector等方法;在这些回调方法中,可以进行相关的绑定工作,如对象绑定,CCMenuItem selector的绑定等。更多的绑定细则,请参考cocosBuilder中的帮助。

如果用Object-C就方便多了,由于其动态性,可以根据名称自动反射,而C++则需要手工绑定,繁琐些,不过为了cocosBuilder带来的方便,这个也值得。

下一篇文章介绍怎么使用CocosBuilder加快Box2d的开发

版本说明

在这个demo中,cocosBuilder的版本为Version 3 alpha3,配套的cocos2dx版本为cocos2d-2.1rc0-x-2.1.2;

cocosBuilder的官方:http://cocosbuilder.com/

cocosBuilder官方教程:https://github.com/vlidholt/CocosDragon/blob/master/Tutorial/Tutorial.md

cocos2dx的相关页面:http://www.cocos2d-x.org/news/95


源代码下载: http://vdisk.weibo.com/s/BSjUaUQYZZ_MR

源码下载2:链接:http://pan.baidu.com/s/1qYJ4Nnu 密码:g66s

没有更多推荐了,返回首页