5.1 Cocos2d-x核心概念
Cocos2d-x游戏引擎中将屏幕出现的所有内容进行了对象化的表示,导演、场景、图层、精灵是其基本概念,见下图:
图5-1 Cocos2d-x中的核心概念
导演(CCDirector) 控制整个游戏的界面显示哪个场景
场景(CCScene) 一个场景就是一个界面,定义了所显示的所有角色图层信息
图层(CCLayer) 一个场景中有多个可以交互的图层,每层显示不同的角色信息
精灵(CCSprite) 处理图片和动画的基本层
动作(CCAction) 每个角色都可以通过CCAction实现运动、旋转、渐变等
5.1.1 导演CCDirector
导演在整个应用中我们可以通过CCDirector::sharedCCDirector()方法得到单例对象,下面介绍以下几个方法可以管理场景CCScene:
1、void runWithScene(CCScene * scene)
将场景入栈,并激活。(只适用于整个App的第一个CCScene)
2、void replaceScene(CCScene * scene)
新场景入栈,并激活,旧场景出栈并释放。
3、void pushScene(CCScene * scene)
新场景入栈,并激活。
4、void popScene()
出栈,既然是栈肯定是后进先出。
5、void pause()
暂停场景渲染和活动
6、void resume()
恢复场景渲染和活动
还记得在HellowWord项目中的AppDelegate.cpp中的方法CCDirector的使用吗,
bool AppDelegate::applicationDidFinishLaunching() {
CCDirector* pDirector = CCDirector::sharedDirector();
CCEGLView* pEGLView = CCEGLView::sharedOpenGLView();
pDirector->setOpenGLView(pEGLView);
pDirector->setDisplayStats(true);
pDirector->setAnimationInterval(1.0 / 60);
CCScene *pScene = HelloWorld::scene();
pDirector->runWithScene(pScene);
return true;
}
5.1.2 场景CCScene
CCScene 继承CCNode,是整个CCNode数的根节点,CCScene作用是为了联系所有CCNode节点(一般为他的子类CCLayer,CCSprite.....),见下图:
图5-2 CCNode和CCScene
当清楚知到道每个屏幕要显示的内容,并分别定义在不同的CCNod,然后统一填加在CCScene中,执行 CCDirector->runWithScene(xxx)就可以显示在屏幕了,在游戏中通过定义游戏菜单界面的场景、游戏界面的场景、游戏帮助的界面场景、游戏设置的界面场景,然后通过CCDirector在适当时候来切换场景,就可以实现游戏中的界面跳转。
5.1.3 CCTransitionScene场景切换
当场景在切换过程可以通过带有动画功能的场景CCTransitionScene的子类来包装,我们要切换的场景就可以显示动画切换的效果,
CCTransitionScene继承自CCScene,而所有场景过渡效果的类都继承自CCTransitionScene。CCTransitionScene的几个基本方法如下。
1、finish
该方法在过渡效果结束时调用。
2、hideOutShowIn
部分过渡效果会使用该方法来隐藏更外面的场景。
3、initWithDuration(float t, CCScene *s)
该方法初始化一个场景过渡效果,并指定过渡时间和即将过渡的场景。
虽然过渡效果的名称和需要的参数数量很多,但是添加场景间的过渡效果只需要增加一行代码而已。
以一个最简单的淡入淡出过渡效果为例,场景在1秒内过渡到白色。
// 初始化一个过渡场景
CCTransitionFade* transitionScene =
CCTransitionFade::transitionWithDuration(1,gamescene);
// 使用过渡场景对象替代HelloWorld场景
CCDirector::sharedDirector()->replaceScene(transitionScene);
关于在Cocos2d-x中所能支持的动画效果读者可以查看TestCpp的源码中,Classes目录下,TransitionsTest目录下的TransitionsTest.h和TransitionsTest.cpp。该案例对应于在testCpp运行后的第二个菜单项。
5.1.4 绘图节点CCNode和图层CCLayer
通过图5-2了解到,CCScene和CCLayer都继承自CCNode,CCNode是Cocos2d-x绘图的基本单位,CCScene、CCLayer、CCMenu、CCSprite都继承自CCNode,所有节点都具有以下特征:
1、每个节点都可以通过addChild方法包含子节点、通过removeChild删除节点。
2、每个节点的子节点都可以设置标记,通过getChildByTag来获取节点。
3、每个节点都可以执行计划任务,在Cocos2d-x的系统循环中处理这些任务。
4、每个节点都可以通过runAction执行瞬时动作和延时动作。
5、每个节点添加到场景中,当所在场景为激活场景时,这个节点的绘图方法会被自动调用完成自我绘制。
下面看一下CCNode类中的属性和方法定义来了解CCNode的功能:
class CC_DLL CCNode : public CCObject
{
protected://属性列表
float m_fRotationX; ///x轴旋转角度
float m_fRotationY; ///y轴旋转角度
float m_fScaleX; ///x轴缩放比例
float m_fScaleY; ///y轴缩放比例
float m_fVertexZ; ///z轴设置
CCPoint m_obPosition; ///节点坐标
float m_fSkewX; ///x轴扭曲角度
float m_fSkewY; ///y轴扭曲角度
CCPoint m_obAnchorPointInPoints; ///锚点坐标
CCPoint m_obAnchorPoint; ///缺省锚点坐标
CCSize m_obContentSize; ///节点大小
CCAffineTransform m_sAdditionalTransform; ///变换信息
CCAffineTransform m_sTransform; ///变换信息
CCAffineTransform m_sInverse; ///变换信息
CCCamera *m_pCamera; ///摄像头对象
CCGridBase *m_pGrid; ///网格对象
int m_nZOrder; ///Z轴设置,影响图层顺序
CCArray *m_pChildren; ///所有子节点
CCNode *m_pParent; ///所在父节点
int m_nTag; ///当前节点标识
void *m_pUserData; ///用户调用指针
CCObject *m_pUserObject; ///用户调用对象
CCGLProgram *m_pShaderProgram; ///OpenGL shader
ccGLServerState m_eGLServerState; ///OpenGL servier side state
unsigned int m_uOrderOfArrival; ///
CCScheduler *m_pScheduler; ///调度类完成计划任务调度
CCActionManager *m_pActionManager; ///动作管理器
bool m_bRunning; ///标识当前节点是否在运行
bool m_bTransformDirty; ///< transform dirty flag
bool m_bInverseDirty; ///< transform dirty flag
bool m_bAdditionalTransformDirty; ///
bool m_bVisible; ///标识当前节点是否可见
bool m_bIgnoreAnchorPointForPosition; ///在CCLayer和CCScene中使用
bool m_bReorderChildDirty; ///记录子节点的 dirty flag
int m_nScriptHandler; ///在Javascript 和 Lua 编程使用
int m_nUpdateScriptHandler; ///在Javascript 和 Lua 编程使用 ccScriptType m_eScriptType; ///标识Javascript或Lua 脚本
CCComponentContainer *m_pComponentContainer; ///组件字典
.......
}
通过以上属性列表我们了解到CCNode具有以下属性:
-坐标 position
-缩放 scale (x, y)
-旋转rotation (in degrees, clockwise)
-摄像机CCCamera (an interface to gluLookAt )
-网格 CCGridBase (to do mesh transformations)
- 锚点anchor point
- 尺寸size
- 可见性visible
- 层次设定z-order
- openGL z轴位置
这些属性的缺省值设定如下:
- 旋转rotation: 0
- 坐标position: (x=0,y=0)
- 缩放scale: (x=1,y=1)
- 尺寸contentSize: (x=0,y=0)
- 锚点anchorPoint: (x=0,y=0)
- 一个新的CCNode对象没有任何纹理
在CCNode对象创建完成,可以实现以下操作:
1、变换坐标 translated (position)
2、变化角度rotated (rotation)
3、变换尺寸 scaled (scale)
4、变换显示状态 (camera)
下面看一下在CCNode类定义中所包含的的方法:
class CC_DLL CCNode : public CCObject
{
public:
CCNode(void);
virtual ~CCNode(void);
virtual bool init(); //初始化节点
static CCNode * create(void); //创建节点对象
const char* description(void);
virtual void setZOrder(int zOrder); //设定节点Z轴排序
virtual void _setZOrder(int z);
virtual int getZOrder();
virtual void setVertexZ(float vertexZ);//设置Z坐标
virtual float getVertexZ();
virtual void setScaleX(float fScaleX); //设定X轴缩放系数(0-1)
virtual float getScaleX();
virtual void setScaleY(float fScaleY); //设定Y轴缩放系数
virtual float getScaleY();
virtual void setScale(float scale); //设定缩放系数
virtual float getScale();
virtual void setScale(float fScaleX,float fScaleY);
virtual void setPosition(const CCPoint &position); //设定坐标
virtual const CCPoint& getPosition(); //获取坐标
virtual void setPosition(float x, float y);
virtual void setPositionX(float x);
virtual float getPositionX(void);
virtual void setPositionY(float y);
virtual float getPositionY(void);
virtual void setSkewX(float fSkewX); //设定x轴扭曲系数
virtual float getSkewX();
virtual void setSkewY(float fSkewY); //设定y轴扭曲系数
virtual float getSkewY();
virtual void setAnchorPoint(const CCPoint& anchorPoint); //设定锚点
virtual const CCPoint& getAnchorPoint(); //获取锚点
virtual const CCPoint& getAnchorPointInPoints();//获取锚点绝对坐标
virtual void setContentSize(const CCSize& contentSize); //设定节点尺寸
virtual const CCSize& getContentSize() const; //获取节点尺寸
virtual void setVisible(bool visible); //设置节点是否可见
virtual bool isVisible(); //返回节点是否可见
virtual void setRotation(float fRotation);//设置旋转角度
virtual float getRotation(); //获取旋转角度
virtual void setRotationX(float fRotaionX);//设置X轴旋转角度
virtual float getRotationX();
virtual void setRotationY(float fRotationY);//设置Y轴旋转角度
virtual float getRotationY();
//当节点的ZOrder相同时,使用该值做以区分
virtual void setOrderOfArrival(unsigned int uOrderOfArrival);
virtual unsigned int getOrderOfArrival();
//设置GLServer状态
virtual void setGLServerState(ccGLServerState glServerState);
//获取GLServer状态
virtual ccGLServerState getGLServerState();
//设置是否忽略锚点位置
virtual void ignoreAnchorPointForPosition(bool ignore);
//获取是佛忽略锚点位置
virtual bool isIgnoreAnchorPointForPosition();
virtual void addChild(CCNode * child); //添加子节点
virtual void addChild(CCNode * child, int zOrder);
virtual void addChild(CCNode* child, int zOrder, int tag);
virtual CCArray* getChildren(); //获取所有子节点
virtual void setParent(CCNode* parent); //设置父节点
virtual CCNode* getParent(); //获取父节点
virtual void removeFromParent(); //从父节点中移除
//从父节点移除、参数决定是否清楚本节点
virtual void removeFromParentAndCleanup(bool cleanup);
virtual void removeChild(CCNode* child); //移除某个子节点
virtual void removeChild(CCNode* child, bool cleanup);
virtual void removeChildByTag(int tag); //移除某个tag对应的子节点
virtual void removeChildByTag(int tag, bool cleanup);
virtual void removeAllChildren(); //移除所有子节点
virtual void removeAllChildrenWithCleanup(bool cleanup);
virtual void reorderChild(CCNode * child, int zOrder);//重排某个子节点
virtual void sortAllChildren(); //对所有子节点重拍
virtual CCGridBase* getGrid(); //获取网格对象
virtual void setGrid(CCGridBase *pGrid); //设置网格对象
virtual int getTag() const; //获取当前节点的Tag
virtual void setTag(int nTag); //设置当前节点的Tag
virtual void* getUserData(); //获取用户数据
virtual void setUserData(void *pUserData); //设置用户数据
virtual CCObject* getUserObject(); //获取用户数据对象
virtual void setUserObject(CCObject *pUserObject); //设置用户数据对象
virtual CCGLProgram* getShaderProgram(); //获取Shader程序
virtual void setShaderProgram(CCGLProgram *pShaderProgram);
virtual CCCamera* getCamera(); //获取摄像机对象
virtual bool isRunning(); //获取当前Node是否在运行
virtual void registerScriptHandler(int handler); //注册脚本Handler
virtual void unregisterScriptHandler(void);
inline int getScriptHandler() { return m_nScriptHandler; };
//执行Lua脚本计划任务
void scheduleUpdateWithPriorityLua(int nHandler, int priority);
virtual void onEnter() ; //进入节点对象
virtual void onEnterTransitionDidFinish(); //进入节点动画完成
virtual void onExit(); //离开节点对象
virtual void onExitTransitionDidStart(); //离开节点动画完成
virtual void cleanup(void); //停止所有动画和调度
virtual void draw(void); //绘图,渲染当前节点函数
virtual void visit(void); //递归遍历当前节点树
CCRect boundingBox(void); //返回节点区域
//设置当前节点的ActionManager
virtual void setActionManager(CCActionManager* actionManager);
virtual CCActionManager* getActionManager();
CCAction* runAction(CCAction* action);//让当前节点执行动作
void stopAllActions(void); //停止所有动作
void stopAction(CCAction* action);//停止某一个动作
void stopActionByTag(int tag); //停止标记为tag的动作
CCAction* getActionByTag(int tag); //获取标记为tag的动作
unsigned int numberOfRunningActions(void);//返回绑定该节点的动作数
virtual void setScheduler(CCScheduler* scheduler); //设置任务管理器
virtual CCScheduler* getScheduler(); //获取任务管理器
bool isScheduled(SEL_SCHEDULE selector); //是否执行了某个任务
void scheduleUpdate(void); //执行定时调用任务update
//按照指定优先级运行update任务
void scheduleUpdateWithPriority(int priority);
void unscheduleUpdate(void); //停止update任务
//指定按照的时间间隔和等待时间,指定次数执行某个任务
void schedule(SEL_SCHEDULE selector, float interval,
unsigned int repeat, float delay);
//指定按照的时间间隔执行某个任务
void schedule(SEL_SCHEDULE selector, float interval);
//等待一定时间以后执行某个任务
void scheduleOnce(SEL_SCHEDULE selector, float delay);
void schedule(SEL_SCHEDULE selector);//执行任务
void unschedule(SEL_SCHEDULE selector);//取消执行某个任务
void unscheduleAllSelectors(void);//取消执行所有任务
void resumeSchedulerAndActions(void);//恢复节点任务和动作
void pauseSchedulerAndActions(void); //暂停节点任务和动作
virtual void update(float delta); //定时任务回调方法
void transform(void); //矩阵坐标变换
void transformAncestors(void); //预设变换矩阵
virtual void updateTransform(void); //变换方法
//仿射变换从当前节点到父节点
virtual CCAffineTransform nodeToParentTransform(void);
//仿射变换从父节点到当前节点
virtual CCAffineTransform parentToNodeTransform(void);
//仿射变换当前节点到世界节点
virtual CCAffineTransform nodeToWorldTransform(void);
//仿射变换世界节点到当前节点
virtual CCAffineTransform worldToNodeTransform(void);
//转化为节点空间坐标,相对于节点左下角,与锚点无关
CCPoint convertToNodeSpace(const CCPoint& worldPoint);
//转化为世界空间坐标,相对于节点左下角,与锚点无关
CCPoint convertToWorldSpace(const CCPoint& nodePoint);
//转化为节点空间坐标,相对于节点左下角,相对于锚点
CCPoint convertToNodeSpaceAR(const CCPoint& worldPoint);
//转化为世界空间坐标,相对于节点左下角,相对于锚点
CCPoint convertToWorldSpaceAR(const CCPoint& nodePoint);
//从触屏对象转化为节点空间坐标
CCPoint convertTouchToNodeSpace(CCTouch * touch);
//从触屏对象转化为节点空间坐标,输入输出都相对于锚点
CCPoint convertTouchToNodeSpaceAR(CCTouch * touch);
//设置变换
void setAdditionalTransform(const CCAffineTransform&
additionalTransform);
//获取组件
CCComponent* getComponent(const char *pName) const;
//添加组件
virtual bool addComponent(CCComponent *pComponent);
//移除组件
virtual bool removeComponent(const char *pName);
//移除所有组件
virtual void removeAllComponents();
private:
void childrenAlloc(void);
void insertChild(CCNode* child, int z);
void detachChild(CCNode *child, bool doCleanup);
CCPoint convertToWindowSpace(const CCPoint& nodePoint);
......
}
以上方法为CCNode中的提供,在public块中的方法主要有以下几个部分:
1、针对节点显示的属性信息读写
2、针对节点变换的属性信息读写
3、针对子节点的管理相关方法
4、针对节点数据绑定的相关方法
5、针对节点生命周期的相关方法
6、针对节点处理动作CCAction的相关方法
7、针对节点定时任务的相关方法
8、针对节点坐标变换的相关方法
在CCNode中的节点都有自己的坐标系,称为节点坐标系,当节点坐标系变换后该节点的所有子节点都会随之变换。
节点在添加到场景时会参考节点的锚点,锚点缺省定义为该节点的中心,在贴图时候会以锚点为中心,改变锚点并没有改变节点的位置,只是改变了贴图的位置。
Cocos2d-x中的世界坐标系和openGL坐标系一致,左下角为0,0点,x轴向右,y轴向上。
CCScene类是CCNode类的子类。和CCNode相比,它只是多了自己的锚点,在屏幕的中间位置。
CCLayer也是CCNode的子类,相比CCNode增加了用户的交互功能。
5.2 在CCLayer处理图层交互
CCLayer继承自CCNode,在CCLayer中可以实现单点触摸、多点触摸和重力感应回调3种不同形式的交互。
5.3.1 在CCLayer处理用户触摸
在用户自定义的图层处理用户触摸事件步骤如下:
1、编写图层类继承CCLayer
2、在初始化阶段(init方法)将此层的属性设置为接收触摸消息:
setTouchEnabled(true);//开启屏幕触摸
2、重载CCLayer函数virtual void registerWithTouchDispatcher(void);
因为默认的方式为Standard Touch Delegate,需要在函数中添加以下语句:
CCDirector::sharedDirector()->getTouchDispatcher()->
addTargetedDelegate(this,0, true);
其中,第二个参数是代理的优先级,如果数值越小优先级越高,第三个参数是true表示当前图层处理消息后不再进行消息的后续传递,如果需要继续传递消息需要设置为false。
3、重载触摸响应函数, 接收触摸消息需要重载以下函数
ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent); //按下
ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent); //滑动
ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent); //抬起
4、在触摸相应函数中通过pTouch得到用户的触摸点:
CCPoint point=pTouch->getLocation();
5.3.2 实现多点触摸
在用户自定义的图层处理用户多点触摸步骤与单点触摸事件处理相似,只是回调的消息相应函数不同:
virtual void ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent);
virtual void ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent);
virtual void ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent);
virtual void ccTouchesCancelled(CCSet*pTouches,CCEvent *pEvent);
在以上函数中通过pTouches来获取用户触摸所有点信息。
5.3.3 实现重力感应传感器交互
游戏中除了通过用户触摸手势来处理用户交互外,还有很多游戏通过手机中的重力感应器交互,其核心是系统层将重力感应的消息也传递给了Cocos2d-x引擎层,Cocos2d-x引擎层通过内部的消息传递机制传递给CCLayer,其接受步骤为:
1、编写一个类继承CCLayer
2、在这个类的init方法实现重力加速器设置,代码如下:
setAccelerometerEnabled(true);
3、在这个类中覆盖重力加速器的回调函数
void CCLayer::didAccelerate(CCAcceleration* pAccelerationValue)
4、在回调函数中通过pAccelerationValue的到重力传感器的数值
以下代码案例实现了通过重力感应器来控制一个球体的位置变化:
void AccelerometerTest::didAccelerate(CCAcceleration* pAccelerationValue)
{ CCDirector* pDir = CCDirector::sharedDirector();
CCSize winSize = pDir->getWinSize();
CCSize ballSize = m_pBall->getContentSize();
CCPoint ptNow = m_pBall->getPosition();
CCPoint ptTemp = pDir->convertToUI(ptNow);
ptTemp.x += pAccelerationValue->x * 9.81f;
ptTemp.y -= pAccelerationValue->y * 9.81f;
CCPoint ptNext = pDir->convertToGL(ptTemp);
FIX_POS(ptNext.x, (ballSize.width / 2.0), (winSize.width - ballSize.width / 2.0));
FIX_POS(ptNext.y, (ballSize.height / 2.0), (winSize.height - ballSize.height / 2.0));
m_pBall->setPosition(ptNext);
}
5.3 在CCLayer实现《贪食蛇》游戏实例
前面介绍了CCNode、CCScene、CCLayer,本节通过一个贪食蛇的游戏实例来实现如下功能:
1、通过CCScene实现游戏主菜单窗口、帮助窗口、游戏窗口之间不同场景的跳转。
2、通过CCLayer来定义不同场景显示的内容
3、通过CCMenu实现从帮助窗口跳转到游戏菜单窗口
4、通过CCLayer来实现用户的触摸,根据用户触摸点来完成蛇的移动
5、通过在CCLayer子类实现draw方法来自定义图层显示内容
6、通过在CCLayer中定义计划任务,实现贪食蛇的不断移动
该程序的运行效果如下:
图5-3 贪食蛇游戏主菜单界面
图5-4 贪食蛇游戏的游戏主界面
图5-5 贪食蛇游戏的帮助画面
由于该项目时在HelloWorld项目基础上增加,因此,读者可以不用在HelloWorld项目增加任何新的文件,只需要修改HelloWorldScene.h和HelloWorldScene.cpp即可,下面是HelloWorldScene.h源代码:
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include ""cocos2d.h""
typedef enum {
UP=1,
DOWN,
LEFT,
RIGHT
}DIR_DEF;
class SnakeNode :public cocos2d::CCObject{
public:
int row;
int col;
int dir;
};
//游戏欢迎画面
class HelloWorld : public cocos2d::CCLayer
{
public:
virtual bool init();
virtual void onEnter();
virtual void onExit();
static cocos2d::CCScene* scene();//获取欢迎画面的Scene
void menuCloseCallback(CCObject* pSender);
CREATE_FUNC(HelloWorld);
};
//游戏帮助画面
class GameHelp :public cocos2d::CCLayer{
public :
virtual bool init();
virtual void onEnter();
virtual void onExit();
static cocos2d::CCScene * scene();//获取帮助画面
CREATE_FUNC(GameHelp);
void menuBackToMain(CCObject *pSender);//返回主菜单
};
//游戏画面
class GameLayer :public cocos2d::CCLayer{
protected:
SnakeNode *sHead; //贪食蛇px py
SnakeNode *sFood; //食物
cocos2d::CCArray * allBody;//蛇的身体
cocos2d::CCTexture2D * chead;
public :
virtual bool init();
virtual void onEnter();
virtual void onExit();
virtual void draw();//实现当前Layer的定义
virtual void registerWithTouchDispatcher(void);
virtual bool ccTouchBegan(cocos2d::CCTouch *pTouch,
cocos2d::CCEvent *pEvent);
virtual void ccTouchMoved(cocos2d::CCTouch *pTouch,
cocos2d::CCEvent *pEvent);
virtual void ccTouchEnded(cocos2d::CCTouch *pTouch,
cocos2d::CCEvent *pEvent);
void logic01(float t);
static cocos2d::CCScene * scene();//获取游戏画面
CREATE_FUNC(GameLayer);
void menuBackToMain(CCObject *pSender);//返回主菜单
};
#endif // __HELLOWORLD_SCENE_H__
代码说明:
在HelloWorldScene.h中定义了游戏的枚举类型DIR_DEF,分别标识贪食蛇的移动方向:
typedef enum {
UP=1,
DOWN,
LEFT,
RIGHT
}DIR_DEF;
每个节点都有自己的移动方向,因此,在节点类的定义中包含了行、列和方向3个成员:
class SnakeNode :public cocos2d::CCObject{
public:
int row;
int col;
int dir;
};
然后分别定义了主菜单图层类、游戏界面图层类、帮助图层类,在游戏界面图层类中处理了用户的触摸事件,并定义了cocos2d::CCArray集合类来保存所有的节点,下面看一下HelloWorldScene.cpp的代码实现:
#include ""HelloWorldScene.h""
using namespace cocos2d;
CCScene* HelloWorld::scene()
{ CCScene * scene = NULL;
do
{
scene = CCScene::create();
CC_BREAK_IF(! scene);
HelloWorld *layer = HelloWorld::create();
CC_BREAK_IF(! layer);
scene->addChild(layer);
} while (0);
return scene;
}
bool HelloWorld::init()
{ bool bRet = false;
do
{
CC_BREAK_IF(! CCLayer::init());
///添加项菜单进入游戏游戏帮助退出游戏
CCLabelTTF * labelstart=CCLabelTTF::create(""startGame"",""宋体"",24);
CCLabelTTF * labelhelp=CCLabelTTF::create(""GameHelp"",""宋体"",24);
CCLabelTTF * labelexit=CCLabelTTF::create(""exitGame"",""宋体"",24);
//定义菜单条目
CCMenuItemLabel *mi01=CCMenuItemLabel::create(labelstart,this,
menu_selector(HelloWorld::menuCloseCallback));
mi01->setTag(1);
mi01->setPosition(ccp(100,200));
CCMenuItemLabel *mi02=CCMenuItemLabel::create(labelhelp,this,
menu_selector(HelloWorld::menuCloseCallback));
mi02->setTag(2);
mi02->setPosition(ccp(100,150));
CCMenuItemLabel *mi03=CCMenuItemLabel::create(labelexit,this,
menu_selector(HelloWorld::menuCloseCallback));
mi03->setTag(3);
mi03->setPosition(ccp(100,50));
CCMenuItemImage *pCloseItem = CCMenuItemImage::create(
""CloseNormal.png"",
""CloseSelected.png"",
this,
menu_selector(HelloWorld::menuCloseCallback));
CC_BREAK_IF(! pCloseItem);
pCloseItem->setTag(4);
pCloseItem->setPosition(ccp(CCDirector::sharedDirector()->
getWinSize().width - 20, 20));
CCMenu* pMenu = CCMenu::create(mi01,mi02,mi03,
pCloseItem, NULL);
pMenu->setPosition(CCPointZero);
CC_BREAK_IF(! pMenu);
this->addChild(pMenu, 1);
CCLabelTTF* pLabel = CCLabelTTF::create(""Hello World"", ""Arial"", 24);
CC_BREAK_IF(! pLabel);
CCSize size = CCDirector::sharedDirector()->getWinSize();
pLabel->setPosition(ccp(size.width / 2, size.height - 50));
this->addChild(pLabel, 1);
CCSprite* pSprite = CCSprite::create(""HelloWorld.png"");
CC_BREAK_IF(! pSprite);
pSprite->setPosition(ccp(size.width/2, size.height/2));
this->addChild(pSprite, 0);
bRet = true;
} while (0);
return bRet;
}
void HelloWorld::menuCloseCallback(CCObject* pSender)
{ switch(((CCNode*)pSender)->getTag())
{case 1:// 进入游戏
CCLog(""go to game"");
CCDirector::sharedDirector()->replaceScene(GameLayer::scene());
break;
case 2://进入帮助
CCLog(""go to help"");
CCDirector::sharedDirector()->replaceScene(GameHelp::scene());
break;
case 3:
case 4:
CCDirector::sharedDirector()->end();
};
}
bool GameHelp::init(){
if(!CCLayer::init())
{return false;}
CCLabelTTF * labhelp=CCLabelTTF::create(
""please click screen to game"",""宋体"",15);
labhelp->setPosition(ccp(0,280));
this->addChild(labhelp);
CCLabelTTF * labback=CCLabelTTF::create(""MainMenu"",""宋体"",15);
CCMenuItemLabel * miback=CCMenuItemLabel::create(labback,this,
menu_selector(GameHelp::menuBackToMain));
miback->setPosition(ccp(0,0));
CCMenu * pm=CCMenu::create(miback,NULL);
this->addChild(pm);
return true;
}
cocos2d::CCScene * GameHelp::scene(){
CCScene * scene=CCScene::create();
CCLayer * hl=GameHelp::create();
scene->addChild(hl);
return scene;
}//获取帮助画面
void GameHelp::menuBackToMain(CCObject *pSender)//返回主菜单
{
CCDirector::sharedDirector()->replaceScene(HelloWorld::scene());
}
bool GameLayer::init(){
if(!CCLayer::init())
{return false;}
CCLabelTTF * labhelp=CCLabelTTF::create(""this is game"",""宋体"",15);
labhelp->setPosition(ccp(0,340));
this->addChild(labhelp);
CCLabelTTF * labback=CCLabelTTF::create(""MainMenu"",""宋体"",15);
CCMenuItemLabel * miback=CCMenuItemLabel::create(labback,this,
menu_selector(GameLayer::menuBackToMain));
miback->setPosition(ccp(360,200));
//chead=::CCTextureCache::sharedTextureCache()->addImage(""head.png"");
///接受触摸事件
this->setTouchEnabled(true);
//this->setAccelerometerEnabled(true);
//this->setKeypadEnabled(true);
//初始化蛇头坐标和食物的坐标
sHead=new SnakeNode();
sHead->row=rand()%10;
sHead->col=rand()%10;
//初始化食物的坐标
sFood=new SnakeNode();
sFood->row=rand()%10;
sFood->col=rand()%10;
//初始化蛇的身体
this->allBody=cocos2d::CCArray::create();
this->allBody->retain();
//执行定时任务
this->schedule(schedule_selector(GameLayer::logic01),0.5);
return true;
}
cocos2d::CCScene * GameLayer::scene(){
CCScene * scene=CCScene::create();
CCLayer * hl=GameLayer::create();
scene->addChild(hl);
return scene;
}//获取帮助画面
void GameLayer::menuBackToMain(CCObject *pSender)//返回主菜单
{
CCDirector::sharedDirector()->replaceScene(
HelloWorld::scene());
}
void HelloWorld::onEnter(){
CCLayer::onEnter();
CCLog(""HelloWorld onEnter"");
}
void HelloWorld::onExit(){
CCLog(""HelloWorld onExit"");
}
void GameHelp::onEnter(){
CCLayer::onEnter();
CCLog(""GameHelp onEnter"");
}
void GameHelp::onExit(){
CCLog(""GameHelp onExit"");
}
void GameLayer::onEnter(){
CCLayer::onEnter();
CCLog(""GameLayer onEnter"");
}
void GameLayer::onExit(){
CCLog(""GameLayer onExit"");
}
void GameLayer::draw(){
///绘制形状
::glLineWidth(2);//设定画线的宽度
for(int i=0;i<11;i++)
{
::ccDrawLine(ccp(0,i*32),ccp(320,i*32));//绘制条横线
::ccDrawLine(ccp(i*32,0),ccp(i*32,320));//绘制条竖线
}
// RGBA
//::ccDrawColor4B(ccc4(255,0,0,255));//设定画线的颜色
//绘制蛇头
::ccDrawSolidRect(ccp(sHead->col*32+2,sHead->row*32+2),
ccp(sHead->col*32+32,sHead->row*32+32),
ccc4FFromccc3B(ccc3(255,0,0)));
//绘制食物
::ccDrawSolidRect(ccp(sFood->col*32+2,sFood->row*32+2),
ccp(sFood->col*32+32,sFood->row*32+32),
ccc4FFromccc3B(ccc3(0,0,255)));
//绘制身体
for(int i=0;i<allBody->count();i++)
{SnakeNode * node=(SnakeNode *)allBody->objectAtIndex(i);
::ccDrawSolidRect(ccp(node->col*32+2,node->row*32+2),
ccp(node->col*32+32,node->row*32+32),
ccc4FFromccc3B(ccc3(0,0,255)));
}
CCRect r(340,0,57,57);
chead->drawInRect(r);
CCLayer::draw();
}
void GameLayer::registerWithTouchDispatcher()
{
CCDirector::sharedDirector()->getTouchDispatcher()->
addTargetedDelegate(this,1,false);
}
bool GameLayer::ccTouchBegan(cocos2d::CCTouch *pTouch,
cocos2d::CCEvent *pEvent){
//CCLayer::ccTouchBegan(pTouch,pEvent);
CCPoint p=pTouch->getLocation();
int nowrow;//当前用户触摸的行
nowrow=((int)p.y)/32;
int nowcol;////当前用户触摸的列
nowcol=((int)p.x)/32;
if(::abs(nowrow-sHead->row)>abs(nowcol-sHead->col)) //上下移动
{
if(nowrow>sHead->row)
{
sHead->dir=DIR_DEF::UP;
}else
{
sHead->dir=DIR_DEF::DOWN;
}
}else////左右移动
{
if(nowcol>sHead->col)
{
sHead->dir=DIR_DEF::RIGHT;
}else
{ sHead->dir=DIR_DEF::LEFT;
}
}
CCLog(""rand %d"",rand());
CCLog(""you touchbegan%f,%f"",p.x,p.y);
return true;
}
void GameLayer::ccTouchMoved(cocos2d::CCTouch *pTouch,
cocos2d::CCEvent *pEvent){
//CCPoint p=pTouch->getLocation();
//CCLog(""you ccTouchMoved%f,%f"",p.x,p.y);
}
void GameLayer::ccTouchEnded(cocos2d::CCTouch *pTouch,
cocos2d::CCEvent *pEvent){
//CCPoint p=pTouch->getLocation();
//CCLog(""you ccTouchEnded%f,%f"",p.x,p.y);
}
void GameLayer::logic01(float t)
{
//移动蛇的身体
for(int i=allBody->count()-1;i>=0;i--)
{ SnakeNode * sn=(SnakeNode *)allBody->objectAtIndex(i);
if(i>0)
{ SnakeNode * snpre=(SnakeNode *)allBody->objectAtIndex(i-1);
sn->dir=snpre->dir;
sn->row=snpre->row;
sn->col=snpre->col;}
else if(i==0)
{//移动第一个节点
sn->dir=sHead->dir;
sn->row=sHead->row;
sn->col=sHead->col;
}
}
//移动蛇头
switch(sHead->dir)
{case DIR_DEF::UP:
sHead->row++;
if(sHead->row>=10){sHead->row=0;}
break;
case DIR_DEF::DOWN:
sHead->row--;
if(sHead->row<0){sHead->row=9;}
break;
case DIR_DEF::LEFT:
sHead->col--;
if(sHead->col<0){sHead->col=9;}
break;
case DIR_DEF::RIGHT:
sHead->col++;
if(sHead->col>=10){sHead->col=0;}
break;
};
//碰撞检测
if(sHead->row==sFood->row&&sHead->col==sFood->col)
{ //食物消失
sFood->row=rand()%10;
sFood->col=rand()%10;
//添加身体到集合
SnakeNode * sn=new SnakeNode();
SnakeNode *lastNode=NULL;
if(allBody->count()>0)
lastNode=(SnakeNode *)allBody->lastObject();
else
lastNode=sHead;
switch(lastNode->dir)
{case DIR_DEF::UP:
sn->row=lastNode->row-1;
sn->col=lastNode->col;
break;
case DIR_DEF::DOWN:
sn->row=lastNode->row+1;
sn->col=lastNode->col;
break;
case DIR_DEF::LEFT:
sn->row=lastNode->row;
sn->col=lastNode->col+1;
break;
case DIR_DEF::RIGHT:
sn->row=lastNode->row;
sn->col=lastNode->col-1;
break;
}
this->allBody->addObject(sn);
}
}
代码说明:
在HelloWorldScene.cpp的代码实现中,对原来HelloWorld的图层增加了3个菜单项目分别为:
startGame 目标进入游戏场景
GameHelp 目标进入游戏帮助场景
exitGame 退出游戏
这几个菜单项目都将事件委托给了HelloWorld::menuCloseCallback通过对HelloWorld菜单回调的改造,实现不同场景的跳转:
void HelloWorld::menuCloseCallback(CCObject* pSender)
{ switch(((CCNode*)pSender)->getTag())
{case 1:// 进入游戏
CCLog(""go to game"");
CCDirector::sharedDirector()->replaceScene(GameLayer::scene());
break;
case 2://进入帮助
CCLog(""go to help"");
CCDirector::sharedDirector()->replaceScene(GameHelp::scene());
break;
case 3:
case 4:
CCDirector::sharedDirector()->end();
};
}
进入帮助场景之后通过菜单实现跳转回主菜单界面场景,代码如下:
void GameHelp::menuBackToMain(CCObject *pSender)//返回主菜单
{
CCDirector::sharedDirector()->replaceScene(HelloWorld::scene());
}
进入游戏场景时会自动执行场景的init方法,在init方法中通过schedule定义计划任务,代码如下:
this->schedule(schedule_selector(GameLayer::logic01),0.5);
这时,Cocos2d-x引擎系统会每隔0.5秒执行一次GameLayer::logic01函数,在该函数中实现了贪食蛇的游戏逻辑,包括移动蛇的身体、碰撞检测等。
并且在进入游戏场景是会自动执行draw函数来完成场景的绘制,在draw函数中通过图形绘制的相关函数来完成了贪食蛇游戏的网格地图绘制和蛇的身体块绘制(具体在Cocos2d-x中提供的绘图方法可以参考testCpp中DrawPrimitivesTest.cpp)。
同时在游戏图层中处理了用户的触摸事件:
bool GameLayer::ccTouchBegan(cocos2d::CCTouch *pTouch,
cocos2d::CCEvent *pEvent)
当用户触摸屏幕以后会计算出蛇头的方向,并随之改变蛇头的方向,进而在logc01中移动蛇头。
该Demo代码并不多,但综合使用了CCDirector、CCNode、CCLayer、CCScene的相关方法,读者不妨试试开发一个类似的程序。
5.4 菜单CCMenu、文字CCLabelTTF
CCMenu可以快速实现用户的交互功能,在Cocos2d-x中提供了标签菜单项、精灵菜单项、触发器菜单项,通过不同的菜单项实现菜单的不同效果。
在游戏项目中,帮助信息、关于信息、角色对话等都需要显示文字到场景,在文字显示时也有系统字体文字显示方式、图片文字显示方式、精灵图片渲染方式等,这些文字都实现了CCLabelProtocol协议。
5.4.1标签菜单项
是使用字体定义的菜单项,包括CCMenuItemFont和CCMenuItemAtlasFont两种定义菜单项字体的方式,其中CCMenuItemFont通过设置字体名称来实现,代码如下:
CCMenuItemFont *item4 = CCMenuItemFont::create(""Menu01"",
this, menu_selector(MenuLayerMainMenu::menuCallbackEnable) );
item4->setFontSizeObj(20); //设置字号
item4->setFontName(""Marker Felt"");//设置字体
CCMenuItemAtlasFont是通过字体的PNG文件来设置文字内容和显示方式,代码如下:
CCLabelAtlas* labelAtlas = CCLabelAtlas::create(""0123456789"",
""fonts/labelatlas.png"", 16, 24, '.');
CCMenuItemLabel* item3 = CCMenuItemLabel::create(labelAtlas,
this, menu_selector(MenuLayerMainMenu::menuCallbackDisabled) );
item3->setDisabledColor( ccc3(32,32,64) );
CCLabelBMFont* label = CCLabelBMFont::create(""configuration"",
""fonts/bitmapFontTest3.fnt"");
CCMenuItemLabel* item5 = CCMenuItemLabel::create(label,
this, menu_selector(MenuLayerMainMenu::menuCallbackConfig));
item5->setScale( 0.8f );
5.4.2 精灵菜单项
CCMenuItemSprite可以封装图片作为菜单不同状态显示的内容,代码如下:
CCSprite* spriteNormal = CCSprite::create(s_MenuItem,
CCRectMake(0,23*2,115,23));
CCSprite* spriteSelected = CCSprite::create(s_MenuItem,
CCRectMake(0,23*1,115,23));
CCSprite* spriteDisabled = CCSprite::create(s_MenuItem,
CCRectMake(0,23*0,115,23));
CCMenuItemSprite* item1 = CCMenuItemSprite::create(spriteNormal,
spriteSelected, spriteDisabled, this,
menu_selector(MenuLayerMainMenu::menuCallback) );
以上代码实现了在缺省状态、用户选中菜单、用户未选中菜单状态显示不同的图片内容,而这几种不同状态的图片内容是精灵的一个区域。
5.4.3 触发器菜单项
触发器菜单项CCMenuItemToggle可以将任意的菜单项传进去,作为一个触发器按钮开关,代码如下:
CCMenuItemToggle* item1 = CCMenuItemToggle::createWithTarget(this,
menu_selector(MenuLayer4::menuCallback),
CCMenuItemFont::create( ""On"" ),
CCMenuItemFont::create( ""Off""), NULL );
CCMenuItemFont::setFontName( ""American Typewriter"" );
CCMenuItemFont::setFontSize(18);
CCMenuItemFont* title2 = CCMenuItemFont::create( ""Music"" );
title2->setEnabled(false);
CCMenuItemFont::setFontName( ""Marker Felt"" );
CCMenuItemFont::setFontSize(34);
CCMenuItemToggle *item2 = CCMenuItemToggle::createWithTarget(this,
menu_selector(MenuLayer4::menuCallback),
CCMenuItemFont::create( ""On"" ),
CCMenuItemFont::create( ""Off""),
NULL );
CCMenuItemFont::setFontName( ""American Typewriter"" );
CCMenuItemFont::setFontSize(18);
CCMenuItemFont* title3 = CCMenuItemFont::create( ""Quality"" );
title3->setEnabled( false );
CCMenuItemFont::setFontName( ""Marker Felt"" );
CCMenuItemFont::setFontSize(34);
关于菜单的其他使用可以参考TestCpp中MenuTest.cpp。
5.4.4 文本渲染CCLabelAtlas
CCLabelAtlas是使用图片作为文字的一种方式,该类可以通过图片直接定义,代码如下:
CCLabelAtlas* label1 = CCLabelAtlas::create(""123 Test"",
""fonts/tuffy_bold_italic-charmap.png"", 48, 64, ' ');
以上代码创建文字显示标签,第一个参数定义了文字内容,第二个参数是图片路径,第三、四个参数是字符宽高,第四个参数是起始字符。
CCLabelAtlas也可以使用plist配置文件来描述定义,代码如下:
CCLabelAtlas* label1 = CCLabelAtlas::create(""123 Test"",
""fonts/tuffy_bold_italic-charmap.plist"");
在plist文件中描述了文字的相关属性,清单文件如下:
<?xml version=""1.0"" encoding=""UTF-8""?>
<!DOCTYPE plist PUBLIC ""-//Apple//DTD PLIST 1.0//EN""
""http://www.apple.com/DTDs/PropertyList-1.0.dtd"">
<plist version=""1.0"">
<dict>
<key>version</key>
<integer>1</integer>
<key>textureFilename</key>
<string>fonts/tuffy_bold_italic-charmap.png</string>
<key>itemHeight</key>
<integer>64</integer>
<key>itemWidth</key>
<integer>48</integer>
<key>firstChar</key>
<integer>32</integer>
</dict>
</plist>
5.4.5 文本渲染CCLabelTTF
CCLabelTTF是通过系统字体来实现标签内容样式的,定义方式如下:
CCLabelTTF* ttf0 = CCLabelTTF::create(""Alignment 0\nnew line"",
""Helvetica"", 12,
CCSizeMake(256, 32), kCCTextAlignmentLeft);
第一个参数是文本内容,第二、三个参数是使用的字体、字号,第4个参数是范围大小,第5个参数是对齐方式。
5.4.6 文本渲染CCLabelBMFont
CCLabelBMFont的每一个字都是一个精灵类,每个字都可以有自己的动作,并且支持FNT类型的文件,创建CCLabelBMFont代码如下:
CCLabelBMFont* label1 = CCLabelBMFont::create(""Test"",
""fonts/bitmapFontTest2.fnt"");
label1->setAnchorPoint( ccp(0,0) );
addChild(label1, 0, kTagBitmapAtlas1);
CCActionInterval* fade = CCFadeOut::create(1.0f);
CCActionInterval* fade_in = fade->reverse();
CCFiniteTimeAction* seq = CCSequence::create(fade, fade_in, NULL);
CCAction* repeat = CCRepeatForever::create((CCActionInterval*)seq);
label1->runAction(repeat);
以上代码可以看到,每个文字都可以定义CCAction实现跳跃或旋转的文字。
……
展开
——宋炜(GMGC 创始人兼秘书长)
“全国移动开发工程师认证考试”是我国针对移动互联网领域人才培养制定的人才标准评价和职业资格认证体系,我们向广大对移动开发感兴趣的读者推荐沈老师编写的《Cocos2d-x手机游戏开发与项目实战详解》,并作为认证指定教材广泛使用。
——艾鹏(全国移动开发工程师认证考试管理中心)
沈大海老师的《Cocos2d-x手机游戏开发与项目实战详解》,实用性较强,涵盖了游戏策划、美术、编程、运营等多个方面, 是Cocos2d社区里一本较为全面的图书,非常值得阅读。希望这本书能够对广大Cocos2d-x开发者有所帮助,也祝更多的手机游戏开发者在这个高速发展的市场里实现自己的梦想。
——王哲(Cocos2d-x创始人)