先来看看Cocos2d-x中关于游戏的一些基础概念,首先盗用官网的图描述一下游戏中各个游戏对象的关系。
从上图中可以知道在Cocos2d-x中只存在一个导演,而事实上之前我们也看到CCDirector是一个单例对象;而游戏中可以存在多个场景,图中存在N个场景;每个场景中又可以存在多个层;每个层中又可以包含多个精灵。
一个导演同一时间只能运行一个场景,一个场景当中,可以同时加载多个层,一个层同可以可载多个精灵。层中亦可以加层。
其实CCScen、CCLayer与CCSprite都直接或间接派生自CCNode;CCNode是与渲染有关的基类,定义了一些用于显示子类的方法,之后再详细介绍它。
导演(CCDirector):
Cocos2d-x中统筹游戏大局的类,游戏中一些常用的的操作就是由CCDirector来控制,在游戏中是单例对象,项目中没错获取的CCDirector都是同一个对象。例如:OpenGL ES的初始化,场景的转换,游戏的暂停与继续控制,世界坐标与GL坐标的转换,对游戏节点的控制等都是CCDirector控制。
场景(CCScene):
场景的基础关系:class CC_DLL CCScene : public CCNode。
场景是游戏当中相对来说不变的一个游戏元素,由场景来承载层与精灵,一些层与精灵表现出的游戏内容存放在一个场景中,往往场景切换对应着关卡切换。其重要的作用就是进行流程控制。
在CCDirector中有关CCScene的函数有如下几种,截取类中一部分代码如下:
class CC_DLL CCDirector : public CCObject, public TypeInfo { public: void runWithScene(CCScene *pScene); void pushScene(CCScene *pScene); void popScene(void); void replaceScene(CCScene *pScene); void end(void); void pause(void); void resume(void); }void runWithScene(CCScene *pScene):这个函数在启动游戏时候调用,运行场景pScene。AppDelegate.cpp中曾经调用过,其实这个函数是第一次执行主场景时候调用。如果已有正在运行的场景则不能调用该方法。
void pushScene(CCScene *pScene):将当前运行中的场景暂停并压入到代码执行场景栈中,再将传入的pScene设置为当前运行场景,只有存在正在运行的场景时才调用该方法。
void popScene(void);:释放当前场景,再从代码执行场景中弹出栈顶的场景,并将其设置为当前运行场景。如果栈为空,直接结束应用。和PushScene结对使用。
void replaceScene(CCScene *pScene);:直接使用传入的pScene替换当前场景来切换画面,当前场景被释放。这是切换场景时最常用的方法。
void pause(void);:暂停当前运行场景中的所有计时器和动作,场景仍然会显示在屏幕上。
void resume(void): 恢复当前运行场景的所有计时器和动作,场景仍然会显示在屏幕上。
void end(void):释放和终止执行场景,同时退出应用。
场景切换用到的代码如下:
CCScene* pScene = TestLayer::scene(); CCDirector::sharedDirector()->pushScene(pScene); CCDirector::sharedDirector()->popScene(); CCDirector::sharedDirector()->replaceScene(pScene);其中我们要知道的是CCScene* pScene = TestLayer::scene();返回的是一个场景指针,执行之后的代码便可进行场景入栈,出栈和场景替换了,当然不能在同一代码库中同时运行。
层(CCLayer):
层的继承关系:class CC_DLL CCLayer : public CCNode, public CCTouchDelegate, public CCAccelerometerDelegate, public CCKeypadDelegate。
由层的继承关系可以看出,层可以接受触摸、加速度计、键盘输入。
层包含显示在屏幕上的内容,多个层可以组成复杂的场景。
层可以包含任何Node作为子节点,包括CCSprites(精灵),,Labels(标签),甚至其他的Layer对象。
要向场景添加层,我们可以使用addChild方法。
addChild方法是继承自CCNode中,只要CCNode的子类,都存在addChild()方法,并且可以利用此方法把节点添加到另一个节点上去,在CCNode中定义如下:
class CC_DLL CCNode : public CCObject { public: virtual void addChild(CCNode * child); virtual void addChild(CCNode * child, int zOrder); virtual void addChild(CCNode* child, int zOrder, int tag); }其中,child参数就是节点。先添加的节点会被置于后添加的节点之下。可以使用不同的zOrder值指定先后顺序,zOrder值越大离屏幕越近,越不容易被覆盖。tag是节点的标识号码,如果为子节点设置了tag值,就可以在它的父节点中利用tag值就可以找到它了。
精灵(CCSprite):
精灵的继承关系:class CC_DLL CCSprite : public CCNodeRGBA, public CCTextureProtocol。
层和场景是其他游戏元素的容器,它们看起来是透明的。而精灵是具体可见的图形,可以执行动画等。
导演、场景、层、精灵暂且学习到这里,下面看看HelloWorld中的一些内容。此HelloWorld为精简化的,HelloWorld头文件内容如下:
#ifndef __HelloWorld_H__ #define __HelloWorld_H__ #include "cocos2d.h" USING_NS_CC; class HelloWorld:public CCLayer { public: static CCScene* scene(); CREATE_FUNC(HelloWorld); bool init(); }; #endif其中先来看看CREATE_FUNC:
#define CREATE_FUNC(__TYPE__) static __TYPE__* create() { __TYPE__ *pRet = new __TYPE__(); if (pRet && pRet->init()) { pRet->autorelease(); return pRet; } else { delete pRet; pRet = NULL; return NULL; } }由此可知CREATE_FUNC是一个宏,这个宏预处理后在程序中添加了一个静态create()函数。把HelloWorld中的进一步翻译后的到如下代码,当我们需要包含参数的create函数的时候,就必须自己仿照下面代码来写这个create函数了。
static HelloWorld* create() { HelloWorld *pRet = new HelloWorld(); if (pRet && pRet->init()) { pRet->autorelease(); return pRet; } else { delete pRet; pRet = NULL; return NULL; } }再来看看static CCScene* scene()函数与bool init()函数,这两个函数在HelloWorld.cpp中定义:
#include "HelloWorld.h" CCScene* HelloWorld::scene() { CCScene* scene = CCScene::create(); HelloWorld* layer = HelloWorld::create(); scene->addChild(layer); return scene; } bool HelloWorld::init() { if (!CCLayer::init())return false; return true; }
static CCScene* scene()中首先创建了一个场景对象保存了指向它的指针,利用CCScene中的create()函数;然后用HelloWorld中的create()函数创建了一个HelloWorld对象并保存了指向它的指针,此时用到的create()函数就是之前CREATE_FUNC替换后的函数;之后通过addChild把HelloWorld对象添加到场景对象scene中;最后返回场景对象指针scene。
bool HelloWorld::init()中是我们编写程序的主要地方,因为Cocos2d-x中有许多类不使用构造函数进行初始化,所以在HelloWorld::init()中首先调用了其父类的init()函数进行初始化,以防错误发生;如果父类init()产生错误,返回false,之后运行一系列我们自定义的函数后,返回true。
其实static CCScene* scene()在需要返回场景时候类中可以添加,不需要返回场景时候可以不添加,具体如何看实际情况。