下面将用Cocos2dx完成一个跑酷游戏,跑酷游戏从头到尾包括美工完全可以一个人完成,就是比较耗费时间,只能达到能玩的程度而已。
做出来的跑酷游戏如下所示:
玩家能做的就只有一个动作,触摸屏幕,触摸屏幕之后,游戏的主角,就是黑色的方块,能够向上跳跃,再次触摸就能够二段跳,
而在屏幕右上方则有不停发射的箭矢,玩家控制的黑色方块被射中,则会掉10滴血,从1000血可以扣,扣到0则游戏自动重新开始。在屏幕的左上方与中上部有当前的血量显示,而右上角是分数,玩家能撑住,躲过箭矢,分数就会增加。
左下与右下是必须提供的两个功能,一个是暂停游戏,一个是退出游戏,这些小游戏就不提供保存了……(其实,主要是笔者太菜,Cocos2dx的关卡功能与保存功能还有待研究,不会做的缘故。)
一、基本准备
好,游戏大致是这样的情况。在开始游戏之前,我们先要利用(cocos2d-x-2.2.6安装目录)\tools\project-creator下的create_project.py用python命令创建一个名为RunGame的工程。具体是用命令行进入到此文件夹,利用如下命令,新建工程
python create_project.py -project Rungame -package test.Rungame -language cpp
之后准备素材到RunGame的资源文件夹Resource中,上述游戏用到了如下的素材:
1、图片准备
除了各种自带的素材,下面说说每一张图片是如何自己用WIN7画图搞出来的。
首先是关于血条的1.png,就是一张1x1的图片,自己在画图的属性调出一张1x1的图片,之后保存即可。
kuang.png,之后是框,同时用于血条与按钮,具体是用画图的圆角矩形工具一拖就有了,属性设置为100x25像素
hp_full.png,最后是满血状态的血条,是在kuang.png上面加工,用刷子工具,涂上两涂就可以了。大小同上。
至于为什么要这样整,具体可以参考我之前的《【Cocos2dx】利用音量螺旋控件控制血量条》(点击打开链接)
接着是背景backgroud.png,就是利用画图的三角形工具与直线工具,加文字工具弄出来的,唯一注意到的是要适配游戏的屏幕大小,在480x320的像素上创作。
player.png直接将属性设置为40x40,用黑色一填充就完事。
kuang_fan.png,是根据kuang.png用画图的反色工具,一整就出来了。
最后是,稍微有点复杂的是array.png,用画图的直线工具搞出来之后,还要通过photoshop将array.png白色的部分扣出来搞成透明,当然,不整也可以,只是在游戏运行的时候不美观而已。
2、strings.xml,具体如下,原理同《【Cocos2dx】中文乱码问题》(点击打开链接),主要防止乱码。
<dict> <key>fanhui</key> <string>返回</string> <key>xueliang</key> <string>血量:</string> <key>fenshu</key> <string>分数:</string> <key>zanting</key> <string>暂停</string> <key>tuichu</key> <string>退出</string> </dict>
二、工程制作
之后就可以开始工程的制作了,整个工程的UML大致如下:
先提醒大家注意,新建的类一定要放在Classes这个文件夹,如果放在proj.win32就悲剧了,这个我在《【Cocos2dx】新建场景、场景的切换、设置启动场景与菜单的新建》(点击打开链接)中已经提到过了,不在赘述。
先来说两个处于次要地位的类。
首先是飘字类FlowWord,这个类完全与《【Cocos2dx】飘字特效与碰撞检测》(点击打开链接)原理一样,只是改了把字体的颜色从黄色改成红色而已。
FlowWord.h:
#include "cocos2d.h" USING_NS_CC; class FlowWord:public CCNode{ public: void showWord(const char* text,CCPoint pos);//飘字方法,text为飘字的内容,pos为飘字的位置 private: CCLabelTTF* label;//类成员 void flowEnd();//飘字结束时的回调(处理)函数,主要用于删除自己 };
FlowWord.cpp:
#include "FlowWord.h" void FlowWord::showWord(const char* text,CCPoint position){//text为飘字的内容,pos为飘字的位置 /*初始化*/ label=CCLabelTTF::create(text,"Arial",18);//创建一个字体为Arial,字号为18,内容为text的CCLabelTTF,也就是标签文本 label->setColor(ccc3(255,0));//设置其颜色为红色 label->setPosition(position);//设置其位置 this->addChild(label);//在场景上添加这个标签文本 /*三个动作,放大->移动->缩小*/ CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();//获取屏幕的尺寸、位置信息等 CCFiniteTimeAction* action1=CCScaleTo::create(0.2f,3.0f,3.0f);//0.2s内在x、y上方向皆放大为原尺寸的3倍 CCFiniteTimeAction* action2=CCMoveTo::create(0.3f,ccp(visibleSize.width/4,3*visibleSize.height/4));//在0.3s内,移动到坐标为(x=屏幕宽度的25%,y=屏幕高度的75%处) CCFiniteTimeAction* action3 = CCScaleTo::create(0.2f,0.1f,0.1f);//之后在0.2s内在x、y上皆缩小为原尺寸的0.1倍 CCCallFunc* callFunc = CCCallFunc::create(this,callfunc_selector(FlowWord::flowEnd));//声明一个回调(处理)函数,为FlowWord类中的flowEnd() CCFiniteTimeAction* action = CCSequence::create(action1,action2,action3,callFunc,NULL);//以上的所有动作组成动作序列action /*对label实行action这个动作*/ label->runAction(action); } void FlowWord::flowEnd(){//动作结束,从父节点中删除自身 label->setVisible(false);//先隐藏显示 label->removeFromParentAndCleanup(true);//再删除 }
之后是暂停场景PauseScene,这个原理也在《【Cocos2dx】利用导演类、场景类完成重新开始游戏、暂停游戏、关闭游戏功能》( 点击打开链接)讲过,不再赘述了。
PauseScene.h:
#include "cocos2d.h" #include "cocos-ext.h" //使用按钮事件,必须要需要的头文件 USING_NS_CC_EXT;//使用按钮事件,必须要需要的命名空间 using namespace cocos2d; class PauseScene:public CCLayer{ public: static CCScene* scene(); virtual bool init(); CREATE_FUNC(PauseScene); void back(CCObject* pSender,CCControlEvent event);//返回场景 };
PauseScene.cpp:
#include "PauseScene.h" USING_NS_CC; CCScene* PauseScene::scene() { CCScene *scene=CCScene::create(); PauseScene* pauseScene=PauseScene::create(); scene->addChild(pauseScene); return scene; } bool PauseScene::init() { //获取屏幕的尺寸、位置信息等 CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize(); /*通过载入strings.xml的中文,防止乱码*/ CCDictionary *strings = CCDictionary::createWithContentsOfFile("strings.xml");//利用CCDictionary来读取xml,载入资源文件夹的strings.xml const char *fanhui = ((CCString*)strings->objectForKey("fanhui"))->m_sString.c_str();//读取xueliang键中的值,objectForKey会根据key,获取对应的string //声明按钮部分 cocos2d::extension::CCScale9Sprite *btn_noraml=cocos2d::extension::CCScale9Sprite::create("kuang_fan.png");//声明按钮没被按下时的背景图片 CCLabelTTF *label1=CCLabelTTF::create(CCString::createWithFormat("%s",fanhui)->getCString(),"arial",36);//声明第1个参数是文字的内容,第2个参数是字体,仅能使用Resource文件夹中fonts文件夹中的字体,第3个参数是字体大小 CCControlButton *controlButton=CCControlButton::create(label1,btn_noraml);//创建按钮 controlButton->setPosition(ccp(visibleSize.width/2,visibleSize.height/2));//按钮的位于屏幕的中央 controlButton->addTargetWithActionForControlEvents(this,cccontrol_selector(PauseScene::back),CCControlEventTouchDown);//声明按钮的事件,第三个参数为定值常量意为,点击此按钮之后,触发第二个函数所声明的,下面给出的PauseScene::back(){}中所有代码。 this->addChild(controlButton);//将此按钮添加到场景,默认不自动添加 return true; } void PauseScene::back(CCObject* pSender,CCControlEvent event) { //本场景出栈 CCDirector::sharedDirector()->popScene(); }
接下来比较重要的两个主角,一个玩家类,一个是怪物类:
首先是怪物类Monster,也就是那些箭矢,一只怪物很简单,声明好其开始位置、图片即可,到时候其产生与击中玩家、飞出屏幕外围的方法,在MainScene这个主场景类中控制。唯一注意的是暴露出CCSprite* sprite;自身精灵类与是否处于屏幕的标识bool isEmerge给主场景类MainScene控制。
Monster.h:
#include "cocos2d.h" USING_NS_CC;//用到了CCxx,比如CCNode class Monster:public CCNode{//由于用到了this->addChild(sprite);必须继承CCNode public: Monster();//构造函数 CCSprite* sprite; bool isEmerge;//是否处于屏幕的标识 };Monster.cpp:
#include "Monster.h" Monster::Monster(){ CCSize visibleSize=CCDirector::sharedDirector()->getVisibleSize();//获取屏幕的尺寸、位置信息等 sprite=CCSprite::create("array.png");//使用CloseSelected.png这张图片 sprite->setScale(0.5f); sprite->setPosition(ccp(5*visibleSize.width/6+CCRANDOM_0_1()*visibleSize.width/6,5*visibleSize.height/6+CCRANDOM_0_1()*visibleSize.height/6)); this->addChild(sprite);//添加到舞台 isEmerge=true;//开始为已经出现状态 }
随后是玩家类Player,要完成的事情是三件,一个在玩家的构造函数,设置其初始诞生的位置、自身拥有的属性等;二是,其跳跃与二段跳跃具体是怎么实现的,原理与《【Cocos2dx】精灵触摸跳跃功能》( 点击打开链接)一模一样,这里不再赘述了,二段跳跃只是在此基础上加深,具体见下面代码的注释;最后是被箭矢击中,怎么做什么动作的hit(),主要就是调用FlowWord的showword()飘字,扣血除自身的hp10点。
Player.h,暴露jump()这个方法给主场景类MainScene,主场景类的触摸事件直接调用这个方法即可。hit()、hp、score都要暴露给主函数所操作、修改、判断、执行:
#include "cocos2d.h" #include "FlowWord.h" USING_NS_CC;//用到了CCxx,比如CCNode class Player:public CCNode{//由于用到了this->addChild(sprite);必须继承CCNode public: Player();//构造函数 void jump();//跳跃 CCSprite* sprite; void hit();//被击中的处理函数 float score;//当前得分 int hp;//当前血量 private: bool isJumping;//是否跳跃的flag void doubleJump();//二段跳的实现 void jumpEnd();//声明跳跃结束的回调函数 bool isDoubleJumping;//是否二段条的flag void doubleJumpEnd();//声明二段条结束的回调函数 };
Player.cpp:
#include "Player.h" /*构造函数,也就是初始化的函数*/ Player::Player(){ CCSize visibleSize=CCDirector::sharedDirector()->getVisibleSize();//获取屏幕的尺寸、位置信息等 sprite=CCSprite::create("player.png");//使用CloseSelected.png这张图片 sprite->setPosition(ccp(visibleSize.width/3,visibleSize.height/6));//放在屏幕大小的(1/3,1/6)这个位置 this->addChild(sprite);//添加到舞台 isJumping=false;//开始为没有跳跃状态 isDoubleJumping=false; score=0; hp=1000; } /*被击中之后的动作*/ void Player::hit(){ FlowWord* flowWord=new FlowWord();//初始化FlowWord this->addChild(flowWord);//将FlowWord飘字特效放上舞台,尽管飘字特效,一不是精灵,二在showWord中同样有将字体放上舞台的代码,然而没有这一句,HelloWorldScene这个场景根本不会与FlowWord联系起来,也就是FlowWord中添加的Label根本无法在HelloWorldScene中显示。 flowWord->showWord("-10",sprite->getPosition());//执行飘字-10,位置从自身位置开始。 hp-=10; } /*跳跃*/ void Player::jump(){ CCSize visibleSize=CCDirector::sharedDirector()->getVisibleSize();//获取屏幕的尺寸、位置信息等 if(!isJumping){//如果主角还在跳跃中,则不重复执行 isJumping=true;//标记主角为跳跃状态 CCJumpBy* jump=CCJumpBy::create(1.0f,ccp(0,0),visibleSize.height/2,1);//在2.0秒内,先跳起屏幕尺寸的1/2再落下0px,该动作重复1次 CCCallFunc* callFunc=CCCallFunc::create(this,callfunc_selector(Player::jumpEnd));//创建回调函数,声明跳跃结束后调用jumpEnd函数 CCActionInterval* jumpActions=CCSequence::create(jump,NULL);//将回调函数与跳跃动作结合起来,这个NULL不能省 sprite->runAction(jumpActions);//执行动作 } else{//如果是在跳跃 if(isDoubleJumping){//判断是否在二段跳 return;//否则不能二段跳 } else{//如果不是,则可以二段跳 doubleJump(); } } }; void Player::jumpEnd(){//主角已经完成跳跃了 isJumping=false;//标志主角为没有跳跃状态 isDoubleJumping=false; } /*二段跳跃*/ void Player::doubleJump(){ CCSize visibleSize=CCDirector::sharedDirector()->getVisibleSize();//获取屏幕的尺寸、位置信息等 isDoubleJumping=true;//标记主角为跳跃状态 CCJumpBy* double_jump=CCJumpBy::create(0.5f,visibleSize.height/3,1);//在2.0秒内,先跳起屏幕尺寸的1/2再落下0px,该动作重复1次 CCCallFunc* callFunc=CCCallFunc::create(this,callfunc_selector(Player::doubleJumpEnd));//创建回调函数,声明跳跃结束后调用jumpEnd函数 CCActionInterval* jumpActions=CCSequence::create(double_jump,NULL);//将回调函数与跳跃动作结合起来,这个NULL不能省 sprite->runAction(jumpActions);//执行动作 }; void Player::doubleJumpEnd(){//主角已经完成二段跳跃了 isDoubleJumping=false;//标志主角为没有二段跳跃状态 }
最后是整个程序的核心、入口,主函数类MainScene,
1、首先因为用到了音乐资源,所以先给这个类如同《【Cocos2dx】资源文件夹,播放背景音乐,导入外部库》(点击打开链接)一样,引入cocos2dx的音频引擎,否则SimpleAudioEngine.h会被说是找不到这个类,编译错误
2、Cocos2dx中文的使用请看《【Cocos2dx】中文乱码问题》(点击打开链接)
3、滚动背景的实现请看《【Cocos2dx】连续滚动的场景》(点击打开链接)
4、血条的使用请看《【Cocos2dx】利用音量螺旋控件控制血量条》(点击打开链接)
5、按钮的使用请看《【Cocos2dx】使用CCControlButton创建按钮、按钮点击事件,点击事件中的组件获取,setPosition的坐标问题》(点击打开链接)
6、关于两只精灵如何的碰撞检测的,请看《【Cocos2dx】飘字特效与碰撞检测》(点击打开链接)
7、暂停游戏、退出游戏等请看《【Cocos2dx】利用导演类、场景类完成重新开始游戏、暂停游戏、关闭游戏功能》(点击打开链接)
8、关键在与箭矢的产生、也就是怪物的产生,大家可以看到上面介绍过的怪物类,通过主场景类中的init的new方法,执行怪物的构造函数,怪物将产生如下图的在右上角的区域,区域的控制,核心在设置了一个固定位置再加上一个随机数。
之后,我们将这一个一个类的指针压入一个类指针数组,在Java就是将类压入一个ArrayList,在这C++中可以清晰地看到实质是在操作类的指针。
当然这扯远了,回到正题,主场景类中的update即时更新函数,这个类指针数组被不停地遍历,被不停地操作。不停地判断箭矢,是否碰到玩家、是否已经飞出屏幕,对碰到玩家、飞出屏幕的箭矢,重新将其位置搞出其原来产生的地方,同时记得更改其出现标识与显示与否。
触摸事件只放一个Player类的jump函数就可以了,当用户触摸屏幕之时,则不停地执行jump(),但是,我们已经在Player实现了封装,判断好此时主角是处于什么状态,该怎么操作了。
具体见下面的代码的注释,欢迎交流。
MainScene.h:
#include "cocos2d.h" #include "Player.h" #include "Monster.h" #include "cocos-ext.h"//血条 #include "SimpleAudioEngine.h"//BGM #include "PauseScene.h" USING_NS_CC; //用到了CCxx,比如CCNode using namespace cocos2d::extension;//血条 #define MAX_MONSTER_NUM 2//怪物最大数量 class MainScene:public cocos2d::CCLayer { public: /*场景创建必须的函数*/ static cocos2d::CCScene* scene(); CREATE_FUNC(MainScene); virtual bool init();//场景初始化函数 virtual void update(float delta);//即使更新事件 private: CCLabelTTF* label_upper_left;//左上角文字 CCLabelTTF* label_upper_right;//右上角文字 void ccTouchesBegan(CCSet *pTouches,CCEvent *pEvent);//触摸事件的函数声明,开始触摸屏幕的瞬间则触发此事件 Player* player;//主角类 Monster* monster;//怪物类 CCControlSlider *controlSlider;//血条 //添加两个背景精灵,作为背景用,实质上就是同一张图片放上二个不同的背景精灵 CCSprite* bgSprite1; CCSprite* bgSprite2; Monster* monster_arr[10];//存放怪物的数组 void close(CCObject* pSender,CCControlEvent event);//关闭 void pause(CCObject* pSender,CCControlEvent event);//暂停 };
MainScene.cpp:
#include "MainScene.h" CCScene* MainScene::scene()//必须存在的场景建立函数 { CCScene *scene = CCScene::create(); MainScene *layer = MainScene::create(); scene->addChild(layer); return scene; } /*初始化*/ bool MainScene::init(){ CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();//获取屏幕的尺寸、位置信息等 /*BGM*/ CocosDenshion::SimpleAudioEngine::sharedEngine()->playBackgroundMusic("Kalimba.mp3",true);//播放音乐,BGM是Kalimba.mp3这首难听的歌,而且是洗脑循环 /*事件声明*/ this->setTouchEnabled(true);//声明这个场景是存在触摸事件的 this->scheduleUpdate();//声明这个场景是存在即使更新事件的 /*通过载入strings.xml的中文,防止乱码*/ CCDictionary *strings = CCDictionary::createWithContentsOfFile("strings.xml");//利用CCDictionary来读取xml,载入资源文件夹的strings.xml const char *xueliang = ((CCString*)strings->objectForKey("xueliang"))->m_sString.c_str();//读取xueliang键中的值,objectForKey会根据key,获取对应的string const char *fenshu = ((CCString*)strings->objectForKey("fenshu"))->m_sString.c_str();//读取fenshu键中的值,objectForKey会根据key,获取对应的string const char *zanting = ((CCString*)strings->objectForKey("zanting"))->m_sString.c_str();//读取zanting键中的值,objectForKey会根据key,获取对应的string const char *tuichu = ((CCString*)strings->objectForKey("tuichu"))->m_sString.c_str();//读取tuichu键中的值,objectForKey会根据key,获取对应的string /*背景*/ bgSprite1=CCSprite::create("background.png"); bgSprite1->setPosition(ccp(visibleSize.width/2,visibleSize.height/2)); this->addChild(bgSprite1,0);//将此控件添加到场景,层次为0,最底部 bgSprite2=CCSprite::create("background.png"); bgSprite2->setPosition(ccp(visibleSize.width+visibleSize.width/2,visibleSize.height/2)); this->addChild(bgSprite2,0);//将此控件添加到场景,层次为0,最底部 /*文字声明*/ label_upper_left=CCLabelTTF::create(CCString::createWithFormat("%s%d",xueliang,1000)->getCString(),36); label_upper_left->setAnchorPoint(ccp(0,1));//设置label的中心点在左上角 label_upper_left->setPosition(ccp(0,visibleSize.height));//把中心点摆在屏幕的左上角 label_upper_left->setColor(ccc3(0,0)); this->addChild(label_upper_left,1);//添加此文字到场景中,层次为1 label_upper_right=CCLabelTTF::create(CCString::createWithFormat("%s%d",fenshu,0)->getCString(),36); label_upper_right->setAnchorPoint(ccp(1,1));//设置label的中心点在左下角 label_upper_right->setPosition(ccp(visibleSize.width,visibleSize.height));//把中心点摆在屏幕的左下角 label_upper_right->setColor(ccc3(0,0)); this->addChild(label_upper_right,1);//添加此文字到场景中,层次为1 /*血条声明*/ controlSlider = CCControlSlider::create("kuang.png","hp_full.png","1.png");//第1个参数是血条没有被占据的部分的背景图片,第2是血条被占据的部分的背景图片,第3个参数是条件按钮。 controlSlider->setAnchorPoint(ccp(0.5,1));//设置controlSlider的中心点在中上部 controlSlider->setPosition(ccp(visibleSize.width/2,visibleSize.height));//将此组件布置在中上部 //设置按钮最大、最小值的基准 controlSlider->setMinimumValue(0); controlSlider->setMaximumValue(1000); controlSlider->setValue(1000);//设置按钮当前值 controlSlider->setTouchEnabled(false);//本来CCControlSlider是供用户调节的,调节按钮是1.png,但是1.png是一张1x1的近乎看不到的图片,同时利用setTouchEnabled(false)将此按钮锁上,禁止用户调节 this->addChild(controlSlider,1);//将此控件添加到场景,层次为1 /*右下角按钮*/ CCScale9Sprite *btn_noraml3 = CCScale9Sprite::create("kuang.png");//声明按钮没被按下时的背景图片 CCLabelTTF *label3 = CCLabelTTF::create(CCString::createWithFormat("%s",tuichu)->getCString(),36);//第1个参数是按钮的内容,第2个参数是字体,仅能使用Resource文件夹中fonts文件夹中的字体,第3个参数是字体大小 label3->setColor(ccc3(0,0)); CCControlButton *controlButton3 = CCControlButton::create(label3,btn_noraml3); controlButton3->setAnchorPoint(ccp(1,0)); controlButton3->setPosition(ccp(visibleSize.width,0)); controlButton3->addTargetWithActionForControlEvents(this,cccontrol_selector(MainScene::close),CCControlEventTouchDown);//声明按钮的事件,第三个参数为定值常量意为,点击此按钮之后,触发第二个函数所声明的,下面给出的MainScene::close(){}中所有代码。 this->addChild(controlButton3);//将此按钮添加到场景,默认不自动添加 /*左下角按钮*/ CCScale9Sprite *btn_noraml2 = CCScale9Sprite::create("kuang.png");//声明CloseNormal图片,用于按钮没被按下时的背景图片 CCLabelTTF *label2 = CCLabelTTF::create(CCString::createWithFormat("%s",zanting)->getCString(),36);//声明一个文字Refresh!第2个参数是字体,仅能使用Resource文件夹中fonts文件夹中的字体,第3个参数是字体大小 label2->setColor(ccc3(0,0)); CCControlButton *controlButton2 = CCControlButton::create(label2,btn_noraml2); controlButton2->setAnchorPoint(ccp(0,0)); controlButton2->setPosition(ccp(0,0)); controlButton2->addTargetWithActionForControlEvents(this,cccontrol_selector(MainScene::pause),CCControlEventTouchDown);//声明按钮的事件,第三个参数为定值常量意为,点击此按钮之后,触发第二个函数所声明的,下面给出的HelloWorld::restart(){}中所有代码。 this->addChild(controlButton2);//将此按钮添加到场景,默认不自动添加 /*精灵声明*/ player=new Player();//创建一个主角 this->addChild(player,2);//将主角注册到这个场景中,层次为2 /*怪物的产生*/ for(int i=0;i<MAX_MONSTER_NUM;i++){ monster=new Monster(); this->addChild(monster,2);//将此控件添加到场景,层次为2 monster_arr[i]=monster;//保存怪物对象到类指针中,给即时更新事件update中处理。 } return true; } /*碰撞检测,判断两只精灵是否相交的函数*/ bool is_collision(CCSprite* sprite1,CCSprite* sprite2){ //建立精灵1的矩形 CCSize sprite_size1=sprite1->getContentSize();//求精灵的尺寸 CCPoint sprite_position1=sprite1->getPosition();//求精灵的中心点坐标 CCRect sprite_rect1=CCRectMake(//以精灵的中心点为中心点、以精灵的尺寸为宽与高,建立一个矩形。 sprite_position1.x-sprite_size1.width/2,sprite_position1.y-sprite_size1.height/2,sprite_size1.width,sprite_size1.height); //建立精灵2的矩形,同理 CCSize sprite_size2=sprite2->getContentSize(); CCPoint sprite_position2=sprite2->getPosition(); CCRect sprite_rect2=CCRectMake( sprite_position2.x-sprite_size2.width / 2,sprite_position2.y-sprite_size2.height / 2,sprite_size2.width,sprite_size2.height); return sprite_rect1.intersectsRect(sprite_rect2);//如果是判断矩形与一个像素点则用Rect实例.containsPoint(像素点);这里是判断两个矩形是否相交 } /*触摸屏幕*/ void MainScene::ccTouchesBegan(cocos2d::CCSet *pTouches,cocos2d::CCEvent *pEvent){ player->jump();//主角跳跃 } /*即时更新事件*/ void MainScene::update(float delta){ CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();//获取屏幕的尺寸、位置信息等 /*背景连续滚动*/ int posX1=bgSprite1->getPositionX();//背景地图1的x坐标 int posX2=bgSprite2->getPositionX();//背景地图2的x坐标 int iSpeed=1;//地图滚动速度 //两张地图向左滚动,因为两张地图是相邻的,所以要一起滚动,否则会出现空隙。 posX1-=iSpeed; posX2-=iSpeed; CCSize mapSize=bgSprite1->getContentSize();//地图大小 //当第1个地图完全离开屏幕时,让第2个地图完全出现在屏幕上,同时让第1个地图紧贴在第2个地图后面 if(posX1<-mapSize.width/2){ posX2=mapSize.width/2; posX1=mapSize.width+mapSize.width/2; } //同理,当第2个地图完全离开屏幕时,让第1个地图完全出现在屏幕上,同时让第2个地图紧贴在第1个地图后面 if(posX2<-mapSize.width/2){ posX1=mapSize.width/2; posX2=mapSize.width+mapSize.width/2; } bgSprite1->setPositionX(posX1); bgSprite2->setPositionX(posX2); /*通过载入strings.xml的中文,防止乱码*/ CCDictionary *strings = CCDictionary::createWithContentsOfFile("strings.xml");//利用CCDictionary来读取xml,载入资源文件夹的strings.xml const char *xueliang = ((CCString*)strings->objectForKey("xueliang"))->m_sString.c_str();//读取xueliang键中的值,objectForKey会根据key,获取对应的string const char *fenshu = ((CCString*)strings->objectForKey("fenshu"))->m_sString.c_str();//读取fenshu键中的值,objectForKey会根据key,获取对应的string player->score+=0.2;//不断给玩家加分 label_upper_right->setString(CCString::createWithFormat("%s%.0f",player->score)->getCString());//改变右上角的标签文本 for(int i=0;i<MAX_MONSTER_NUM;i++){//时刻遍历怪物的类指针数组 monster=monster_arr[i]; if(monster->isEmerge) {//如果怪物处于屏幕内 monster->sprite->setPosition(ccp(monster->sprite->getPositionX()-visibleSize.width/100,monster->sprite->getPositionY()-visibleSize.height/100));//那么他们的位置将会移动当前屏幕宽度与高度的1/100 if(monster->sprite->getPositionX()<0|| monster->sprite->getPositionY()<0){//如果怪物x坐标小于0,则表示已经超出屏幕范围,隐藏怪物 monster->sprite->setVisible(false);//将其隐藏 monster->isEmerge=false;//设置为不在屏幕内 } if (is_collision(player->sprite,monster->sprite)){//判断怪物是否碰撞玩家 monster->sprite->setVisible(false);//将其隐藏 player->hit();//玩家执行被撞击函数 monster->isEmerge=false;//设置为不在屏幕内 controlSlider->setValue(player->hp);//改变中上部血条当前的值 label_upper_left->setString(CCString::createWithFormat("%s%d",player->hp)->getCString());//左上角的文字 if(player->hp<1){//如果玩家被扣到0 CCDirector::sharedDirector()->replaceScene(MainScene::scene());//游戏重新开始 } } } else { monster->sprite->setPosition(ccp(5*visibleSize.width/6+CCRANDOM_0_1()*visibleSize.width/6,5*visibleSize.height/6+CCRANDOM_0_1()*visibleSize.height/6));//在屏幕的右上方区域产生箭矢 monster->sprite->setVisible(true); monster->isEmerge=true; } } } void MainScene::close(CCObject* pSender,CCControlEvent event) { //结束当前游戏 CCDirector::sharedDirector()->end(); exit(0); } void MainScene::pause(CCObject* pSender,CCControlEvent event) { //将游戏界面暂停,压入场景堆栈。并切换到GamePause界面 CCDirector::sharedDirector()->pushScene(PauseScene::scene()); }
总体来说吧,这个游戏的逻辑并不是复杂,但是将Cocos2dx所有的基本知识一次性地运用起来了。
而且在C++中,尤其是这些多个类配合一起来工作的时候,一定要时刻注意你是在操作哪个指针,不要出现指针的重复声明,导致编译没错,程序打死都运行都不起来。