上一篇完成了游戏结算界面,但是数据都是假的,这篇来实现游戏数据的保存,这个游戏的玩法就是根据玩家游戏坚持的时间计算游戏分数,然后给予玩家一个荣誉称号。我把荣誉称号保存在level.xml文件中,因为cocos2d-x不支持直接显示中文,level.xml文件内容如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="xiaobing">小兵</string>
    <string name="banzhang">班长</string>
<string name="paizhang">排长</string>
<string name="lianzhang">连长</string>
<string name="yingzhang">营长</string>
<string name="tuanzhang">团长</string>
<string name="lvzhang">旅长</string>
<string name="shizhang">师长</string>
<string name="junzhang">军长</string>
<string name="siling">司令</string>
</resources>

格式跟android中的资源文件string.xml差不多。
首先需要完成一个xml文件的解析类,我直接拿以前写过的一个XmlParser类,可参考cocos2d-x 显示中文字符和解析XML文件这篇博客。
因为现在使用的是cocos2d-x 3.0版本,有点差别,需要修改XMLParser::initWithFile函数:

bool XMLParser::initWithFile(const char *xmlFileName)
{
 m_pDictionary = new CCDictionary();
 SAXParser _parser;
 _parser.setDelegator(this);

 auto sharedFileUtils = FileUtils::getInstance();
 string fullPath = sharedFileUtils->fullPathForFilename(xmlFileName);
 return _parser.parse(fullPath.c_str());
}

先来添加几个辅助函数,在GameScene.cpp中添加代码:

int g_gameTime = 0;

string getLevel(int score) {
 string ret = "";
 if(score < 20) {
  ret = "xiaobing";
 }else if(score < 30) {
  ret = "banzhang";
 }else if(score < 40) {
  ret = "paizhang";
 }else if(score < 50) {
  ret = "lianzhang";
 }
 else if(score < 60) {
  ret = "yingzhang";
 }else if(score < 70) {
  ret = "tuanzhang";
 }else if(score < 80) {
  ret = "lvzhang";
 }else if(score < 90) {
  ret = "shizhang";
 }else if(score < 100) {
  ret = "junzhang";
 }else if(score >= 120) {
  ret = "siling";
 }
 return ret;
}

int getBulletNum(int score) {
 int ret = 10 + score;
 if(ret > 100) {
  ret = 100;
 }
 return ret;
}

变量g_gameTime ,用来保存游戏进行的时间。getLevel函数根据游戏分数返回荣誉名称,后面就根据这个荣誉名称返回xml中对应的中文名称。getBulletNum函数是根据游戏分数返回屏幕上子弹的最大数目。
在GameScene::init函数中添加:

g_gameTime = 0;

每次游戏开始时初始化该值为0.
修改GameScene::createBullet函数:

 int maxBulletNum = getBulletNum(g_gameTime);
 if (_bullets->count() >= maxBulletNum)
 {
  return;
 }

之前是写死的10颗子弹,现在改成由玩家游戏时间决定子弹最大数目。
修改GameOverLayer::init函数:

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));

  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);

  auto userDefault = UserDefault::getInstance();
  int oldScore = userDefault->getIntegerForKey("score");
  if(g_gameTime <= oldScore) {
   newRecordText->setVisible(false);
  }

int maxScore = g_gameTime > oldScore ? g_gameTime : oldScore;
  userDefault->setIntegerForKey("score", maxScore);
  XMLParser *pXmlParser = XMLParser::parseWithFile("level.xml");
  string level = ::getLevel(maxScore);
  CCString *pValue1 = pXmlParser->getString(level.c_str());
  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);

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;
}

使用UserDefault来保存游戏历史最高分数,然后根据最高分数活动荣誉名称,再读取xml对应的中文名称。
最后要更新g_gameTime 这个值,以秒为单位计时,在GameScene类中添加:
void updateScore(float dt);
实现updateScore函数:

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

 if( _plane->isVisible()) {
  g_gameTime += dt;
  if(_scoreLabel) {
  char tmp[4];
  sprintf(tmp, "%d", g_gameTime);
  string scoreStr(tmp);
  _scoreLabel->setString(scoreStr);
  }
 }
}

在GameScene::init函数添加:
this->schedule(schedule_selector(GameScene::updateScore),1);
每一秒执行updateScore一次。

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_...

阅读全文

欢迎留言