2013年4月21日 星期日

cocos2d-x jsoncpp load file

很久很久沒碰c++了
近日在寫cocos2d x,因為遊戲中要定義怪物的資料,所以必需載入怪物的資料設定表
因為之前都是用as3寫遊戲,所以希望能做個像在as3的manager
以下就是實作的結果,因為不太會c++,所以要是有用的不好 或是有更好的方法,請不吝告知。

構想:載入本地端的json檔,因為可能會載入很多支json,所以要先存在一個固定的地方,
使用時,再用一個function 取出想要的資料。

環境佈置:
vs 2012 express
cocos2d-x 2.1.2(在這裡跟cocos2d哪個版本沒有影響~)
jsoncpp...呃 版本我也不知道是多少,我是從http://cocos2d.cocoachina.com/bbs/forum.php?mod=viewthread&tid=266&highlight=jsoncpp 這篇文章中的附件載下來使用的
因為這好像是說解決了中文編碼的問題,我就直接拿來用了。可能之後再補上一篇
更詳細的文章。

※所以,總而言之,你需要先確定你能正常的使用jsoncpp。
怎麼確認正不正常。
你先引入
#include "json.h"
再程式碼中加一句 reader
Json::Reader reader;
試著跑跑看,假如沒出現error,那應該就是配置完成了:)

正式開始:

1.建立一個json檔,我的範例
[{"id":100,"name":"Knight","info":"騎士","healthy":110,"attack":7},{"id":101,"name":"mage","info":"法師","healthy":80,"attack":5}]

//不知道為什麼上面的key都會變成大寫,原文件都是小寫- -
把它存成avatar.json

2.解析並儲存json
先看第一個function

void JsonManager::setJsonFromFile(const char* fileName,const char* name,const char* key)
{
Json::Reader reader;
ifstream file(getFullPath(fileName));//載入json檔
CCAssert(file.is_open(), "file is open fail!");
Json::Value root;
if  (!reader.parse(file, root, false )) {
CCAssert(false, "Json::Reader Parse error!");
}
saveList(root,name,key);//寫入
};
setJsonFromFile 這個function主要是載入json檔,並將之儲存起來。
以我寫遊戲的經驗來說,json檔都是一開始就載入進來的,直到要使用的時候,才另外提取裡面的資料。
當然 會有例外,例如解析socket傳來的json資料,就是要當下解析並立即使用的,如果是這樣 ,那就不用saveList了,而是直接return root;

至於saveList

map<string,Json::Value> maps;
map<string,string> key_maps;


void JsonManager::saveList(Json::Value value,const char* name,const char* key)
{
key_maps[name]=key;
maps[name]=value;
}
只是將解析完的資料儲存在map裡。
我也是第一次使用map,用得不是很暢快,我為了連key一起存,所以用了2個map..一個儲json的資料,一個儲key。

這樣子我們就可以在遊戲一進入的時候,使用
#define AVATAR "avatar"
JsonManager::setJsonFromFile("avatar.json",AVATAR,"id");
把json的資料先儲存在記憶體裡了。
參數1是 json檔的檔名
參數2是 這個json的索引值 (因為我們可能會載入一大堆json檔,要提取其中一份使用的key就是靠這個)
參數3是 提取該json資料中的key,在這裡,我的key都會是int的型態。(就是說id指的值會是int)

3.取值
我們已經載進了一份avatar.json 並把化存在 maps["avatar"]裡。
現在要提取這份資料中的其中一筆,KEY是id
使用以下的function getData

Json::Value JsonManager::getData(const char* name,int id)
{
map<string,string>::iterator key_it;
map<string,Json::Value>::iterator it;
key_it=key_maps.find(name);
string key;
if(key_it!=key_maps.end())//先找出在key_maps中有沒有這個key
{
key=key_it->second;//有的話就把key值記下來,這裡也就是之前傳入的"id"
}
else
return NULL;
it=maps.find(name);
if(it!=maps.end())//再找找看maps裡有沒有"avatar"的JSON資料
{
Json::Value arrayObj=it->second;//有找到就繼續比對 其中的id,是不是跟傳入的id是一樣的。
for (unsigned int i = 0; i < arrayObj.size(); i++)
        {   
int s = arrayObj[i][key].asInt();
if(s==id)
return (Json::Value)arrayObj[i];//如果找到一樣的id 就傳回這個資料行
        } 
return NULL;
}
return NULL;
}


在程式碼中的使用如下

 Json::Value myjson =   JsonManager::getData(AVATAR,100);
string s = myjson["name"].asString();
CCLog("%s::::",s.c_str());//印出 knight:::::
string info = myjson["info"].asString();
CCLog("%s::::",info.c_str());//印出 騎士:::::
int atk = myjson["attack"].asInt();
CCLog("%d::::",atk);//印出 7::::::

okay 基本上想要的功能都實現了~

以下附上完整的JsonManager class的原始碼

----JsonManager .h----


#ifndef __JSONMANAGER_H__
#define __JSONMANAGER_H__
#define AVATAR "avatar"
#include "jsoncpp\json.h"
#include "Basic.h"

USING_NS_CC;
class JsonManager{
public:
static void setJsonFromFile(const char* fileName,const char* name,const char* key);
static const char* getFullPath(const char* path);
static void saveList(Json::Value value,const char* name,const char* key);
static Json::Value getData(const char* name,int id);
};
#endif



-----JsonManager .cpp-------


#include "JsonManager.h"
#include <iostream>
#include <fstream>
#include <string>
#include <map>
using namespace std;
map<string,Json::Value> maps;
map<string,string> key_maps;

void JsonManager::setJsonFromFile(const char* fileName,const char* name,const char* key)
{
Json::Reader reader;
ifstream file(getFullPath(fileName));
CCAssert(file.is_open(), "file is open fail!");
Json::Value root;
if  (!reader.parse(file, root, false )) {
CCAssert(false, "Json::Reader Parse error!");
}
saveList(root,name,key);
};
Json::Value JsonManager::getData(const char* name,int id)
{
map<string,string>::iterator key_it;
map<string,Json::Value>::iterator it;
key_it=key_maps.find(name);
string key;
if(key_it!=key_maps.end())
{
key=key_it->second;
}
else
return NULL;
it=maps.find(name);
if(it!=maps.end())
{
Json::Value arrayObj=it->second;
for (unsigned int i = 0; i < arrayObj.size(); i++)
        {   
int s = arrayObj[i][key].asInt();
if(s==id)
return (Json::Value)arrayObj[i];
        } 
return NULL;
}
return NULL;
}
void JsonManager::saveList(Json::Value value,const char* name,const char* key)
{
key_maps[name]=key;
maps[name]=value;
}
const char* JsonManager::getFullPath(const char* path)
{
return cocos2d::CCFileUtils::sharedFileUtils()->fullPathFromRelativeFile(path,path);
};


※Json:Value 可以將他想像成一個object格式,裡面可能是string int或array