Cocos2d-x项目实践一FlappyBird-游戏逻辑「终于解决」

Cocos2d-x项目实践一FlappyBird-游戏逻辑「终于解决」这一节我们给大家介绍FlappyBird这款游戏的最重要的部分,游戏逻辑及碰撞检测。

欢迎大家来到IT世界,在知识的湖畔探索吧!

这一节我们给大家介绍FlappyBird这款游戏的最重要的部分,游戏逻辑及碰撞检测。

这一节我们还是将代码实现过程分成两部分

UI部分和功能部分

UI部分包括

背景、按钮、小鸟

功能部分

碰撞检测、定时更新、触摸点击检测、游戏状态标识

UI部分

先看下面运行的效果图

Cocos2d-x项目实践一FlappyBird-游戏逻辑「终于解决」

游戏背景、坐上角暂停按钮、顶部计数的精灵

下面是代码实现

//随机产生背景图

int i = random();

if(i%2 == 0)

{

background = Sprite::create(“pic/morning.png”);

}else

{

background = Sprite::create(“pic/night.png”);

}

background->setAnchorPoint(Point(0, 0));

background->setPosition(Point(origin.x,origin.y + visibleSize.height – background->getContentSize().height));

this->addChild(background, 0);

//添加开始吧这个准备精灵

effectNode = NodeGrid::create();

this->addChild(effectNode, 1);

ready = Sprite::create(“pic/readyBird.png”);

ready->setPosition(Point(visibleSize.width/2, visibleSize.height*4/7));

effectNode->addChild(ready, 10);

//添加地板精灵

floor = Sprite::create(“pic/floor.png”);

floor->setAnchorPoint(Point(0, 0));

floor->setPosition(Point(origin.x, origin.y));

this->addChild(floor);

Size floorSize = floor->getContentSize();

floor->runAction(RepeatForever::create(Sequence::create(

MoveTo::create(0.5, Point(-120, 0)),MoveTo::create(0, Point(0, 0)),NULL)));

//添加计数的label

pLabelAtlas = LabelAtlas::create(“0″,”pic/number.png”, 48, 64,’0′);

pLabelAtlas->setPosition(Point(visibleSize.width/2,visibleSize.height/5*4));

this->addChild(pLabelAtlas,4);

//添加左上角按钮精灵,并且注册监听,触摸响应调用BirdLayer::onTouchPause

pause = Sprite::create(“button/pause.png”);

pause->setPosition(Point(32, 928));

this->addChild(pause,10);

EventListenerTouchOneByOne* listenerPause = EventListenerTouchOneByOne::create();

listenerPause->setSwallowTouches(true);

listenerPause->onTouchBegan = CC_CALLBACK_2(BirdLayer::onTouchPause, this);

_eventDispatcher->addEventListenerWithSceneGraphPriority(listenerPause, pause);

以上是游戏开始的准备UI,我们可以想象一下,游戏开始后,我们还需要的UI元素精灵包括,飞行中的小鸟,还有上下不断移动的管道,下面我们就来实现小鸟和管道的初始化。

小鸟的初始化

在initBird()中实现,在这个实现过程中,我们会接触到cocos2d中动画相关的知识点

void BirdLayer::initBird()

{ //首先我们创建精灵帧缓存的实例,通过SpriteFrameCache::getInstance()。

SpriteFrameCache* sfc = SpriteFrameCache::getInstance();

//将精灵帧缓存必须的.plist文件和拼合的png文件添加

sfc->addSpriteFramesWithFile(“pic/bird.plist”, “pic/birdP.png”);

//构建字符创形式的数组,用于创建动画时使用

std::string animBird[3] =

{

“bird1.png”,

“bird2.png”,

“bird3.png”

};

//声明精灵帧的向量

Vector<SpriteFrame*> animFrames;

//精灵帧向量中添加精灵,通过getSpriteFrameByName()

animFrames.pushBack(sfc->getSpriteFrameByName(animBird[0]));

animFrames.pushBack(sfc->getSpriteFrameByName(animBird[1]));

animFrames.pushBack(sfc->getSpriteFrameByName(animBird[2]));

//用精灵帧向量来创建动画

Animation *anim = Animation::createWithSpriteFrames(animFrames, 0.1f);

//创建动画的动作

animAc = Animate::create(anim);

animAc->retain();

}

大家可以通过dash去查一下这里面涉及到的这些类和方法的使用,更加了解其用法

SpriteFrameCache

Animation

Animate

小鸟的初始化完成后,我们要把他添加到当前的场景中来,看代码实现

//创建小鸟的精灵、注意这里并未添加图片,只是创建了精灵,设定位置,添加到场景中,只有在最后一步runAction时通过动画来创建动作时,才将精灵的图片真正添加进来,实际上是以动画的形式添加

bird = Sprite::create();

bird->setPosition(Point(140 + origin.x, origin.y +floorSize.height + backgroundSize.height/2));

this->addChild(bird, 2);

bird->runAction(RepeatForever::create(animAc));

管道的初始化

管道的初始化分为前面的管道1和随后跟着出来的管道2,原理基本一样

initColumn1()

void BirdLayer::initColumn1()

{

Size visibleSize = Director::getInstance()->getVisibleSize();

Size backSize = background->getContentSize();

Size floorSize = floor->getContentSize();

//设定上下管道的高度

int i = random();

int height1 = 400/i;

int height2 = backSize.height – height1 – 196;

//创建上下管道的节点

SpriteBatchNode* columnNode1 = SpriteBatchNode::create(“pic/column1.png”);

SpriteBatchNode* columnNode2 = SpriteBatchNode::create(“pic/column2.png”);

columnFlag1 = true;

//创建下管道的精灵

columnUnder1 = Scale9Sprite::create();

columnUnder1->updateWithBatchNode(columnNode1, Rect(0, 0, 96, 400), false, Rect(0, 30, 96, 400));

columnUnder1->setAnchorPoint(Point(0, 0));

//设置管道的contentsize

columnUnder1->setContentSize(Size(96, height1));

if(count>0)

{ //游戏中columnUnder1的位置

columnUnder1->setPosition(Point(visibleSize.width, floorSize.height));

}else

{//游戏开始前columnUnder1的初始位置

columnUnder1->setPosition(Point(visibleSize.width*2, floorSize.height));

}

//同样的方式创建顶部的管道

columnOn1 = Scale9Sprite::create();

columnOn1->updateWithBatchNode(columnNode2, Rect(0, 0, 96, 400), false, Rect(0, 0, 96, 370));

columnOn1->setAnchorPoint(Point(0, 0));

columnOn1->setContentSize(Size(96, height2));

if(count>0)

{

columnOn1->setPosition(Point(visibleSize.width, visibleSize.height – height2));

}else

{

columnOn1->setPosition(Point(visibleSize.width*2, visibleSize.height – height2));

}

this->addChild(columnUnder1, 0);

this->addChild(columnOn1, 0);

// 创建的上下管道做重复的运动在起始点的基础上,每次都左移25,实现管道的滚动,通过上面的随机值i,来变化管道的高度

columnUnder1->runAction(RepeatForever::create(MoveBy::create(0.1, Point(-25, 0))));

columnOn1->runAction(RepeatForever::create(MoveBy::create(0.1, Point(-25, 0))));

}

从上图看,前面的第一个管道就是column1,后面就是column2,逻辑基本一样。

UI基本介绍到这里,我们现在开始介绍功能和整体逻辑

功能介绍和游戏逻辑

  • 游戏界面完成初始化 birdLayer::init()

  • 点击界面开始游戏startGame()

  • 碰撞检测、游戏是否结束

游戏界面完成初始化 birdLayer::init()

1.游戏状态标识的初始化

stopFlag = false;//游戏停止

gameFlag = true;//游戏中

readyFlag = true;//游戏准备开始

overFlag = false;//越过管道

2.背景、小鸟、管道、暂停、触摸监听(暂停和游戏开始)

暂停的响应函数为onTouchPause

bool BirdLayer::onTouchPause(Touch* touch, Event* event)

{

if(!gameFlag)//判断是否已经暂停,如果已经暂停,直接返回false

{

return false;

}

auto target = static_cast<Sprite*>(event->getCurrentTarget());//通过触摸事件获取到的目标,转换为精灵

auto location = target->convertToNodeSpace(touch->getLocation());

auto size = target->getContentSize();

auto rect = Rect(0, 0, size.width, size.height);

if(!stopFlag && rect.containsPoint(location) )//判断是否点击到暂停的精灵和游戏是否停止

{

stopFlag = true;

pause->setTexture(“button/continue_pause.png”);//将pause的纹理图设定为继续

Director::getInstance()->pause();//调用导演实例,暂停

pauseBack = Sprite::create(“pic/setBackground.png”);

pauseBack->setPosition(Point(270, 600));

this->addChild(pauseBack, 10);

Sprite* music = Sprite::create(“pic/music.png”);

pauseBack->addChild(music, 1);

music->setPosition(Point(120, 300));

CheckBox* checkMusic = CheckBox::create( “button/sound_on.png”,”button/sound_off.png”,

“button/sound_off.png”, “button/sound_stop.png”,

“button/sound_stop.png” );

pauseBack->addChild(checkMusic, 1);

checkMusic->setPosition(Point(350, 300));

checkMusic->setSelectedState(!MainLayer::musicFlag);

checkMusic->addEventListener(CC_CALLBACK_2(BirdLayer::selectedEvent0, this));

Sprite* sound = Sprite::create(“pic/sound.png”);

pauseBack->addChild(sound, 1);

sound->setPosition(Point(120, 200));

CheckBox* checkSound = CheckBox::create(“button/sound_on.png”,”button/sound_off.png”,”button/sound_off.png”,”button/sound_stop.png”,”button/sound_stop.png”);

pauseBack->addChild(checkSound, 1);

checkSound->setPosition(Point(350, 200));

checkSound->setSelectedState(!MainLayer::soundFlag);

checkSound->addEventListener(CC_CALLBACK_2(BirdLayer::selectedEvent1, this));

//继续按钮

MenuItemImage* conItem = MenuItemImage::create(“button/continue.png”,”button/continue_off.png”,CC_CALLBACK_1(BirdLayer::menuCallbackItem3, this) );

conItem->setPosition(Point(80, 80));

//返回按钮

MenuItemImage* backItem = MenuItemImage::create(“button/menu.png”,”button/menu_off.png”,CC_CALLBACK_1(BirdLayer::menuCallbackItem2, this) );

backItem->setPosition(Point(225, 80));

//重新开始按钮

MenuItemImage* againItem = MenuItemImage::create(“button/replay.png”,”button/replay_off.png”,CC_CALLBACK_1(BirdLayer::menuCallbackItem4, this) );

againItem->setPosition(Point(370, 80));

Menu* menu = Menu::create(againItem, conItem, backItem, NULL);

menu->setPosition(Point::ZERO);

pauseBack->addChild(menu,1);

return true;

}

else

{

return false;

}

Cocos2d-x项目实践一FlappyBird-游戏逻辑「终于解决」

触摸界面游戏开始

bool BirdLayer::onTouchBegan(Touch* touch, Event* event)

{

Point birdPosition = bird->getPosition();

if(gameFlag)//游戏标志位判断,如果已经在游戏了,就跳过,直接执行下面小鸟跳动的效果

{

if(readyFlag)//是否准备好

{

startGame();开始游戏

readyFlag = false;//将准备标志位表示为fasle,表示游戏已经开始了

}

if(MainLayer::soundFlag)

{

wingSound();

}

int move = 105;

//创建瞬时动作,点击跳动,同时角度调整

auto action = Spawn::createWithTwoActions(MoveTo::create(0.2, Point(birdPosition.x, birdPosition.y + move)), RotateTo::create(0, -30));

bird->stopAllActions();//让小鸟恢复平稳位置

//执行初始化时的动画播放

bird->runAction(RepeatForever::create(animAc));

//小鸟执行跳动的动作,并通过设置overFlag进行计分

bird->runAction(Sequence::create(CallFunc::create(CC_CALLBACK_0(BirdLayer::setRunFlag1, this)),action,DelayTime::create(0.05 ),CallFunc::create(CC_CALLBACK_0(BirdLayer::setRunFlag2, this)),RotateTo::create(2.0, 90),NULL));

}

return true;

}

开始游戏startGame()

从下面可以看出,点击界面开始游戏后,才进行的管道的初始化,然后执行birdRun(),然后获取定时器,通过定时器,每隔0.05秒更新,执行update_column 和update_bird

void BirdLayer::startGame()

{

Director::getInstance()->setDepthTest(false);

effectNode->runAction(SplitRows::create(0.5f, 30));

initColumn1();

initColumn2();

birdRun();//执行游戏开始后,小鸟的第一次跳动动作,因为后续的touch不在执行stateGame函数

auto director = Director::getInstance();

auto sched = director->getScheduler();

sched->schedule(schedule_selector(BirdLayer::update_column), this, 0.05, false);

sched->schedule(schedule_selector(BirdLayer::update_bird), this, 0.05, false);

}

关于管道和小鸟的更新

判断管道是否移动出当前屏幕可视范围,一旦移出,就remove该精灵,同时生成新的管道精灵

void BirdLayer::update_column(float delta)

{

Point columnPosition1 = columnUnder1->getPosition();

Point columnPosition2 = columnUnder2->getPosition();

Size columnSize = columnUnder1->getContentSize();

if(columnPosition1.x<=-columnSize.width)

{

removeChild(columnUnder1);

removeChild(columnOn1);

initColumn1();

}

if(columnPosition2.x<=-columnSize.width)

{

removeChild(columnUnder2);

removeChild(columnOn2);

initColumn2();

}

}

该游戏的核心就在小鸟的更新函数,这里会更新小鸟的位置,对小鸟和管道进行碰撞检测判断,并对碰撞检测后的结果做出响应。如果检测到碰撞就游戏结束gameOver,如果没有碰撞到,积分就要+1.

这个过程中最重要就是计算小鸟和管道的位置和,各自精灵的size区域

void BirdLayer::update_bird(float delta)

{ //首先判断是否在游戏中,并且小鸟在跳动,如果不是,则让小鸟动起来,就是最开始的阶段

if(gameFlag && !runFlag)

{

birdRun();

}

Point birdPosition = bird->getPosition();

Size birdSize = bird->getContentSize();

Size floorSize = floor->getContentSize();

Point columnPosition1 = columnUnder1->getPosition();

Point columnPosition2 = columnOn1->getPosition();

Point columnPosition3 = columnUnder2->getPosition();

Point columnPosition4 = columnOn2->getPosition();

Size columnSize1 = columnUnder1->getContentSize();

Size columnSize2 = columnOn1->getContentSize();

Size columnSize3 = columnUnder2->getContentSize();

Size columnSize4 = columnOn2->getContentSize();

//如果小鸟的X坐标大于 管道1的X坐标,且column的标志在运动的时候设置为真,就计分,并呈现在计分位置的label上

if(birdPosition.x>columnPosition1.x && columnFlag1)

{

count++;

string num =StringUtils::toString(count);

pLabelAtlas->setString(num);

if(MainLayer::soundFlag)

{

pointSound();

}

columnFlag1 = false;

}

//如果小鸟的X坐标大于 管道2的X坐标,且column的标志在运动的时候设置为真,就计分,并呈现在计分位置的label上

if(birdPosition.x>columnPosition3.x && columnFlag2)

{

count++;

string num =StringUtils::toString(count);

pLabelAtlas->setString(num);

if(MainLayer::soundFlag)

{

pointSound();

}

columnFlag2 = false;

}

//以小鸟当前位置和地板、columnUnder1、columnUnder2、columnOn1、columnOn2

进行碰撞检测

bool check = collision((birdPosition.x – birdSize.width), (birdPosition.y – birdSize.height), (birdPosition.x + birdSize.width), (birdPosition.y + birdSize.height),

0, 0, floorSize.width, floorSize.height);

bool check1 = collision((birdPosition.x – birdSize.width/3), (birdPosition.y – birdSize.height/3), (birdPosition.x + birdSize.width/3), (birdPosition.y + birdSize.height/3),

columnPosition1.x, columnPosition1.y, (columnPosition1.x + columnSize1.width), (columnPosition1.y + columnSize1.height));

bool check2 = collision((birdPosition.x – birdSize.width/3), (birdPosition.y – birdSize.height/3), (birdPosition.x + birdSize.width/3), (birdPosition.y + birdSize.height/3),

columnPosition2.x, columnPosition2.y, (columnPosition2.x + columnSize2.width), (columnPosition2.y + columnSize2.height));

bool check3 = collision((birdPosition.x – birdSize.width/3), (birdPosition.y – birdSize.height/3), (birdPosition.x + birdSize.width/3), (birdPosition.y + birdSize.height/3),

columnPosition3.x, columnPosition3.y, (columnPosition3.x + columnSize3.width), (columnPosition3.y + columnSize3.height));

bool check4 = collision((birdPosition.x – birdSize.width/3), (birdPosition.y – birdSize.height/3), (birdPosition.x + birdSize.width/3), (birdPosition.y + birdSize.height/3),

columnPosition4.x, columnPosition4.y, (columnPosition4.x + columnSize4.width), (columnPosition4.y + columnSize4.height));

if(check || check1 || check2 || check3 || check4)

{//如果检测到碰撞,播放碰撞的音效,并且游戏结束

if(gameFlag)

{

if(MainLayer::soundFlag)

{

hitSound();

}

if(check1 || check2 || check3 || check4)

{

if(MainLayer::soundFlag)

{

dieSound();//播放小鸟死掉的音效

}

}

}

gameOver();//游戏结束

}

}

这里我们详细再讲解一下碰撞检测。看下面的图,碰撞检测实际上先以X坐标区域将碰撞区域划分为左、中、右;然后在以Y坐标分别对上下进行测试,是在上部碰撞,还是下部碰撞。

Cocos2d-x项目实践一FlappyBird-游戏逻辑「终于解决」

static bool collision(double sMinX, double sMinY, double sMaxX, double sMaxY, double gMinX, double gMinY, double gMaxX, double gMaxY)

{

//重叠区域

double xOverlap = 0;

double yOverlap = 0;

//ÖصþÃæ»ý

int impacetArea = 0;

//碰撞检测逻辑,先按水平方向划分左、右、包含,然后分别区分上下碰撞,根据坐标重叠进行计算

if(sMinX<=gMaxX && sMinX>=gMinX && sMaxX>gMaxX)//左边

{

xOverlap = abs(gMaxX – sMinX);

if(sMaxY>gMinY && sMaxY<gMaxY)

{

yOverlap = abs(gMinY – sMaxY);

}else if(sMinY<gMaxY && sMinY>gMinY)

{

yOverlap = abs(gMaxY – sMinY);

}

}else if(sMaxX>=gMinX && sMaxX<=gMaxX && sMinX<=gMinX)//左边

{

xOverlap = abs(gMinX – sMaxX);

//ÉÏ·½Åöײ

if(sMaxY>gMinY && sMaxY<gMaxY)

{

yOverlap = abs(gMinY – sMaxY);

}else if(sMinY<gMaxY && sMinY>gMinY)

{

yOverlap = abs(gMaxY – sMinY);

}

}else if(sMaxX<=gMaxX && sMaxX>=gMinX && sMinX>=gMinX && sMaxX>=gMinX)//中间

{

xOverlap = abs(sMaxX – sMinX);

if(sMaxY>gMinY && sMaxY<gMaxY)

{

yOverlap = abs(gMinY – sMaxY);

}else if(sMinY<gMaxY && sMinY>gMinY)

{

yOverlap = abs(gMaxY – sMinY);

}

}

impacetArea = xOverlap * yOverlap;//计算重叠区域,大于0就表示碰撞到了

if(impacetArea>0)

{

return true;

}

return false;

}

到这里整个FlappyBird的核心过程都讲解完了,在上一节已经将完整代码的百度网盘下载地址分享了,后面我们再以一种更高级的方式,利用Cocos2d中自带的物理刚体、碰撞来重写这个游戏,你会发现比这个简单很多

零基础学习Cocos2d-x开发——游戏引擎基本框架

Cocos2dx项目实践-FlappyBird新建项目

Cocos2d-x项目实践一FlappyBird-场景管理

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/17568.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信