前言
上一篇其实已经说完了这个游戏里的重要部分了,所以接下来开始写代码了。先从解析json文件开始,配置环境啥的就不写了,网上一搜大把的教程(感觉这部分其实都不用写,也有类似读取json文件的教程= =)
地图数据json
       这里使用rapidjson读取json文件。以3.2版本为例,rapidjson头文件在项目目录\cocos2d\external\json下,如下图所示:
       在使用rapidjson前,需要引入以下头文件:1
2
3
4
5
       接下来上地图配置加载的代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118//加载json并解析
bool CDataManager::LoadMapData()
{
	string strContent = FileUtils::getInstance()->getStringFromFile(MAP_FILE_PATH);
	rapidjson::Document oDoc;
	oDoc.Parse<0>(strContent.c_str());
	if (oDoc.HasParseError())
	{
		log("Parse Error: %s\n", oDoc.GetParseError());
		return false;
	}
	if (oDoc.IsNull() || !oDoc.IsArray())
	{
		return false;
	}
	for (int i = 0; i < oDoc.Size(); i++)
	{
		MapInfo mapInfo;
		//color
		if (oDoc[i]["color"].IsNull())
		{
			return false;
		}
		mapInfo.iColor = oDoc[i]["color"].GetInt();
		//id
		if (oDoc[i]["id"].IsNull())
		{
			return false;
		}
		mapInfo.iID = oDoc[i]["id"].GetInt();
		//beat
		if (oDoc[i]["beat"].IsNull())
		{
			return false;
		}
		mapInfo.iBeat = oDoc[i]["beat"].GetInt();
		//map
		if (oDoc[i]["map"].IsNull() || (!oDoc[i]["map"].IsArray()))
		{
			return false;
		}
		//每行数据
		for (int j = 0; j < oDoc[i]["map"].Size(); j++)
		{
			if (oDoc[i]["map"][j].IsNull() 
				|| (!oDoc[i]["map"][j].IsArray()))
			{
				return false;
			}
			for (int k = 0; k < oDoc[i]["map"][j].Size(); k++)
			{
				if (oDoc[i]["map"][j][k].IsNull())
				{
					return false;
				}
				int iValue = oDoc[i]["map"][j][k].GetInt();
				//每行数据存入vecMap中
				mapInfo.vecMap.push_back(iValue);
			}
		}
		//mapitem
		if (oDoc[i]["mapitem"].IsNull() 
			|| (!oDoc[i]["mapitem"].IsArray()))
		{
			return false;
		}
		for (int j = 0; j < oDoc[i]["mapitem"].Size(); j++)
		{
			if (oDoc[i]["mapitem"][j].IsNull())
			{
				return false;
			}
			string strMapItem = oDoc[i]["mapitem"][j].GetString();
			int iArrow[3] = {0, 0, 0};
			SplitString(strMapItem, '_', iArrow);
			ArrowInfo arrowInfo;
			//X和Y位置互换
			arrowInfo.iY = iArrow[0];
			arrowInfo.iX = iArrow[1];
			arrowInfo.iType = iArrow[2];
			//存入vecMapItem中
			mapInfo.vecMapItem.push_back(arrowInfo);
		}
		
		//useritem
		if (oDoc[i]["useritem"].IsNull() 
			|| (!oDoc[i]["useritem"].IsArray()))
		{
			return false;
		}
		for (int j = 0; j < oDoc[i]["useritem"].Size(); j++)
		{
			if (oDoc[i]["useritem"][j].IsNull())
			{
				return false;
			}
			int iNum = oDoc[i]["useritem"][j].GetInt();
			mapInfo.vecUserItem.push_back(iNum);
		}
		m_oMapVec.push_back(mapInfo);
	}
	return true;
}
       以上代码涉及到了两个结构体,MapInfo和ArrowInfo,对应定义如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24//箭头信息
struct ArrowInfo
{
	int iX;		//横向位置索引
	int iY;		//纵向位置索引
	int iType;	//箭头类型
};
struct MapInfo
{
	int iColor;	//颜色序号,也是主题序号
	int iID;	//关卡ID
	int iBeat;	//可点击次数
	vector<int> vecMap;	//六边形行列分布序列
	vector<ArrowInfo> vecMapItem;	//地图中箭头分布
	vector<int> vecUserItem;	//可用箭头数据
};
       另外还有一个函数SplitString,其负责解析出字符串中的三个数值,代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//移除string中所有位置出现过的某个字符
void CDataManager::SplitString(string &srcStr, char chTarget, int* pArray)
{
	int iPos = srcStr.find_first_of('_');
	string strTemp = srcStr.substr(0, iPos);
	pArray[0] = atoi(strTemp.c_str());
	int iNewPos = srcStr.find_first_of('_', iPos + 1);
	strTemp = srcStr.substr(iPos + 1, iNewPos - iPos - 1);
	pArray[1] = atoi(strTemp.c_str());
	iPos = srcStr.find_first_of('_', iNewPos + 1);
	strTemp = srcStr.substr(iNewPos + 1, iPos - iNewPos - 1);
	pArray[2] = atoi(strTemp.c_str());
}
进度数据json
       现在需要考虑一个问题,游戏的进度该如何保存?使用UserDefault类?很显然是不行的,因为需要记录每个关卡的分数情况,200个关卡用UserDefault去存储的话显然够呛,所以这里可以考虑使用json文件保存游戏进度数据(当然,用xml也是可以的)。进度数据文件格式如下:1
{"score":600,"level":200,"data":[3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3]}
       先写个创建进度数据文件的函数。因为第一次打开游戏时,进度是木有的,所以需要先建好进度数据文件:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37void CDataManager::InitUserData(const char* pName)
{
	rapidjson::Document oWriteDoc;
	oWriteDoc.SetObject();
	rapidjson::Document::AllocatorType& allocator = oWriteDoc.GetAllocator();
	rapidjson::Value oArray(rapidjson::kArrayType);
	oWriteDoc.AddMember("score", 0, allocator);
	oWriteDoc.AddMember("level", 0, allocator);
	//初始化数据
	m_stUserInfo.iLevel = 0;
	m_stUserInfo.iScore = 0;
	for (int i = 0; i < TOTAL_LEVEL_NUM; i++)
	{
		m_stUserInfo.vecData.push_back(0);
		oArray.PushBack(0, allocator);
	}
	oWriteDoc.AddMember("data", oArray, allocator);
	rapidjson::StringBuffer oBuffer;
	rapidjson::Writer<rapidjson::StringBuffer> oWriter(oBuffer);
	oWriteDoc.Accept(oWriter);
	//写入
	FILE* pFile = fopen(pName, "wb+");
	if (pFile == NULL)
	{
		log("Open File '%s' Failed.", pName);
		return;
	}
	fputs(oBuffer.GetString(), pFile);
	fclose(pFile);
}
       这里传入参数pName即要写入的进度数据文件路径,由于创建时没有进度,所以score和level都是0,各个关卡分数记录也是0。
       接下来写加载进度数据文件的函数:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55//加载用户数据
bool CDataManager::LoadUserData()
{
	string strPath = FileUtils::getInstance()->getWritablePath() + string(SAVE_DATA_PATH);
	log("UserData Path:%s\n", strPath.c_str());
	string strContent = FileUtils::getInstance()->getStringFromFile(strPath);
	rapidjson::Document oDoc;
	oDoc.Parse<0>(strContent.c_str());
	if (oDoc.HasParseError() || oDoc.IsNull())
	{
		//log("CDataManager::LoadUserData Load 'oDoc' Error.");
		InitUserData(strPath.c_str());
		return true;
	}
	//当前获得的分数
	if (oDoc["score"].IsNull())
	{
		log("CDataManager::LoadUserData Load 'score' Error.");
		return false;
	}
	m_stUserInfo.iScore = oDoc["score"].GetInt();
	//已通过关卡数量,因为不可能跨关卡,所以也表示当前通过的最后一关
	if (oDoc["level"].IsNull())
	{
		log("CDataManager::LoadUserData Load 'level' Error.");
		return false;
	}
	m_stUserInfo.iLevel = oDoc["level"].GetInt();
	//所有关卡当前信息
	if (oDoc["data"].IsNull() || (!oDoc["data"].IsArray()))
	{
		log("CDataManager::LoadUserData Load 'data' Error.");
		return false;
	}
	for (int i = 0; i < oDoc["data"].Size(); i++)
	{
		if (oDoc["data"][i].IsNull())
		{
			log("CDataManager::LoadUserData Load 'oDoc[data][%d]' Error.", i);
			return false;
		}
		int iValue = oDoc["data"][i].GetInt();
		m_stUserInfo.vecData.push_back(iValue);
	}
	return true;
}
       需要注意的是,默认当进度数据json文件读取失败时,说明json文件不存在,需要重新建立,即调用InitUserData函数创建进度数据文件。m_stUserInfo中保存了读取的进度数据,其类型是UserInfo结构体类型,UserInfo定义如下:1
2
3
4
5
6
7
8struct UserInfo
{
	int iScore;		//分数
	int iLevel;		//当前通过关卡数
	vector<int> vecData;	//关卡得分数据
};
       有了读取函数当然少不了写入函数,进度数据保存函数如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73//用户数据写入到指定文件中
bool CDataManager::SaveUserData()
{
	string strPath = FileUtils::getInstance()->getWritablePath() + string(SAVE_DATA_PATH);
	string strContent = FileUtils::getInstance()->getStringFromFile(strPath);
	rapidjson::Document oDoc;
	oDoc.Parse<0>(strContent.c_str());
	if (oDoc.HasParseError())
	{
		log("Parse Error: %s\n", oDoc.GetParseError());
		return false;
	}
	if (oDoc.IsNull())
	{
		return false;
	}
	//更新分数
	if (oDoc["score"].IsNull())
	{
		return false;
	}
	oDoc["score"].SetInt(m_stUserInfo.iScore);
	//更新通过的关卡数量
	if (oDoc["level"].IsNull())
	{
		return false;
	}
	oDoc["level"].SetInt(m_stUserInfo.iLevel);
	//更新关卡当前信息
	if (oDoc["data"].IsNull() || !oDoc["data"].IsArray())
	{
		return false;
	}
	int iVecSize = m_stUserInfo.vecData.size();
	int iJsonSize = oDoc["data"].Size();
	if (iVecSize != iJsonSize)
	{
		log("Error: %d(iVecSize) != %d(iJsonSize)\n", iVecSize, iJsonSize);
		return false;
	}
	for (rapidjson::SizeType i = 0; i < oDoc["data"].Capacity(); i++)
	{
		if (oDoc["data"][i].IsNull())
		{
			return false;
		}
		oDoc["data"][i].SetInt(m_stUserInfo.vecData[i]);
	}
	//准备写入数据
	rapidjson::StringBuffer oBuffer;
	rapidjson::Writer<rapidjson::StringBuffer> oWriter(oBuffer);
	oDoc.Accept(oWriter);
	FILE* pFile = fopen(strPath.c_str(), "wb+");
	if (pFile == NULL)
	{
		log("Error: Open File '%s' Failed.\n", strPath.c_str());
		return false;
	}
	fputs(oBuffer.GetString(), pFile);
	fclose(pFile);
	log("UserDataPath:%s", strPath.c_str());
	return true;
}
好了,json文件创建、读取和写入部分先说到这,如果有什么不明白的地方可以联系我。