上篇完成了子弹和飞猪的碰撞检测,现在来添加飞猪死亡的爆炸效果,并完成游戏结束界面。这个游戏规则是当子弹碰到飞猪的时候就Game Over,游戏结束。首先在GameScene类中添加:

 void explosionEndDid();

 cocos2d::Sprite *_explosion;
 cocos2d::LabelAtlas *_scoreLabel;
 bool _isGameOver;

在gamescene.h中添加:

class GameOverLayer : public cocos2d::LayerColor
{
public:
 GameOverLayer();

 virtual ~GameOverLayer();

 bool init();

 CREATE_FUNC(GameOverLayer);
};

在GameScene::init函数中添加:

int index = (int)(rand() % 5 + 1);
const char* bg_name = String::createWithFormat("img_bg_%d.jpg", index)->getCString();

auto background = Sprite::create(bg_name);
background->setPosition(Point(_screenSize.width/2 + origin.x, _screenSize.height/2 + origin.y));
this->addChild(background, -1);

_scoreLabel = LabelAtlas::create("0", "number_small.png", 22, 28, '0');
_scoreLabel->setPosition(Point(origin.x + _screenSize.width / 2 - _scoreLabel->getContentSize().width / 2, origin.y + _screenSize.height - _scoreLabel->getContentSize().height - 5));
this->addChild(_scoreLabel);

游戏一共有5个背景图,随机显示。_scoreLabel 用来显示游戏的分数,就是游戏进行的时间,暂时为初始值0。
修改GameScene::updateBoxBody函数,当子弹和飞猪碰撞的时候,把飞机添加到删除的容器中,并标识游戏结束。然后执行飞猪爆炸动画,这里使用了粒子效果,并跳转到游戏结束界面。

void GameScene::updateBoxBody(float dt)
{
_world->Step(dt, 10, 10);
std::vector<b2Body *> toDestroy;
for(b2Body *body = _world->GetBodyList(); body; body = body->GetNext()) {
 if(body->GetUserData() != NULL) {
   Sprite *sprite = (Sprite*)body->GetUserData();
   b2Vec2 b2Pos = b2Vec2(sprite->getPositionX() / PTM_RATIO, sprite->getPositionY() / PTM_RATIO);
   float b2Angle = -1 * CC_DEGREES_TO_RADIANS(sprite->getRotation());
   body->SetTransform(b2Pos, b2Angle);
   if (sprite->getTag() == SPRITE_BULLET && !_screenRect.containsPoint(sprite->getPosition())) {
    toDestroy.push_back(body);
   }
   }
 }

 std::vector<MyContact>::iterator iter;
 for(iter = _contactListener->_contacts.begin(); iter != _contactListener->_contacts.end(); ++ iter) {
  MyContact contact = *iter;
  b2Body *bodyA = contact.fixtureA->GetBody();
  b2Body *bodyB = contact.fixtureB->GetBody();
  if(bodyA->GetUserData() != NULL && bodyB->GetUserData() != NULL) {
   Sprite *spriteA = (Sprite*)bodyA->GetUserData();
   Sprite *spriteB = (Sprite*)bodyB->GetUserData();
   if(spriteA->getTag() == SPRITE_PLANE && spriteB->getTag() == SPRITE_BULLET) {
    toDestroy.push_back(bodyA);
    _isGameOver = true;
   }else if(spriteB->getTag() == SPRITE_PLANE && spriteA->getTag() == SPRITE_BULLET) {
    toDestroy.push_back(bodyB);
    _isGameOver = true;
   }
  }
 }

 std::vector<b2Body *>::iterator iter2;
 for(iter2 = toDestroy.begin(); iter2 != toDestroy.end(); ++ iter2) {
  b2Body *body = *iter2;
  if(body->GetUserData() != NULL) {
   Sprite *sprite = (Sprite *)body->GetUserData();
   if(sprite->getTag() == SPRITE_BULLET) {
    _spriteBatch->removeChild(sprite, true);
    _bullets->removeObject(sprite);
   }
  }
  _world->DestroyBody(body);
 }

 if(toDestroy.size() > 0 && _isGameOver) {
  _plane->setVisible(false);
  _scoreLabel->setVisible(false);
  //飞机爆炸粒子效果
  auto particleEmitter = ParticleSystemQuad::create("explosion.plist");
  particleEmitter->setPosition(_plane->getPosition());
  auto particleBatch = ParticleBatchNode::createWithTexture(particleEmitter->getTexture());
  particleBatch->addChild(particleEmitter);
  this->addChild(particleBatch, 10);
  _spriteBatch->removeAllChildrenWithCleanup(true);
  _spriteBatch->setVisible(false);
  _bullets->removeAllObjects();
  this->runAction(Sequence::create(DelayTime::create(1), CallFunc::create(CC_CALLBACK_0(GameScene::explosionEndDid, this)), NULL));
 }
}

cocos2d-x使用的粒子效果十分方便,使用ParticleSystem来表示,分为两大类,重力式粒子系统ParticleSystemPoint和放射式粒子系统ParticleSystemQuad,这里使用ParticleSystemQuad。 粒子效果使用的explosion.plist文件使用ParticleEditor编辑,怎么使用可以google,很容易上手,也可以参考TestCpp中提供的demo。
在GameScene::createBullet和GameScene::updateBullet、GameScene::updateBoxBody函数首行添加:

 if (_isGameOver)
 {
  return;
 }

去掉Bullet类的_is_live属性,修改 GameScene::updateBullet函数:

void GameScene::updateBullet(float dt)
{
 if (_isGameOver)
 {
  return;
 }

 Object *bulletObj = NULL;
 CCARRAY_FOREACH(_bullets, bulletObj)
 {
  Bullet *bullet = (Bullet*)bulletObj;
  Point position = bullet->getPosition();
  Point new_pos = Point(position.x + bullet->get_speed_x(), position.y + bullet->get_speed_y());
  bullet->setPosition(new_pos);
 }
}

实现explosionEndDid函数:

void GameScene::explosionEndDid()
{
 auto scene = Director::getInstance()->getRunningScene();
 auto layer = GameOverLayer::create();
 scene->addChild(layer);
}

游戏结束界面实现代码:

bool GameOverLayer::init()
{
 bool ret = false;
 do {
  CC_BREAK_IF( !this->initWithColor(Color4B(105, 105, 105, 128)) );
  Size visibleSize = Director::getInstance()->getVisibleSize();
  Point origin = Director::getInstance()->getVisibleOrigin();

  auto title = Sprite::create("girl.png");
  title->setPosition(Point(origin.x + visibleSize.width / 2, origin.y + visibleSize.height - title->getContentSize().height / 2 - 100));
  this->addChild(title);

  auto scorePanel = Scale9Sprite::create("content_bg.png");
  scorePanel->setPreferredSize(Size(500, 250));
  scorePanel->setPosition(Point(origin.x + visibleSize.width / 2, title->getPositionY() - title->getContentSize().height / 2 - scorePanel->getContentSize().height / 2 - 80));

 auto purpleBase = Sprite::create("purple_base.png");
  purpleBase->setScaleY(1.2f);
  purpleBase->setScaleX(1.3f);
  purpleBase->setPosition(Point(origin.x + purpleBase->getContentSize().width / 2, scorePanel->getPositionY() + scorePanel->getContentSize().height / 2 + 60));

  int g_gameTime = 21;
  char tmp2[4];
  sprintf(tmp2, "%d", g_gameTime);
  string scoreStr(tmp2);

  auto newScoreLabel = LabelAtlas::create(scoreStr.c_str(), "number_large.png", 64, 90, '0');
  newScoreLabel->setScale(0.8f);
  newScoreLabel->setPosition(Point(purpleBase->getContentSize().width / 2 - newScoreLabel->getContentSize().width / 2 + 20, purpleBase->getContentSize().height / 2 - newScoreLabel->getContentSize().height / 2 + 30));
  purpleBase->addChild(newScoreLabel);

 auto newRecordText = Sprite::createWithSpriteFrameName("record_breaking.png"); 
  newRecordText->setPosition(Point(purpleBase->getContentSize().width / 2, purpleBase->getContentSize().height - newRecordText->getContentSize().height / 2));
  purpleBase->addChild(newRecordText);

  auto levelText = Sprite::createWithSpriteFrameName("level.png");
  levelText->setPosition(Point(levelText->getContentSize().width / 2 + 140, scorePanel->getContentSize().height - levelText->getContentSize().height - 30));
  levelText->setScale(1.5f);
  scorePanel->addChild(levelText);

  String *pValue1 = String::create("Alex");
  auto levelDescText = LabelTTF::create(pValue1->getCString(), "Arial", 40);
  levelDescText->setColor(Color3B(220, 145, 39));
  levelDescText->setPosition(Point(levelText->getPositionX() + levelText->getContentSize().width + 60, levelText->getPositionY()));
  scorePanel->addChild(levelDescText);

  auto maxText = Sprite::createWithSpriteFrameName("max.png");
  maxText->setScale(1.5f);
  maxText->setPosition(Point(levelText->getPositionX(), levelText->getPositionY() - levelText->getContentSize().height - 50));
  scorePanel->addChild(maxText);

  int maxScore = 21;
  char tmp[4];
  sprintf(tmp, "%d", maxScore);
  string maxScoreStr(tmp);

  auto maxScoreLabel = LabelAtlas::create(maxScoreStr.c_str(), "number_large.png", 64, 90, '0');
  maxScoreLabel->setPosition(Point(levelDescText->getPositionX() - 30, maxText->getPositionY() - maxText->getContentSize().height / 2 - 10));
  maxScoreLabel->setScale(0.6f);
  scorePanel->addChild(maxScoreLabel);
  this->addChild(scorePanel);
  this->addChild(purpleBase);

  //C++11之lambda表达式
  auto startBtnItem = MenuItemImage::create("", "", [](Object *sender) {
    Scene *scene = GameScene::scene();
    Director::getInstance()->replaceScene(scene);
  });

  auto btnSprite = Sprite::create("btn_yellow.png");
  startBtnItem->setNormalSpriteFrame(btnSprite->getDisplayFrame());
  startBtnItem->setPosition(Point(origin.x + visibleSize.width / 2, origin.y + startBtnItem->getContentSize().height / 2 + 150));
  auto startMenu = Menu::create(startBtnItem, NULL);
  startMenu->setPosition(Point::ZERO);
  this->addChild(startMenu);
  auto startBtnText = Sprite::createWithSpriteFrameName("again_game_text.png");
  startBtnText->setPosition(startBtnItem->getPosition());

  this->addChild(startBtnText);
  ret = true;
 }while(0);
 return ret;
}

游戏结束界面比较简单,游戏数据都是假数据,上面代码使用了C++11之lambda表达式,格式有点像java中的匿名类,很方便。这里还使用了Scale9Sprite,支持9宫格图片,放大缩小时只拉伸指定区域,这样拉伸时不会变形,做过android开发的应该很熟悉这个东东,非常好用。使用Scale9Sprite ,需要在gamescene.cpp中添加:

#include "cocos-ext.h"
USING_NS_CC_EXT;

然后,右键项目属性-配置属性-C/C++-常规-附加包含目录,添加$(EngineRoot)。

右键项目属性-》链接器-》输入-》附加依赖项里添加libExtensions.lib。

然后运行项目。

 

cocos2d-x手游性能优化总结

近段时间在使用cocos2d-x开发2D手游,技术方案使用的是cocos2d-x+lua,因为游戏使用的是cocos2d-x 2.1.5版本,有些优化方案在最新版的cocos2d-x版本已经实现...

阅读全文

cocos2dx-html5 实现网页版flappy bird游戏

我也是第一次使用cocos2d_html5,对js和html5也不熟,看引擎自带的例子和引擎源码,边学边做,如果使用过cocos2d-x的话,完成这个游戏还是十分简单的。游戏体...

阅读全文

【cocos2d-x开发实战 特训99-终结篇】移植到android平台和添加admob广告

上一篇已经完成特性99在win32平台下的开发,现在把它移植到android上,首先修改Android.mk文件,内容如下: LOCAL_PATH := $(call my-dir) include $(CLEAR_...

阅读全文

1 条评论

欢迎留言