植物大战僵尸游戏开发教程专栏地址http://t.csdnimg.cn/xjvbb
打开LevelData.h和LevelData.cpp文件。文件位置如下图所示。
LevelData.h
此头文件中定义了两个类,分别是OpenLevelData、LevelData,其中OpenLevelData用于加载文件数据。LevelData解析数据,将数据保存到数据结构中。
cpp
#pragma once
#include "cocos2d.h"
#include "json/writer.h"
#include "json/document.h"
#include "json/stringbuffer.h"
using namespace std;
using namespace cocos2d;
using namespace rapidjson;
class LevelData;
class OpenLevelData
{
public:
/**
*单例
*/
static OpenLevelData* getInstance();
/**
*打开关卡数据
*/
bool openLevelsData(const string& worlddata);
/*
*解密关卡数据
*/
void decrypt(char* cSrc, char* cDest);
void decrypt(string& cSrc, char* cDest);
/**
*获取所有关卡数据
*/
Document* getDocument();
/**
*创建某一个关卡数据
*/
void createLevelData(const int level, const char* levelName);
/**
*读取某一个关卡
*/
LevelData* readLevelData(const int level);
/**
*设置关卡数
*/
void setLevelNumber(const int levelNumber);
/**
*获取关卡数
*/
int getLevelNumber() const;
/**
初始化
*/
void documentInit();
private:
OpenLevelData():_document(new Document), _levelNumber(-1){}
~OpenLevelData() {}
private:
static OpenLevelData* _instance;
Document* _document;
map<int, LevelData*>_levelData;
int _levelNumber;
};
struct MyPoint
{
MyPoint():x(0),y(0){}
int x, y;
};
class LevelData
{
public:
bool readLevelData(const char* LevelName);
bool getZombiesVisible() const { return _zombiesIsVisible; }
bool getZombiesIsSmall() const { return _zombiesIsSmall; }
bool getZombiesIsBig() const { return _zombiesIsBig; }
bool getIsNoPlants() const { return _isNoPlants; }
int getZombiesFrequency() const { return _zombiesFrequency; }
int getCoinNumbers() const { return _coinNumbers; }
int getAtLeastSunNumbers() const { return _atLeastSunNumbers; }
int getFlowerPosition() const { return _flowerPosition; }
int getCarNumbers() const { return _carNumbers; }
int getUsePlantsNumbers() const { return _usePlantsNumbers; }
int getFirstFrequencyTime() const { return _firstFrequencyTime; }
float getUserLostPosition() const { return _userLose; }
vector<int>& getGameType() { return _gameType; }
vector<int>& getZombiesType() { return _zombiesType; }
vector<int>& getZombiesNumbers() { return _zombiesNumbers; }
vector<int>& getMunchZombiesFrequency() { return _munchZombiesFrequency; }
vector<MyPoint>& getNoPlantsPosition() { return _noPlantsPosition; }
vector<vector<int> >& getZombiesTypeProbabilityFrequency() { return _zombiesTypeProbabilityFrequency; }
CC_CONSTRUCTOR_ACCESS:
LevelData();
~LevelData();
private:
void setGameTypes(const char* LevelName);
private:
bool _isEncryption; /* 是否加密 */
bool _zombiesIsVisible; /* 僵尸是否隐身 */
bool _zombiesIsSmall; /* 是否是小僵尸 */
bool _zombiesIsBig; /* 是否是巨人僵尸 */
bool _isNoPlants; /* 是否不可种植 */
int _zombiesFrequency; /* 僵尸总波数 */
int _coinNumbers; /* 金币数 */
int _atLeastSunNumbers; /* 至少产生的阳光数 */
int _flowerPosition; /* 花坛位置 */
int _carNumbers; /* 小车数量 */
int _usePlantsNumbers; /* 使用植物数量 */
int _firstFrequencyTime; /* 第一波僵尸出现时间 */
float _userLose; /* 玩家失败 */
vector<int>_gameType; /* 游戏类型 */
vector<int>_zombiesType; /* 僵尸类型 */
vector<int>_zombiesNumbers; /* 僵尸数 */
vector<int>_munchZombiesFrequency; /* 多僵尸波数 */
vector<vector<int> >_zombiesTypeProbabilityFrequency; /* 每一波每种僵尸出现的概率 */
vector<MyPoint>_noPlantsPosition; /* 不可以种植的地方 */
Document* _document;
};
LevelData.cpp
getInstance()函数
OpenLevelData 使用了单例模式,这样保证了只会创建唯一的一个实例。该实例会使用map数据结构保存所有关卡数据。
cpp
map<int, LevelData*>_levelData;
cpp
OpenLevelData* OpenLevelData::getInstance()
{
if (_instance == nullptr)
{
_instance = new (std::nothrow)OpenLevelData;
}
return _instance;
}
openLevelsData()函数
函数有一个参数,表示要加载的文件名称。使用Cocos2d-x 中FileUtils类加载磁盘文件中的数据,函数返回字符串类型数据。
由于该文件存在加密,所以首先对字符串数据进行解密,然后使用RapidJson 库来解析json 字符串数据。如果解析失败返回false, 解析成功返回true。
cpp
bool OpenLevelData::openLevelsData(const string& worlddata)
{
char* passWords;
string str = FileUtils::getInstance()->getStringFromFile(worlddata);
passWords = (char*)malloc(sizeof(char) * str.size());
/* 解密 */
decrypt(str, passWords);
documentInit();
_document->Parse<rapidjson::kParseDefaultFlags>(passWords);
free(passWords);
if (_document->HasParseError())return false;
return true;
}
decrypt()函数
函数有两个参数,第一个参数表示要解密的字符串,第二个参数表示解密后的字符串。通过逐个遍历字符进行字符串解密,其算法如下代码所示。
cpp
void OpenLevelData::decrypt(string& cSrc, char* cDest)
{
int i, h, l, m, n, j = 0;
for (i = 0; i < static_cast<int>(cSrc.size()); i = i + 2)
{
h = (cSrc[i] - 'x');
l = (cSrc[i + 1] - 'z');
m = (h << 4);
n = (l & 0xf);
cDest[j] = m + n;
j++;
}
cDest[j] = '\0';
}
createLevelData()函数
函数有两个参数,第一参数表示关卡编号,用作map中的key值,当需要获取某一关卡数据时,只需要根据key值就可以获取相关数据。第二参数是关卡名称,根据关卡名称获取文件中的关卡数据。
cpp
void OpenLevelData::createLevelData(const int level, const char* levelName)
{
/* map中如果没有关卡数据 */
if (!_levelData.count(level))
{
LevelData* levelData = new LevelData;
levelData->readLevelData(levelName);
_levelData.insert(pair<int, LevelData*>(level, levelData));
}
}
这个函数功能是解析某一关卡数据,并将其保存的map数据结构中。首先判断map数据结构中是否已经存在该关卡数据,如果不存在,则使用LevelData 类中的**readLevelData()**函数解析文件中该关卡的数据然后将其保存到map数据结构中供后续使用。
readLevelData()函数
函数有一个参数,表示关卡名称,函数根据关卡名称来解析json文件。
cpp
bool LevelData::readLevelData(const char* LevelName)
{
_document = OpenLevelData::getInstance()->getDocument();
if (_document->HasMember(LevelName))
{
_isEncryption = (*_document)[LevelName]["IsEncryption"].GetBool();
_coinNumbers = (*_document)[LevelName]["CoinNumbers"].GetInt();
_zombiesFrequency = (*_document)[LevelName]["Frequency"].GetInt();
_firstFrequencyTime = (*_document)[LevelName]["FirstFrequencyTime"].GetInt();
_userLose = (*_document)[LevelName]["UserLose"].GetFloat();
for (unsigned int i = 0; i < (*_document)[LevelName]["GameType"].Size(); i++)
{
_gameType.push_back((*_document)[LevelName]["GameType"][i].GetInt());
}
for (unsigned int i = 0; i < (*_document)[LevelName]["ZombiesType"].Size(); i++)
{
_zombiesType.push_back((*_document)[LevelName]["ZombiesType"][i].GetInt());
}
for (unsigned int i = 0; i < (*_document)[LevelName]["MunchZombiesFrequency"].Size(); i++)
{
_munchZombiesFrequency.push_back((*_document)[LevelName]["MunchZombiesFrequency"][i].GetInt());
}
for (unsigned int i = 0; i < (*_document)[LevelName]["ZombiesNumbers"].Size(); i++)
{
_zombiesNumbers.push_back((*_document)[LevelName]["ZombiesNumbers"][i].GetInt());
}
vector<int> v;
for (unsigned int i = 0; i < (*_document)[LevelName]["ZombiesTypeProbability"].Size(); i++)
{
v.clear();
for (unsigned int j = 0; j < (*_document)[LevelName]["ZombiesTypeProbability"][i].Size(); j++)
{
v.push_back((*_document)[LevelName]["ZombiesTypeProbability"][i][j].GetInt());
}
_zombiesTypeProbabilityFrequency.push_back(v);
}
setGameTypes(LevelName);
return true;
}
return false;
}
函数首先判断json文件中是否存在以形参变量命名的关卡名称,如果有则进行数据解析,最后返回true 表示解析成功,否则返回false表示解析失败。
setGameTypes()函数
函数有一个参数,表示关卡名称,函数根据关卡名称来解析json文件。
cpp
void LevelData::setGameTypes(const char* LevelName)
{
for (unsigned int i = 0; i < _gameType.size(); i++)
{
switch (static_cast<GameTypes>(_gameType.at(i)))
{
case GameTypes::AtLeastSunNumbers:
_atLeastSunNumbers = (*_document)[LevelName]["AtLeastSunNumbers"].GetInt();
break;
case GameTypes::FlowerPosition:
_flowerPosition = 570 + 122 * (*_document)[LevelName]["FlowerPosition"].GetInt();
break;
case GameTypes::CarNumbers:
_carNumbers = (*_document)[LevelName]["CarNumbers"].GetInt();
break;
case GameTypes::UserPlantsNumbers:
_usePlantsNumbers = (*_document)[LevelName]["UserPlantsNumbers"].GetInt();
break;
case GameTypes::ZombiesInvisible:
_zombiesIsVisible = true;
break;
case GameTypes::SmallZombies:
_zombiesIsSmall = true;
break;
case GameTypes::BigZombies:
_zombiesIsBig = true;
break;
case GameTypes::NoPlants:
{
_isNoPlants = true;
MyPoint MyPoint;
for (unsigned int i = 0; i < (*_document)[LevelName]["NoPlants"].Size(); i++)
{
MyPoint.x = (*_document)[LevelName]["NoPlants"][i][0].GetInt();
MyPoint.y = (*_document)[LevelName]["NoPlants"][i][1].GetInt();
_noPlantsPosition.push_back(MyPoint);
}
}
break;
}
}
}
函数根据不同的游戏类型解析不同的数据,使用switch case语句来判断不同的游戏类型。
其他函数
其他函数就不一一介绍了,可以自行查看阅读代码。
后续
后续将讲解游戏多语言切换功能的实现。