Cocos2d-x 3.x学习笔记:猩先生带你打飞机(五)敌我碰撞处理、分数计算、音乐播放

前端之家收集整理的这篇文章主要介绍了Cocos2d-x 3.x学习笔记:猩先生带你打飞机(五)敌我碰撞处理、分数计算、音乐播放前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

下面先是介绍下这一节要使用到的相关cocos2d-x的技术
一、粒子特效
粒子特效是什么,这个就百度百度,我只理解到它就是个很酷炫的效果。在cocos2d-x中使用粒子特效很简单,其实他也就是个Node,创建和使用都跟精灵差不多。另外,粒子特效一般用一个plist来描述,就是一个plist做后缀的xml文件,他描述了粒子的特性,可以自定义粒子,创建粒子特效的时候,加载这个plist文件即可。 下面是代码

auto * particleSystem = ParticleSystemQuad::create("particle_texture.plist");       //使用plist文件创建粒子特效
particleSystem ->setPosition(vec1);                        //设置粒子特效显示的位置
this->addChild(particleSystem );                            //添加到当前的层

二、数据持久化
在cocos2d-x中,数据存储有很多种方式,一种是sqlit,轻量级的数据库,不过数据量小的话使用UserDefault是非常方便的,当然还有其他的方式,这里就不介绍了,我也不熟哈。UserDefault,在项目中我就是用他来存储用户的分数和最高分数的,存储一些游戏设置啊基本上都用他的。其实他也就是个map,只是用了xml文件来存储,使用方面跟map也是极其相似。当第一次调用UserDefault::getInstance(); 时将会在系统中创建一个UserDefault.xml文件,注意是系统中创建,也就是说用户可见,也是可以操作的。其实用户的敏感数据放在这里是非常危险的,非常危险!比如现在的最高分,用户修改了那数据就是作假,这游戏追求高分就没意义了。但是我找了半天发现系统文件目录下并没有这个文件,不知道是不是cocos2d-x采用了些手段隐藏起来了,不过不影响我们使用,不可见本该是它的属性。 下面是使用代码

UserDefault * userDefault = UserDefault::getInstance();        //第一次调用即创建UserDefualt.xml文件
userDefault->setIntegerForKey("topscore",score);                  //存value,索引是key,每种类型都有对应的get、set方法,跟map一样,有一个key和value
userDefault->getIntegerForKey("topscore");                            //根据key来拿value

三、播放音乐
播放音乐cocos2d-x也帮我们做了相关的工作,以至于我们想播放音乐时,只需一行代码,对没错,不要998,只要001!so,现在播放音乐这一块只剩下:我们该在where播放音乐,又是在where停止音乐。这个跟我们的游戏逻辑有相当大的联系。暂时还没踩到这个坑,这个只能在游戏中调试调试再调试了。注意:音乐最好是wav格式,我试着播放mp3格式,发现会延迟几秒钟才能播放音乐,所以最好是用工具转下格式。另外一个话题就是音乐缓冲,就是在我们还未开始游戏前,我们得把他缓冲好,要不然下次播放音乐的时候在加载音乐会很卡,小音乐就算了。所以一般的做法就是在游戏开始初始化的时候,即AppDelegate::applicationDidFinishLaunching()中进行preload,预先读取。对了,写这些代码前要把#include “SimpleAudioEngine.h”,还有那些类的命名空间是CocosDenshion,不想每次都用命名空间去调用类就直接using namespace CocosDenshion吧。
下面看代码

SimpleAudioEngine::getInstance()->preloadBackgroundMusic("sounds/game_music.wav");            //预读背景音乐
 SimpleAudioEngine::getInstance()->preloadEffect("sounds/game_over.wav");                                   //预读音效
SimpleAudioEngine::getInstance()->playEffect("sounds/game_over.wav");                                         //播放音效,一次性,播完即止
 SimpleAudioEngine::getInstance()->playBackgroundMusic("sounds/game_music.wav");                //播放背景音乐,循环播放
SimpleAudioEngine::getInstance()->stopBackgroundMusic("sounds/game_music.wav");                //停止播放背景音乐

四、游戏逻辑的实现
好吧,下面就开始我们打飞机的正题。
经过之前的一番啪啪啪(敲代码的声音,别想歪了),现在我们已经可以捕捉到碰撞的事件了。这时我们只要在捕捉函数里面做工作就好了,就是万事俱备只差碰撞处理了。
处理,我们该处理些什么呢?
首先子弹与敌机碰撞时,播放敌机被击爆的音乐,计算所得的分数,引爆敌机,就是播放一个敌机爆炸的动画,再移除动画。然后移除Vector中的敌机和子弹,最后从层中移除两者。
然后是我机与敌机碰撞,先播放我机被击爆的音乐,修改游戏状态(游戏已经结束),停止所有的定时器,造成全屏停止的效果,再播放两者爆炸效果,再从层中移除敌机和我机,存储游戏的分数并刷新最高纪录,这里使用到的cocos2d的UserDefualt类,等下介绍如何使用。最后就是跳转到游戏结束场景。
下面来实现这些逻辑。
先声明一切用到的变量和函数
在GameScene.h中声明如下代码:

int score;         //游戏所得分数
virtual void onEnter();     //层进入时调用方法,碰撞事件监听在此处声明定义
void planeBomb(cocos2d::Vec2 vec,int tag);    //飞机爆炸动画
void bombRemove(Node * sprite);                         //飞机爆炸动画播放完之后的回调函数
void stopAllSchedule();         //停止所有的定时器
void gameOver();                     //游戏结束逻辑

下面是碰撞事件的处理,具体的逻辑是当两个可以互相碰撞的实体碰撞后,触发onContactBegin事件,然后通过捕捉的参数PhysicsContact类获取到相关的信息。碰撞一般是两个实体进行碰撞的,我们可以通过contact.getShapeA()->getBody()->getNode();和contact.getShapeA()->getBody()->getNode();来获取两个碰撞的实体(就是一个节点啦,游戏里是我机、敌机或子弹精灵)。然后就是判断是哪种碰撞,是我机和敌机,或者是敌机和子弹,然后进行不同的处理。
敌机和子弹碰撞处理:
先是判断碰撞的是哪种飞机,有大的小的,加分不同。

//层进入的时候会调用函数,进行物理世界的碰撞监听
void GameScene::onEnter()
{
 Layer::onEnter();            //注意层的生命周期相关的函数一定要先调用父类的,否则会无效。

 auto listener = EventListenerPhysicsContact::create();                //创建一个监听器

 listener -> onContactBegin = [=](PhysicsContact& contact)        //这里使用了个lambda,可以理解为匿名函数,具体的用法百度吧,(>_<)
 {
  auto spriteA = (Sprite *)contact.getShapeA()->getBody()->getNode();                //获取到节点是我们最关心的事,获取到了他们就任我们宰割了,嘿嘿
  auto spriteB = (Sprite *)contact.getShapeB()->getBody()->getNode();
  int tag1 = spriteA->getTag();
  int tag2 = spriteB->getTag();
  Vec2 vec1 = spriteA->getPosition();
  Vec2 vec2 = spriteB->getPosition();
  //敌机和子弹碰撞碰撞
  if(tag1 + tag2 == 210 || tag1 + tag2 == 211)  //敌机105104 子弹106 我机103
  {
    SimpleAudioEngine::getInstance()->playEffect("sounds/use_bomb.wav");
   //加分,如果是104则是小敌机500分,105是大敌机1000if(tag1 == 104 || tag2 == 104)
   {
    score += 500;
   }
   else
   {
    score += 1000;
   }
   auto scoreSpire = (Label *)this->getChildByTag(100);                                            //根据tag获取分数Node,并修改显示的分数 
   scoreSpire->setString(String::createWithFormat("分数:%d",score)->_string); //启动粒子特效 //auto * system = ParticleSystemQuad::create("particle_texture.plist"); //使用自定义的粒子特效,就一个以plict为后缀的xml文件 //auto system = ParticleExplosion::create(); //使用cocos2d自带的粒子特效 if(tag1 == 104 || tag1 == 105) { //移除敌机和子弹 enemyList.eraSEObject(spriteA); bulletList.eraSEObject(spriteB); system->setPosition(vec1);
    //启动动画
    this->planeBomb(vec1,tag1);
   }
   else
   {
    enemyList.eraSEObject(spriteB);
    bulletList.eraSEObject(spriteA);
    //启动动画
    this->planeBomb(vec2,tag2);
    system->setPosition(vec2);
   }

   //粒子特效加入层中
   //this->addChild(system);
  }
  //敌机与我机碰撞
  else if(tag1 + tag2 == 207 || tag1+tag2 == 208)
  {
   SimpleAudioEngine::getInstance()->playEffect("sounds/game_over.wav");
   //修改游戏状态为结束状态3
   status = 3;
   stopAllSchedule();     //停止所有的定时器
   if(tag1 == 103)        //游戏结束逻辑
   {
    this->planeBomb(vec2,tag2);
    this->planeBomb(vec1,tag1);
   }
   else
   {
    this->planeBomb(vec1,tag1);
    this->planeBomb(vec2,tag2);
   }
   //使用UserDefault存储些用户数据分数,用法比较简单,map的使用方法,
   UserDefault * userDefault = UserDefault::getInstance();
   int topscore = userDefault->getIntegerForKey("topscore");
   if(topscore < score)
   {
    userDefault->setIntegerForKey("topscore",score);
   }
   else
    userDefault->setIntegerForKey("topscore",topscore);
   userDefault->setIntegerForKey("currentscore",score);

  }
  else{}        //这里处理了敌机和敌机碰撞的bug

  return true;
 };

 //注册监听器
 EventDispatcher * eventDispatcher = Director::getInstance()->getEventDispatcher();
 eventDispatcher ->addEventListenerWithSceneGraPHPriority(listener,this);

}

游戏碰撞处理完。
下面就是处理里面调用了一些自定义函数

//飞机爆动画 ,只需要知道是哪个飞机,和位置,即可
void GameScene::planeBomb(Vec2 vec,int tag)//爆炸效果
{
 float timeDelay = 0.1;
 Vector<SpriteFrame*> animationframe;
 if(tag == 104)  //小敌机动画帧
 {
  for(int i=1;i<5;i++)
  {
   auto string = cocos2d::__String::createWithFormat("enemy1_down%d.png",i);
   SpriteFrame * sf=SpriteFrame::create(string->getCString(),Rect(0,0,57,43));
   animationframe.pushBack(sf);
  }
 }
 else if(tag == 105)        //大飞机
 {
   for(int i=1;i<5;i++)
  {
   auto string = cocos2d::__String::createWithFormat("enemy2_down%d.png",69,95));
   animationframe.pushBack(sf);
  }
 }
 else            //我机爆炸就游戏结束了,给个潇洒的特写,将动画延迟到0.5秒一帧
 {
  timeDelay = 0.5;
   for(int i=1;i<5;i++)
  {
   auto string = cocos2d::__String::createWithFormat("hero_blowup_n%d.png",102,126));
   animationframe.pushBack(sf);
  }
 }
 Animation * ani=Animation::createWithSpriteFrames(animationframe,timeDelay);
 auto blanksprite=Sprite::create();
 blanksprite->setTag(tag);    
//参数中的回调函数会在动画播放完后调用
 Action * act=Sequence::create(Animate::create(ani),CCCallFuncN::create(blanksprite,callfuncN_selector(GameScene::bombRemove)),NULL);
 this->addChild(blanksprite);
 blanksprite->setPosition(vec);
 blanksprite->runAction(act);
}
//移除动画
void GameScene::bombRemove(Node * sprite)//删除自己
{
 sprite->removeFromParentAndCleanup(true);
 if (sprite->getTag() == 103)            //当移除了我机爆炸的动画,那就是游戏结束了。
 {
  SimpleAudioEngine::getInstance()->stopBackgroundMusic("sounds/game_music.wav");
  gameOver();         //游戏结束逻辑
 }
}

好,下面看看效果

游戏结束后,应该是跳到显示分数和最高分,还有继续游戏,退出游戏的场景,下一节就实现他。完结篇了哇。
源码下载:源码3

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