cocos2dx实例开发之flappybird(入门版)
cocos2dx社区里有个系列博客完整地复制原版flappybird的全部特性。只是那个代码写得比較复杂,新手学习起来有点捉摸不透,这里我写了个简单的版本号。演演示样例如以下:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjIzNDExNQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
创建项目
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjIzNDExNQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
游戏设计
游戏结构例如以下,游戏包括预载入场景和主场景,主场景中包括背景、小鸟、管道和各种UI界面。
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjIzNDExNQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
开发步骤
1,素材收集
//加入载入回调函数,用异步载入纹理
Director::getInstance()->getTextureCache()->addImageAsync("game.png", CC_CALLBACK_1(LoadingScene::loadingCallBack, this));
void LoadingScene::loadingCallBack(Texture2D *texture)
{
//预载入帧缓存纹理
SpriteFrameCache::getInstance()->addSpriteFramesWithFile("game.plist", texture);
//预载入帧动画
auto birdAnimation = Animation::create();
birdAnimation->setDelayPerUnit(0.2f);
birdAnimation->addSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("bird1.png"));
birdAnimation->addSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("bird2.png"));
birdAnimation->addSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("bird3.png"));
AnimationCache::getInstance()->addAnimation(birdAnimation, "birdAnimation"); //将小鸟动画加入到动画缓存
//预载入音效
SimpleAudioEngine::getInstance()->preloadEffect("die.mp3");
SimpleAudioEngine::getInstance()->preloadEffect("hit.mp3");
SimpleAudioEngine::getInstance()->preloadEffect("point.mp3");
SimpleAudioEngine::getInstance()->preloadEffect("swooshing.mp3");
SimpleAudioEngine::getInstance()->preloadEffect("wing.mp3"); //载入完成跳转到游戏场景
auto gameScene = GameScene::createScene();
TransitionScene *transition = TransitionFade::create(0.5f, gameScene);
Director::getInstance()->replaceScene(transition);
}
3,游戏主场景
//加入游戏背景
Sprite *backGround = Sprite::createWithSpriteFrameName("bg.png");
backGround->setPosition(visibleOrigin.x + visibleSize.width / 2, visibleOrigin.y + visibleSize.height / 2);
this->addChild(backGround);
//logo
auto gameLogo = Sprite::createWithSpriteFrameName("bird_logo.png");
gameLogo->setPosition(visibleOrigin.x + visibleSize.width / 2, visibleOrigin.y + visibleSize.height / 2+100);
gameLogo->setName("logo");
this->addChild(gameLogo);
logo在游戏開始后要隐藏掉。
//小鸟
birdSprite = Sprite::create();
birdSprite->setPosition(visibleOrigin.x + visibleSize.width / 3, visibleOrigin.y + visibleSize.height / 2);
this->addChild(birdSprite);
auto birdAnim = Animate::create(AnimationCache::getInstance()->animationByName("birdAnimation"));
birdSprite->runAction(RepeatForever::create(birdAnim)); //挥翅动画
auto up = MoveBy::create(0.4f, Point(0, 8));
auto upBack = up->reverse();
if (gameStatus == GAME_READY)
{
swingAction = RepeatForever::create(Sequence::create(up, upBack, NULL));
birdSprite->runAction(swingAction); //上下晃动动画
}
在准备界面下除了有扇翅膀的动作,还有上下浮动的动作。
3.3,地板
须要用到自己定义调度器,注意调节移动速度。
//加入两个land
land1 = Sprite::createWithSpriteFrameName("land.png");
land1->setAnchorPoint(Point::ZERO);
land1->setPosition(Point::ZERO);
this->addChild(land1, 10); //置于最顶层
land2 = Sprite::createWithSpriteFrameName("land.png");
land2->setAnchorPoint(Point::ZERO);
land2->setPosition(Point::ZERO);
this->addChild(land2, 10);
Size visibleSize = Director::getInstance()->getVisibleSize();
//两个图片循环移动
land1->setPositionX(land1->getPositionX() - 1.0f);
land2->setPositionX(land1->getPositionX() + land1->getContentSize().width - 2.0f);
if (land2->getPositionX() <= 0)
land1->setPosition(Point::ZERO);
3.4,水管
//同屏幕出现的仅仅有两根管子。放到容器里面,上下绑定为一根
for (int i = 0; i < 2; i++)
{
auto visibleSize = Director::getInstance()->getVisibleSize();
Sprite *pipeUp = Sprite::createWithSpriteFrameName("pipe_up.png");
Sprite *pipeDown = Sprite::createWithSpriteFrameName("pipe_down.png");
Node *singlePipe = Node::create();
//给上管绑定刚体
auto pipeUpBody = PhysicsBody::createBox(pipeUp->getContentSize());
pipeUpBody->setDynamic(false);
pipeUpBody->setContactTestBitmask(1);
pipeUp->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
pipeUp->setPhysicsBody(pipeUpBody);
//给两个管子分开设置刚体。能够留出中间的空隙使得小鸟通过
//给下管绑定刚体
auto pipeDownBody = PhysicsBody::createBox(pipeDown->getContentSize());
pipeDownBody->setDynamic(false);
pipeDownBody->setContactTestBitmask(1);
pipeDown->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
pipeDown->setPhysicsBody(pipeDownBody); pipeUp->setPosition(0, PIPE_HEIGHT + PIPE_SPACE);
singlePipe->addChild(pipeUp);
singlePipe->addChild(pipeDown); //pipeDown默认加到(0,0),上下合并,此时singlePipe以以下的管子中心为锚点
singlePipe->setPosition(i*PIPE_INTERVAL + WAIT_DISTANCE, getRandomHeight() ); //设置初始高度
singlePipe->setName("newPipe");
this->addChild(singlePipe); //把两个管子都增加到层
pipes.pushBack(singlePipe); //两个管子先后增加到容器
}
//管子滚动
for (auto &singlePipe : pipes)
{
singlePipe->setPositionX(singlePipe->getPositionX() - 1.0f);
if (singlePipe->getPositionX() < -PIPE_WIDTH/2)
{
singlePipe->setPositionX(visibleSize.width+PIPE_WIDTH/2);
singlePipe->setPositionY(getRandomHeight());
singlePipe->setName("newPipe"); //每次重设一根管子,标为new
}
}
3.5。增加物理世界
gameScene->getPhysicsWorld()->setGravity(Vec2(0, -900)); //设置重力场,重力加速度能够依据手感改小点
gameLayer->setPhysicWorld(gameScene->getPhysicsWorld()); //绑定物理世界
小鸟绑定刚体
//小鸟绑定刚体
auto birdBody = PhysicsBody::createCircle(BIRD_RADIUS); //将小鸟当成一个圆,懒得弄精确的轮廓线了
birdBody->setDynamic(true); //设置为能够被物理场所作用而动作
birdBody->setContactTestBitmask(1); //必须设置这项为1才干检測到不同的物体碰撞
birdBody->setGravityEnable(false); //设置是否被重力影响,准备画面中不受重力影响
birdSprite->setPhysicsBody(birdBody); //为小鸟设置刚体
地板绑定刚体
//设置地板刚体
Node *groundNode = Node::create();
auto groundBody = PhysicsBody::createBox(Size(visibleSize.width, land1->getContentSize().height));
groundBody->setDynamic(false);
groundBody->setContactTestBitmask(1);
groundNode->setAnchorPoint(Vec2::ANCHOR_MIDDLE); //物理引擎中的刚体仅仅同意结点锚点设置为中心
groundNode->setPhysicsBody(groundBody);
groundNode->setPosition(visibleOrigin.x+visibleSize.width/2,land1->getContentSize().height/2);
this->addChild(groundNode);
管道设置刚体,上下半根分别设置,留出中间的缝隙
//给上管绑定刚体
auto pipeUpBody = PhysicsBody::createBox(pipeUp->getContentSize());
pipeUpBody->setDynamic(false);
pipeUpBody->setContactTestBitmask(1);
pipeUp->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
pipeUp->setPhysicsBody(pipeUpBody);
//给两个管子分开设置刚体,能够留出中间的空隙使得小鸟通过
//给下管绑定刚体
auto pipeDownBody = PhysicsBody::createBox(pipeDown->getContentSize());
pipeDownBody->setDynamic(false);
pipeDownBody->setContactTestBitmask(1);
pipeDown->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
pipeDown->setPhysicsBody(pipeDownBody);
碰撞检測
//加入碰撞监測
auto contactListener = EventListenerPhysicsContact::create();
contactListener->onContactBegin = CC_CALLBACK_1(GameScene::onContactBegin, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);
//碰撞监測
bool GameScene::onContactBegin(const PhysicsContact& contact)
{
if (gameStatus == GAME_OVER) //当游戏结束后不再监控碰撞
return false; gameOver();
return true;
}
3.6,触摸检測
//触摸监听
bool GameScene::onTouchBegan(Touch *touch, Event *event)
3.7。控制小鸟
birdSprite->getPhysicsBody()->setVelocity(Vec2(0, 250)); //给一个向上的初速度
//小鸟的旋转
auto curVelocity = birdSprite->getPhysicsBody()->getVelocity();
birdSprite->setRotation(-curVelocity.y*0.1 - 20); //依据竖直方向的速度算出旋转角度。逆时针为负
3.8,游戏開始
//游戏開始
void GameScene::gameStart()
{
gameStatus = GAME_START;
score = 0;//重置分数
scoreLabel->setString(String::createWithFormat("%d", score)->getCString());
this->getChildByName("logo")->setVisible(false); //logo消失
scoreLabel->setVisible(true); //计分開始
this->scheduleUpdate();//启动默认更新
this->schedule(schedule_selector(GameScene::scrollLand), 0.01f); //启动管子和地板滚动
birdSprite->stopAction(swingAction); //游戏開始后停止上下浮动
birdSprite->getPhysicsBody()->setGravityEnable(true); //開始受重力作用
}
3.9,计分和数据存储
//当游戏開始时,推断得分,这个事实上也能够写在其它地方。比方管子滚动的更新函数里面或者触摸监測里面
if (gameStatus == GAME_START)
{
for (auto &pipe : pipes)
{
if (pipe->getName() == "newPipe") //新来一根管子就推断
{
if (pipe->getPositionX() < birdSprite->getPositionX())
{
score++;
scoreLabel->setString(String::createWithFormat("%d", score)->getCString());
SimpleAudioEngine::getInstance()->playEffect("point.mp3");
pipe->setName("passed"); //标记已经过掉的管子
}
}
}
}
4.0。游戏结束
//游戏结束
void GameScene::gameOver()
{
gameStatus = GAME_OVER;
//获取历史数据
bestScore = UserDefault::getInstance()->getIntegerForKey("BEST");
if (score > bestScore)
{
bestScore = score; //更新最好分数
UserDefault::getInstance()->setIntegerForKey("BEST", bestScore);
} SimpleAudioEngine::getInstance()->playEffect("hit.mp3");
//游戏结束后停止地板和管道的滚动
this->unschedule(schedule_selector(GameScene::scrollLand));
}
结束后比較当前分数和历史分数。以便更新。
SimpleAudioEngine::getInstance()->playEffect("hit.mp3");
4.2,记分板
//加入记分板和重玩菜单
void GameScene::gamePanelAppear()
{
Size size = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
//用node将gameoverlogo和记分板绑在一起
Node *gameOverPanelNode = Node::create();
auto gameOverLabel = Sprite::createWithSpriteFrameName("gameover.png");
gameOverPanelNode->addChild(gameOverLabel);
auto panel = Sprite::createWithSpriteFrameName("board.PNG");//注意这里是大写PNG。原图片用什么后缀这里就用什么,区分大写和小写
gameOverLabel->setPositionY(panel->getContentSize().height); //设置一下坐标
gameOverPanelNode->addChild(panel);
//记分板上加入两个分数
auto curScoreTTF = LabelTTF::create(String::createWithFormat("%d", score)->getCString(), "Arial", 20);
curScoreTTF->setPosition(panel->getContentSize().width-40, panel->getContentSize().height-45);
curScoreTTF->setColor(Color3B(255, 0, 0));
panel->addChild(curScoreTTF);
auto bestScoreTTF = LabelTTF::create(String::createWithFormat("%d", bestScore)->getCString(), "Arial", 20);
bestScoreTTF->setPosition(panel->getContentSize().width - 40, panel->getContentSize().height - 90);
bestScoreTTF->setColor(Color3B(0, 255, 0));
panel->addChild(bestScoreTTF);
this->addChild(gameOverPanelNode);
gameOverPanelNode->setPosition(origin.x + size.width / 2, origin.y + size.height );
//滑入动画
gameOverPanelNode->runAction(MoveTo::create(0.5f, Vec2(origin.x + size.width / 2, origin.y + size.height / 2)));
SimpleAudioEngine::getInstance()->playEffect("swooshing.mp3");
//加入菜单
MenuItemImage *restartItem = MenuItemImage::create("start_btn.png", "start_btn_pressed.png", this,menu_selector(GameScene::gameRetart));
auto menu = CCMenu::createWithItem(restartItem);
menu->setPosition(origin.x + size.width / 2, 150);
this->addChild(menu);
}
//游戏又一次開始
void GameScene::gameRetart(Ref *sender)
{
//又一次回到初始画面
auto gameScene = GameScene::createScene();
Director::getInstance()->replaceScene(gameScene); //这里懒得加特效了,直接转场
}
效果图:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjIzNDExNQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjIzNDExNQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
源码
cocos2dx实例开发之flappybird(入门版)的更多相关文章
- Cocos2d-X游戏开发之Windows7+VS2010环境搭建(亲测)
Cocos2d—X游戏引擎,提供Mac系统下的Xcode开发和Windows系统的VS开发,应该是比较常用的2种. 使用Mac以后,就会发现使用Xcode开发实在是福分啊.VS开发步骤繁琐,调试效率低 ...
- Cocos2d-x游戏开发之lua编辑器 Sublime 搭建,集成cocos2dLuaApi和自有类
版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/wisdom605768292/article/details/34085969 Sublime Te ...
- 前端开发之CSS入门篇
一.CSS介绍和语法 二.CSS引入方式 三.基本选择器 四.高级选择器 五.伪类选择器 六.伪元素选择器 1️⃣ CSS介绍和语法 1. CSS的介绍 (1)为什么需要CSS? 使用css的目的就 ...
- Cocos2d-x游戏开发之luaproject创建
操作系统:OS X 10.85 Cocos2d-x 版本号: 2.2.1 使用Cocos2d-x 能够创建luaproject,已经使用cpp创建的project也能够继承lua进行开发,可是lua并 ...
- 安卓开发之ListView入门
<!--这个地方最好用match_parent 这样效率高--> <ListView android:layout_width="match_parent" an ...
- Cocos2d-x 3.x游戏开发之旅
Cocos2d-x 3.x游戏开发之旅 钟迪龙 著 ISBN 978-7-121-24276-2 2014年10月出版 定价:79.00元 516页 16开 内容提要 <Cocos2d-x ...
- UWP开发之Template10实践:本地文件与照相机文件操作的MVVM实例(图文付原代码)
前面[UWP开发之Mvvmlight实践五:SuspensionManager中断挂起以及复原处理]章节已经提到过Template10,为了认识MvvmLight的区别特做了此实例. 原代码地址:ht ...
- Android驱动开发之Hello实例
Android驱动开发之Hello实例: 驱动部分 modified: kernel/arch/arm/configs/msm8909-1gb_w100_hd720p-perf_defconf ...
- python开发之路:python数据类型(老王版)
python开发之路:python数据类型 你辞职当了某类似微博的社交网站的底层python开发主管,官还算高. 一次老板让你编写一个登陆的程序.咔嚓,编出来了.执行一看,我的妈,报错? 这次你又让媳 ...
随机推荐
- BFS寻路算法的实现
关于BFS的相关知识由于水平有限就不多说了,感兴趣的可以自己去wiki或者其他地方查阅资料. 这里大概说一下BFS寻路的思路,或者个人对BFS的理解: 大家知道Astar的一个显著特点是带有启发函数, ...
- 自己封装的Socket组件,实现服务端多进程共享Socket对象,协同处理客户端请求
DotNet.Net.MySocket是SLB.NET(Server Load Balance服务器负载均衡)项目中的核心组件. 在实际的项目中发现,单进程的服务端处理高并发的客户请求能力有限. 所以 ...
- something: 重构、正则、vim -- clwu
项目需要做一个db table 操作的小工具. 从phpMyAdmin上拷贝了一些代码过来修改,但我有没有足够的时间把所有拷贝过来的代码都重构修改和测试完,于是希望后面接手的同事在需要修改这些代码时能 ...
- MoveTo和MoveBy
cc.MoveTo是“移动到这里",而cc.MoveBy则是“相对于之前点再移动”,通俗一点就是说这里需要两个坐标pos1(x1,y1),pos2(x2,y2). 如果是cc.MoveTo的 ...
- 一些推荐的spark/hadoop课程
为了分享给你们,也为自己. 感谢下面的老师们! 1.王家林DT大数据梦工厂的大数据IMF传奇行动课程 总的目录是: 第一阶段:Linux和Java零基础企业级实战 第二阶段:Hadoop和Hive零基 ...
- 两年内从零到每月十亿 PV 的发展来谈 Pinterest 的架构设计(转)
原文:Scaling Pinterest - From 0 To 10s Of Billions Of Page Views A Month In Two Years 译文:两年内从零到每月十亿 PV ...
- ASP.NET自定义控件入门Demo
最近看了MSDN关于自定义控件的介绍,根据官方的文档,自己学着做了一个简单的Demo给需要的朋友参考. ASP.NET 源生的TextBox是不带Label标签的,这里我要实现的是创建一个带Label ...
- VoHelper
VoHelper package com.isoftstone.pcis.policy.core.helper; import com.isoftstone.fwk.dao.CommonDao; im ...
- Codeforces 588E. A Simple Task (线段树+计数排序思想)
题目链接:http://codeforces.com/contest/558/problem/E 题意:有一串字符串,有两个操作:1操作是将l到r的字符串升序排序,0操作是降序排序. 题解:建立26棵 ...
- C#.NET 消息机制
一.消息概述 众人周知,window系统是一个消息驱动的系统, windows操作系统本身有自己的消息队列,消息循环,它捕捉键盘,鼠标的动作生成消息,并将这个消息传给应用程序的消息队列. 余下的工作有 ...