cocos2d-x小游戏——飞机大战

前端之家收集整理的这篇文章主要介绍了cocos2d-x小游戏——飞机大战前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

上周,我做了一个基于 cocos2d-x 的飞机大战的游戏,因为我刚学cocos2d-x没多久,所以这个飞机大战很多都是看着别人的教程,再加上自己的一些想法,来做的。

下面我想说一说我的思路。

飞机大战有三个场景:

  1. LayerGameStart(游戏开始)
  2. LayerGameMain(游戏进行中)
  3. LayerGameOver(游戏结束)

一、游戏开始场景(LayerGameStart)

其中,游戏开始和游戏结束是比较简单的,那我就先从简单的说起,
首先说下游戏开始场景。我们在这个场景里面只需要做一下工作:

  • 预加载一些资源(声音,图片缓存,这些资源都是全局的)
  • 注意要将同一类资源放在一起,便于管理
  • 检测游戏游戏的本地存数数据中是否有游戏最高分(UserDefault

在这个场景,我只想说下如果检测游戏的最高分
具体实现如下:

  1. //判断分数是否已经被存储
  2. bool LayerGameStart::isSaveFile()
  3. {
  4. //用一个bool值作为标志,如果有则表示分数已经被存储
  5. if (!UserDefault::getInstance()->getBoolForKey("isSaveFileXml"))
  6. {
  7. //如果没有就设置标志并置为真
  8. UserDefault::getInstance()->setBoolForKey("isSaveFileXml",true);
  9. //设置最高分,默认值为0
  10. UserDefault::getInstance()->setIntegerForKey("Hightestscore",0);
  11. ////flush的作用是将数据写入到xml文件中。flush()在windows下是空的。。。呵呵。。。
  12. UserDefault::getInstance()->flush();
  13. return false;
  14. }
  15. else
  16. return true;
  17. }
  18. void LayerGameStart::getHightestscore()
  19. {
  20. if (isSaveFile())
  21. {
  22. //在这里设置历史最高得分
  23. LayerGameOver::_hightestscore =
  24. UserDefault::getInstance()->getIntegerForKey("Hightestscore",0);
  25. }
  26. }

然后只需要将 getHightestscore( ) 函数放在 LayerGameStartinit( ) 函数中即可。


@H_403_123@二、游戏结束场景(LayerGameOver)

游戏结束场景也很容易,主要实现下面的功能

  • 显示本局游戏的得分
  • 显示历史最高分
  • 设置“返回游戏”按钮,和“退出”按钮

其中显示本局所得分数,也是比较好实现的,需要注意的是,如何将主场景(LayerGameMain)中的分数传递到游戏结束场景中去。做法如下:

  1. static LayerGameOver * create(int score);
  2. static cocos2d::Scene * scene(int score);
  3. bool init(int score);

在创建场景时将分数传入,即在 init( ) 函数中传入分数,因为 init 在 create 有被调用,而 create 函数又在 scene 函数中被调用,所以这三个函数都有参数了。在切换场景的时候直接将分数传递过来就行。

然后就是显示历史最高分,显示历史最高分需要在游戏结束场景的 class 中添加一个静态成员变量

  1. static int _hightestscore;//用于存到本地,记得要在class外进行初始化!
  2. cocos2d::Label * hightestscore;//用于显示

具体实现如下:

  1. //显示历史最高分
  2. Value strHightestscore(_hightestscore);
  3. hightestscore = Label::createWithBMFont("font/font.fnt",strHightestscore.asString());
  4. hightestscore->setColor(Color3B(30,50,240));
  5. hightestscore->setAnchorPoint(Point::ANCHOR_MIDDLE_LEFT);
  6. hightestscore->setPosition(Point(150,winSize.height-40));
  7. this->addChild(hightestscore);

返回和退出按钮就很简单了,就是设置两个图片作为按钮 (MenuItemSpritebackItemexitItem 然后将这两个精灵添加Menu 中去:Menu * menu = Menu::create(backItem,exitItem,nullptr); 这两个按钮点击的回调函数也很简单,返回就是切换到游戏开始场景,退出就是直接退出程序 exit(1);


三、游戏主场景

游戏主场景就是最重要也最难的,主要有一下功能

  • 添加游戏背景并让游戏背景滚动起来

  • 添加玩家飞机
    1.飞机的动作行为(闪三次后播放自身帧动画,要方便控制,扩大其BoundingBox
    2.飞机不能飞出屏幕
    3.由于其他层也需要飞机的位置,为了方便其他层获得飞机,将其设计为单例getInstance( )
    4.玩家飞机自身爆炸的动画(因为爆炸后要切换到游戏结束场景,所以要在这里将本局游戏得分传到结束场景)

  • 添加子弹
    1.首先得拿到玩家飞机的位置(因为子弹是从玩家飞机发出的)
    2.设置定时器来产生子弹
    3.让子弹飞~
    4.子弹的行为:与敌机碰撞或者什么也没碰撞到——飞出屏幕外
    5.将子弹放在一个容器里面,便于碰撞检测
    6.不只有单发子弹还有多发子弹(MultiBullets

  • 添加敌机
    1.首先有一个敌机类Enemy,用于产生所有敌机(小敌机,中敌机,打敌机)
    2.敌机产生的位置(屏幕上方,随机产生)
    3.敌机消失在屏幕下方
    4.与子弹和玩家飞机发生碰撞
    5.敌机爆炸动画,玩家得分。

  • 添加道具
    1.在玩游戏时,会用道具出现(从屏幕上方,随机出现)
    2.道具有两种:炸弹和双发子弹(bigBoom,multiBullets)
    3.道具行为:与玩家飞机碰撞或者什么也没碰到——掉到屏幕外
    4.炸弹可以让当前屏幕中所有敌机爆
    5.双发子弹增加玩家飞机的威力(第一次吃到会变成双发子弹,以后再吃到不会再加子弹而是直接给玩家加100分)

  • 添加控制层
    1.更新玩家得分
    2.实现游戏的暂停和继续的功能添加屏蔽层)

怎么样,头晕了吗?要加这么多层,实现这么多功能。。。还有一些我没写(一时想不起来)
我也不准备,每个都详细说明了,我就说说实现这些功能需要注意的地方吧,也是我觉得比较难的地方。。。

1. 如何实现屏幕滚动:

我在这里其实只用了一个背景图,但是把它加载了两次,然后让两个图片一起向下移动,具体过程请看下图

代码实现如下:

  1. //添加背景
  2. void LayerGameMain::addBackground()
  3. {
  4. SimpleAudioEngine::getInstance()->playBackgroundMusic("sound/game_music.wav",true);
  5.  
  6. auto bg1 = Sprite::createWithSpriteFrameName("background.png");
  7. bg1->setTag(BG1);
  8. bg1->setAnchorPoint(Point::ZERO);
  9. bg1->setPosition(Point(0,0));
  10. this->addChild(bg1);
  11.  
  12. auto bg2 = Sprite::createWithSpriteFrameName("background.png");
  13. bg2->setTag(BG2);
  14. bg2->setAnchorPoint(Point::ZERO);
  15. bg2->setPosition(0,bg1->getContentSize().height - 5);//为了不留空隙
  16. this->addChild(bg2);
  17.  
  18. //利用帧循环来实现背景滚动
  19. this->schedule(schedule_selector(LayerGameMain::movingBackgroundCallback),0.01f);
  20. }
  21.  
  22. //使得背景滚动起来
  23. void LayerGameMain::movingBackgroundCallback(float dt)
  24. {
  25. Sprite * bg1 = (Sprite *)this->getChildByTag(BG1);
  26. Sprite * bg2 = (Sprite *)this->getChildByTag(BG2);
  27.  
  28. bg1->setPositionY(bg1->getPositionY() - 2);//每个循环下移2个像素
  29. bg2->setPositionY(bg1->getPositionY() + bg2->getContentSize().height - 2);
  30.  
  31. if (bg2->getPositionY() < 0)
  32. {
  33. bg1->setPositionY(0);//重置背景
  34. }
  35. }

2. 如何将玩家飞机设计为单例?
cocos2d中很多类都有获得单例的函数getInstance( ),这里我也写了这么一个函数来得到飞机单例

  1. class MyPlane : public cocos2d::Sprite
  2. {
  3. /*省略部分代码*/
  4. //将飞机设计成全局的
  5. static MyPlane * getInstance();
  6. static MyPlane * _splane;
  7. }
  8.  
  9. //初始化
  10. MyPlane * MyPlane::_splane = nullptr;
  11.  
  12. MyPlane * MyPlane::getInstance()
  13. {
  14. if (!_splane)
  15. {
  16. _splane = new MyPlane();
  17. if (_splane && _splane->init())
  18. {
  19. //不将其挂到渲染树上,让飞机的生命周期跟场景一样
  20. //_splane->autorelease();
  21. }
  22. }
  23. return _splane;//return 在if语句外面
  24. }

还有就是玩家飞机爆炸的函数,需要传入飞机爆炸之前得到的分数。好让游戏结束场景能得到分数。

3. 子弹层的设计
因为玩家的飞机是不断移动的,然后子弹的动作都是 MoveTo ,其 create 函数只有时间变量,我们如何使得子弹的速度是一样的呢?很简单,根据 v = s / t;若想要速度一样则在距离不同的情况下,就必须改变子弹运行的时间,所以只要给不同位置发出的子弹不同的时间,就可以使得子弹的速度一样。具体实现如下:

  1. //得到子弹到屏幕上边沿的距离
  2. float distance =
  3. winSize.height - plane->getPositionY() - plane->getBoundingBox().size.height/2;
  4. //确定子弹的速度 一秒跨越800个像素。
  5. float velocity = 800/1;
  6. //根据距离和速率求得时间
  7. float movedt = distance / velocity;
  8. //子弹在movedt的时间内移动到屏幕上边沿之外的地方(加上的 bullet->getContentSize().height 就是超出屏幕的距离)
  9. MoveTo * to = MoveTo::create(movedt,Point(birthPlace.x,winSize.height + bullet->getContentSize().height));

4. 敌机类的设计
这个不说了直接看代码代码都有注释的
Enemy.h:

  1. #ifndef __Enemy_H_
  2. #define __Enemy_H_
  3. #include "cocos2d.h"
  4.  
  5. class Enemy : public cocos2d::Node
  6. {
  7. public:
  8. //构造器
  9. Enemy();
  10. //析构器
  11. ~Enemy();
  12. //创建敌机
  13. static Enemy * create();
  14. //将敌机与其对应的Sprite(图片)和生命值绑定(有三类敌机)
  15. void bindEnemySprite(cocos2d::Sprite * spr,int life);
  16. //得到敌机
  17. cocos2d::Sprite * getSprite();
  18. //得到生命值
  19. int getLife();
  20. //失去生命值
  21. void loseLife();
  22. //得到敌机在世界坐标内的的位置和尺寸大小boundingBox
  23. cocos2d::Rect Get_BoundingBox();
  24.  
  25. private:
  26. cocos2d::Sprite * _sprite;
  27. int _life;
  28. };
  29.  
  30. #endif

Enemy.cpp

  1. #include "Enemy.h"
  2. USING_NS_CC;
  3.  
  4. Enemy::Enemy()
  5. {
  6. //在构造函数中初始化,其实也可以在init函数中初始化,但这里没有init函数
  7. _sprite = nullptr;
  8. _life = 0;
  9. }
  10. Enemy::~Enemy()
  11. {
  12.  
  13. }
  14. Enemy * Enemy::create()
  15. {
  16. Enemy * pRect = new Enemy();
  17. if (pRect != nullptr)
  18. {
  19. pRect->autorelease();
  20. return pRect;
  21. }
  22. else
  23. return nullptr;
  24. }
  25. //绑定敌机,不同的敌机有不同的图片,不同的生命值
  26. void Enemy::bindEnemySprite(cocos2d::Sprite * spr,int life)
  27. {
  28. _sprite = spr;
  29. _life = life;
  30. //将_sprite加到 pRect 上!!pRect 实质就是一个Node
  31. this->addChild(_sprite);
  32. }
  33. Sprite * Enemy::getSprite()
  34. {
  35. return _sprite;
  36. }
  37. int Enemy::getLife()
  38. {
  39. return _life;
  40. }
  41. void Enemy::loseLife()
  42. {
  43. _life--;
  44. }
  45. //自定义的getBoundingBox函数,便于主场景中的碰撞检测
  46. Rect Enemy::Get_BoundingBox()
  47. {
  48. Rect rect = _sprite->getBoundingBox();
  49. //本来敌机是加到pRect上的它的坐标是相对于pRect的
  50. //这里将敌机的坐标转换为世界坐标
  51. Point position = this->convertToWorldSpace(rect.origin);
  52. //这里只需要知道敌机的起始坐标,因为敌机的宽度和长度是不会改变的
  53. Rect enemyRect = Rect(position.x,position.y,rect.size.width,rect.size.height);
  54. return enemyRect;
  55. }

5.有了敌机类,就要将敌机添加到主场景中去(LayerEnemy)
因为要加3类敌机,其实每一类敌机的添加方法都一样,只不过他们的图片,生命值,出场概率,被击毁后玩家所得的分数不相同罢了。在这里就将添加小敌机的方法说一下,中敌机和大敌机都一样。

  1. //小敌机更新函数(在定时器里面调用
  2. void addSmallEnemyCallback(float dt);
  3. //小敌机移动完成后(没有碰撞)
  4. void smallEnemyMoveFinished(cocos2d::Node * node);
  5. //小敌机爆炸
  6. void smallEnemyBlowup(Enemy * smallEnemy);
  7. //移除小敌机
  8. void removeSmallEnemy(cocos2d::Node * target,void * data);
  9. //移除所有小敌机
  10. void removeAllSmallEnemy();
  11.  
  12. //容器,用来存放所有小敌机,便于碰撞检测
  13. cocos2d::Vector<Enemy *> _smallVec;

实现函数

  1. //添加敌机的回调函数(在帧循环里面调用
  2. void LayerEnemy::addSmallEnemyCallback(float dt)
  3. {
  4. Enemy * smallEnemy = Enemy::create();
  5. //绑定
  6. smallEnemy->bindEnemySprite(Sprite::createWithSpriteFrameName("enemy1.png"),SMALL_MAXLIFE);
  7. //加到smallVec中
  8. _smallVec.pushBack(smallEnemy);
  9. //确定敌机的坐标:横坐标x是一个随机
  10. //smallEnemy->Get_BoundingBox().size.width/2 < x < winSize.width - smallEnemy->Get_BoundingBox().size.width/2
  11. //注意:这里要使用 Enemy 类里面的Get_BoundingBox() 函数
  12. float x = CCRANDOM_0_1()*(winSize.width - 2*smallEnemy->Get_BoundingBox().size.width) +
  13. smallEnemy->Get_BoundingBox().size.width/2;
  14. float y = winSize.height + smallEnemy->Get_BoundingBox().size.height/2;
  15. Point smallBirth = Point(x,y);
  16. //设置坐标
  17. smallEnemy->setPosition(smallBirth);
  18. this->addChild(smallEnemy);
  19.  
  20. MoveTo * to = MoveTo::create(3,Point(smallBirth.x,smallBirth.y -
  21. winSize.height - smallEnemy->Get_BoundingBox().size.height));
  22. CallFuncN * actionDone = CallFuncN::create(this,callfuncN_selector(LayerEnemy::smallEnemyMoveFinished));
  23. Sequence * sequence = Sequence::create(to,actionDone,NULL);
  24. smallEnemy->runAction(sequence);
  25. }
  26.  
  27. //敌机爆炸的函数
  28. void LayerEnemy::smallEnemyBlowup(Enemy * smallEnemy)
  29. {
  30. SimpleAudioEngine::getInstance()->playEffect("sound/enemy1_down.wav");
  31. Animate * smallAnimate =
  32. Animate::create(AnimationCache::getInstance()->animationByName("smallBlowup"));
  33.  
  34.  
  35. /*利用 CallFuncN 来完成 CallFuncND 的功能 !! 注意这里(我花了很长时间才解决请看http://blog.csdn.net/Crayondeng/article/details/18767407)*/
  36.  
  37. auto actionDone =
  38. CallFuncN::create(CC_CALLBACK_1(LayerEnemy::removeSmallEnemy,this,smallEnemy));
  39. Sequence * sequence = Sequence::create(smallAnimate,NULL);
  40. smallEnemy->getSprite()->runAction(sequence);//这么写可以吗? smallEnemy->runAction(sequence)不行!
  41. }
  42.  
  43. //这是没有碰撞的remove
  44. void LayerEnemy::smallEnemyMoveFinished(cocos2d::Node * node)
  45. {
  46. Enemy * smallEnemy = (Enemy *)node;
  47. this->removeChild(smallEnemy,true);
  48. _smallVec.eraSEObject(smallEnemy);
  49. //node->removeAllChildrenWithCleanup(true);
  50. }
  51.  
  52. //这是碰撞之后的remove
  53. void LayerEnemy::removeSmallEnemy(cocos2d::Node * target,void * data)
  54. {
  55. Enemy * smallEnemy = (Enemy *)data;
  56. if (smallEnemy)
  57. {
  58. _smallVec.eraSEObject(smallEnemy);
  59. smallEnemy->removeFromParentAndCleanup(true);//和这句等效:this->removeChild(smallEnemy,true);
  60. }
  61.  
  62. }
  63.  
  64. //去掉所有小敌机
  65. void LayerEnemy::removeAllSmallEnemy()
  66. {
  67. for (auto node : _smallVec)
  68. {
  69. Enemy * enemy = (Enemy *)node;
  70. if (enemy->getLife() > 0)
  71. {
  72. this->smallEnemyBlowup(enemy);
  73. }
  74. }
  75. }

6. 然后就添加道具层

道具有两种,一个是大炸弹,一个是双发子弹。它们产生的位置都是在屏幕上方,随机产生。
这里主要说一说炸弹,因为炸弹是可以点击的,一点击后,当前屏幕的所有敌机都会爆炸。炸弹的数量减一。所以炸弹需要在主场景的帧循环中不断检测,用一个容器来存放炸弹,玩家飞机一吃到炸弹道具,就更新炸弹数

  1. void LayerGameMain::updateBigBoomCount(int bigBoomCount)
  2. {
  3. String strBoomCount;//用来显示炸弹的数量
  4. Sprite * norBoom = Sprite::createWithSpriteFrameName("bomb.png");//正常的图片
  5. Sprite * selBoom = Sprite::createWithSpriteFrameName("bomb.png");//选择的图片
  6. if (bigBoomCount < 0)//如果小于0
  7. {
  8. return;//则什么也不做
  9. }
  10. else if (bigBoomCount == 0)//如果炸弹数等于0
  11. {
  12. if (this->getChildByTag(TAG_BIGBOOM))//在主场景里检查是否有炸弹图标
  13. {
  14. this->removeChildByTag(TAG_BIGBOOM,true);//如果有,就将其删除
  15. }
  16. if (this->getChildByTag(TAG_BIGBOOMCOUNT))//在主场景里面检查是否有炸弹数字标签
  17. {
  18. this->removeChildByTag(TAG_BIGBOOMCOUNT,true);//如果有,则删除
  19. }
  20. }
  21. else if (bigBoomCount == 1)//如果炸弹数等于1
  22. {
  23. if ( !(this->getChildByTag(TAG_BIGBOOM)) )//检查是否有炸弹图标
  24. {
  25. //如果没有,就添加一个炸弹图标(其实是一个菜单项)
  26. MenuItemSprite * boomItem = MenuItemSprite::create(norBoom,selBoom,CC_CALLBACK_1(LayerGameMain::boomMenuCallback,this));
  27. boomItem->setPosition(norBoom->getContentSize().width/2,norBoom->getContentSize().height/2);
  28. Menu * boomMenu = Menu::create(boomItem,nullptr);
  29. boomMenu->setPosition(Point::ZERO);
  30. this->addChild(boomMenu,0,TAG_BIGBOOM);
  31. }
  32. if ( !(this->getChildByTag(TAG_BIGBOOMCOUNT)) )//检查是否有炸弹数字标签
  33. {
  34. //如果没有,就添加一个炸弹数字标签
  35. strBoomCount.initWithFormat("X %d",bigBoomCount);
  36. LabelBMFont * labelBoomCount =
  37. LabelBMFont::create(strBoomCount.getCString(),"font/font.fnt");
  38. labelBoomCount->setAnchorPoint(Point::ANCHOR_MIDDLE_LEFT);
  39. labelBoomCount->setPosition(Point(norBoom->getContentSize().width,norBoom->getContentSize().height - 30));
  40. this->addChild(labelBoomCount,TAG_BIGBOOMCOUNT);
  41. }
  42. }
  43. else if (bigBoomCount > 1 )//如果炸弹数大于1
  44. {
  45. //则只更新炸弹数目
  46. strBoomCount.initWithFormat("X %d",bigBoomCount);
  47. LabelBMFont * labelCount =
  48. (LabelBMFont *)this->getChildByTag(TAG_BIGBOOMCOUNT);
  49. labelCount->setString(strBoomCount.getCString());//设置炸弹数目
  50. }
  51. }

7. 最后来添加控制层
控制层主要是两个作用:1,暂停和继续游戏(添加屏蔽层)2,更新玩家得分
暂停和继续游戏需要两个按钮来控制。刚开始游戏时,游戏是进行着的,没有暂停。当玩家按了暂停按钮后,游戏暂停,按钮变成继续状态(三角形)这个还是比较简单的。下面来看实现代码

LayerControl.h

  1. #ifndef __LayerControl_H_
  2. #define __LayerControl_H_
  3. #include "cocos2d.h"
  4. #include "LayerNoTouch.h"
  5. class LayerControl : public cocos2d::Layer
  6. {
  7. public:
  8. CREATE_FUNC(LayerControl);
  9. bool init();
  10.  
  11. void menuCallback(cocos2d::Ref * ref);
  12. void updatescore(int score);
  13.  
  14.  
  15. private:
  16. cocos2d::MenuItemSprite * pauseMenuItem;
  17. cocos2d::LabelBMFont * scoreItem;
  18. LayerNoTouch * _noTouchLayer;
  19. };
  20.  
  21. #endif

LayerControl.cpp

  1. #include "LayerControl.h"
  2. #include "AppMacros.h"
  3. #include "SimpleAudioEngine.h"
  4. using namespace CocosDenshion;
  5. USING_NS_CC;
  6.  
  7. bool LayerControl::init()
  8. {
  9. if (!Layer::init())
  10. {
  11. return false;
  12. }
  13. _noTouchLayer = nullptr;//初始化
  14. //暂停按钮不同状态下的两个图片
  15. Sprite * nor = Sprite::createWithSpriteFrameName("game_pause_nor.png");
  16. Sprite * press = Sprite::createWithSpriteFrameName("game_pause_pressed.png");
  17. pauseMenuItem =
  18. MenuItemSprite::create(nor,press,CC_CALLBACK_1(LayerControl::menuCallback,this));
  19. Point menuBrith = Point(pauseMenuItem->getContentSize().width/2 + 10,winSize.height - pauseMenuItem->getContentSize().height);
  20. pauseMenuItem->setPosition(menuBrith);
  21. Menu * pauseMenu = Menu::create(pauseMenuItem,nullptr);
  22. pauseMenu->setPosition(Point::ZERO);
  23. this->addChild(pauseMenu,101);//将暂停/继续 按钮放在最前面
  24.  
  25. scoreItem = LabelBMFont::create("0","font/font.fnt");
  26. scoreItem->setColor(Color3B(255,255,0));
  27. scoreItem->setAnchorPoint(Point(0,0.5));
  28. scoreItem->setPosition(Point(pauseMenuItem->getPositionX() + nor->getContentSize().width/2 + 5,pauseMenuItem->getPositionY()));
  29. this->addChild(scoreItem);
  30.  
  31. return true;
  32. }
  33.  
  34. //按钮回调函数
  35. void LayerControl::menuCallback(cocos2d::Ref * ref)
  36. {
  37. if (!Director::getInstance()->isPaused())//如果点击按钮之前游戏没有暂停
  38. {
  39. if (SimpleAudioEngine::getInstance()->isBackgroundMusicPlaying())
  40. {
  41. //如果背景音乐还在播放,则暂停其播放
  42. SimpleAudioEngine::getInstance()->pauseBackgroundMusic();
  43. }
  44. //则将 暂停/继续 按钮设置为继续状态的按钮
  45. pauseMenuItem->setNormalImage(Sprite::createWithSpriteFrameName("game_resume_nor.png"));
  46. pauseMenuItem->setSelectedImage(Sprite::createWithSpriteFrameName("game_resume_pressed.png"));
  47. //并暂停游戏
  48. Director::getInstance()->pause();
  49. //添加屏蔽层,屏蔽层一定要加到其它所有层前面,暂停/继续 按钮的后面!!!
  50. _noTouchLayer = LayerNoTouch::create();
  51. this->addChild(_noTouchLayer);
  52. }
  53. else
  54. {
  55. SimpleAudioEngine::getInstance()->resumeBackgroundMusic();//恢复背景音乐
  56. pauseMenuItem->setNormalImage(Sprite::createWithSpriteFrameName("game_pause_nor.png"));
  57. pauseMenuItem->setSelectedImage(Sprite::createWithSpriteFrameName("game_pause_pressed.png"));
  58. Director::getInstance()->resume();
  59. this->removeChild(_noTouchLayer,true);
  60. }
  61. }
  62. //更新游戏得分,这个函数也可以放在主场景中
  63. void LayerControl::updatescore(int score)
  64. {
  65. /*2.0版本 String * strscore = String::createWithFormat("%d",score); scoreItem->setString(strscore->getCString());*/
  66.  
  67. //3.0版本
  68. Value strscore(score);
  69. scoreItem->setString(strscore.asString());//更新成绩转换为字符串
  70. }

8. 最后,我们来看看主场景里面都做了什么

  1. bool LayerGameMain::init()
  2. {
  3. if (!Layer::init())
  4. {
  5. return false;
  6. }
  7. _bigBoomCount = 0;
  8. _score = 0;//不要将_score 设置为static ,否则在场景切换时,它不能清零
  9.  
  10. this->addBackground();
  11.  
  12. this->addMyPlane();
  13.  
  14. this->addBulletLayer();//执行了startShoot()
  15.  
  16. this->addMultiBulletsLayer();//没有执行startShoot()
  17.  
  18. this->addEnemyLayer();
  19.  
  20. this->addFoodLayer();
  21.  
  22. this->addControlLayer();
  23.  
  24. this->scheduleUpdate();//开启定时器便于碰撞检测
  25.  
  26. auto listener = EventListenerTouchOneByOne::create();
  27. listener->setSwallowTouches(true);//触摸吞噬
  28. listener->onTouchBegan = CC_CALLBACK_2(LayerGameMain::onTouchBegan,this);
  29. listener->onTouchMoved = CC_CALLBACK_2(LayerGameMain::onTouchesMoved,this);
  30. this->_eventDispatcher->addEventListenerWithSceneGraPHPriority(listener,this);
  31.  
  32. return true;
  33. }

这是主场景的初始化函数,大家都应该看到了,依次添加了背景、玩家飞机、子弹层、敌机层、食物层、控制层、最后开启帧循环定时器、设置触摸事件的监听。
怎么样,是不是很简单,很清晰? 哈哈,当做完之后再会过来看自己写的代码还真是挺好。。

最后的最后

来说一说主场景里面的 update 函数。这个函数里面做了整个游所有的碰撞检测。其原理都一样,这里我拿子弹与小敌机的碰撞检测为例说明一下:

  1. //单发子弹与小敌机碰撞
  2. /*思路: 两次遍历(即两个for循环),第一次遍历子弹容器(_bulletVector),取出其第一个子弹, 第二次遍历小敌机容器(_smallVec)将这个取出的子弹与当前屏幕上所有的小敌机做碰撞检测, 如果检测到碰撞,再判断当前碰撞到的小敌机的生命值_life 若等于1,则小敌机失去生命值 再分别将当前的子弹和当前的小敌机加到容器 bulletToDel_Small 和 smallToDel 中去, 当第一个子弹与屏幕上的敌机全部碰撞检测完以后,就把 bulletToDel_Small 和 smallToDel 里面的对象全部删除,这样可以防止在遍历时发生错误!*/
  3. Vector<Sprite *> bulletToDel_Small;
  4. for (auto bt : _bulletLayer->_bulletVector)
  5. {
  6. Sprite * bullet = bt;
  7. Vector<Enemy *> smallToDel;
  8. for (auto et : _enemyLayer->_smallVec)
  9. {
  10. Enemy * enemy = et;
  11. if (bullet->getBoundingBox().intersectsRect(enemy->Get_BoundingBox()))
  12. {
  13. if (enemy->getLife() == 1)
  14. {
  15. enemy->loseLife();
  16. bulletToDel_Small.pushBack(bullet);
  17. smallToDel.pushBack(enemy);
  18. _score += SMALL_score;//加上小敌机的分数
  19. _controlLayer->updatescore(_score);
  20. }
  21. }
  22.  
  23. }
  24. for(auto et : smallToDel)//注意for循环的位置,要与创建时的语句在同一层
  25. {
  26. Enemy * enemy = et;
  27. _enemyLayer->smallEnemyBlowup(enemy);//敌机爆炸(删除
  28. }
  29. }
  30. for (auto bt : bulletToDel_Small)//注意for循环的位置,要与创建时的语句在同一层
  31. {
  32. Sprite * bullet = bt;
  33. _bulletLayer->removeBullet(bullet);//删除子弹
  34. }

好了,就写到这里了,写的很乱,不知道大家能不能看懂。
要是有什么写错了的地方,还望斧正。

最后附上本游戏的完整代码地址: PlaneFight

猜你在找的Cocos2d-x相关文章