欢迎光临Software MyZone,有问题可留言或到站点论坛发帖,争取第一时间帮忙解决 || 站点论坛:火龙论坛 || 淘宝小店:应小心的易淘屋 【欢迎大家提建设性意见】

【cocos2d-x开发实战 特训99-part4】添加游戏结束界面 转

欢迎热爱编程的朋友们参与到cocos2d-x编程中,为了给大家提供良好的交流环境,网站以开启QQ群
Software MyZone:66202765(群号,欢迎加入,若满,请加1群)
Software MyZone 1群(2dx):286504621
【加群请写:Software MyZone或者是firedragonpzy】
淘宝店:【应小心的易淘屋】初次开店,大家多多支持……
群论坛:火龙论坛正试运营阶段,欢迎大家多提些建设性意见……
摘自:http://codingnow.cn/cocos2d-x/1431.html

上篇完成了子弹和飞猪的碰撞检测,现在来添加飞猪死亡的爆炸效果,并完成游戏结束界面。这个游戏规则是当子弹碰到飞猪的时候就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,如图:

现在运行项目,效果如图:

发表评论