下面将用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,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, callFunc, 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,ccp(0,0),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, callFunc, 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(),"arial",36);
label_upper_left->setAnchorPoint(ccp(0,1));//设置label的中心点在左上角
label_upper_left->setPosition(ccp(0,visibleSize.height));//把中心点摆在屏幕的左上角
label_upper_left->setColor(ccc3(0,0,0));
this->addChild(label_upper_left,1);//添加此文字到场景中,层次为1
label_upper_right=CCLabelTTF::create(CCString::createWithFormat("%s%d",fenshu,0)->getCString(),"arial",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,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(),"arial",36);//第1个参数是按钮的内容,第2个参数是字体,仅能使用Resource文件夹中fonts文件夹中的字体,第3个参数是字体大小
label3->setColor(ccc3(0,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(),"arial",36);//声明一个文字Refresh!第2个参数是字体,仅能使用Resource文件夹中fonts文件夹中的字体,第3个参数是字体大小
label2->setColor(ccc3(0,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",fenshu,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",xueliang,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++中,尤其是这些多个类配合一起来工作的时候,一定要时刻注意你是在操作哪个指针,不要出现指针的重复声明,导致编译没错,程序打死都运行都不起来。