Cocos2dx学习第10章(csv 和 json 简单应用)

1.retain 和 release

Cocos2dx有一套内存管理机制。retain表示保持引用,以免被释放。一旦对象调用autorelease函数,这个对象就被cocos的内存管理机制盯上了,一旦他没有被认领,那他就会被释放。如下,他就会被内存机制自动释放:

    // add "HelloWorld" splash screen"
    sp = Sprite::create("HelloWorld.png");

    // position the sprite on the center of the screen
    sp->setPosition(Vec2(visibleSize.width/2 + origin.x,visibleSize.height/2 + origin.y));

    // add the sprite as a child to this layer
    //this->addChild(sprite,0);
这样会导致在这个init函数执行完时,自动释放了sp。导致下面的调用出错:
void HelloWorld::menuCloseCallback(Ref* pSender)
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT)
	MessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert");
    return;
#endif
	sp->getPosition();
    Director::getInstance()->end();

#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    exit(0);
#endif
}

要解决这个问题,必须为sp调用retain函数。

sp->retain();
(1)首先,要想让对象参与内存管理机制,必须继承Ref类(Node,Layer)等

(2)其次,调用对象的autorelease函数,对象就会被cocos2dx的内存管理机制盯上。一旦没人认领,就会释放。

(3)如果不想让对象被释放,那么就要调用对象的retain函数,一旦调用,就永远不会被内存管理机制杀掉。所以要调用release函数来进行释放。

(4)addChild();会调用retain函数,所以把对象添加到Layer时,就不需要再次调用retain函数了,当Layer释放时,他也会去调用子对象的release函数。

(5)当你把一个对象作为成员变量时,并且没有把对象addChild到另外一个对象时,就需要调用retain函数。

(6)最后,必须调用了autorelease函数之后,retain函数和release函数才会生效。一般create()函数都会调用autorelease函数。

Sprite* Sprite::create(const std::string & filename)
{
    Sprite* sprite = new Sprite();
    if(sprite && sprite->initWithFile(filename))
    {
        sprite->autorelease();//retain release 必须有调用autorelease才会生效
        return sprite; 
    }
....
}

2.数据保存

Cocos2dx提供保存数据的功能,类:UserDefault,他将数据保存到xml文件里,下次我们就直接从xml那里读取。适合简单的数据保存。

常用的api接口:

//set 数据

void setBoolForKey(const char* pKey,bool value);
void setIntergerForKey(const char* pKey,int value);
void setFloatForKey(const char* pKey,float value);
void setDoubleForKey(const char* pKey,double value);
void setStringForKey(const char* pKey,const std::string & value);
//get数据
bool getBoolForKey(const char* pKey);
bool getBoolForKey(const char* pKey,bool defaultValue);
int getIntegerForKey(const char* pKey);
int getIntegerForKey(const char* pKey,int defaulerVaule);
float getFloatForKey(const char* pKey);
float getFloatForKey(const char* pKey,float defultValue);
...
写个例子验证下:
	UserDefault::getInstance()->setStringForKey("name","LLLdreamer");
	std::string name = UserDefault::getInstance()->getStringForKey("name","none");
	log("name = %s",name.c_str());

3.Csv配置文件读取

csv文件很简单,比如纯粹以逗号作为分隔符的文本文件。


(1)首先来编写字符工具类,用于解析字符。

#ifndef _STRINGUTIL_H_
#define _STRINGUTIL_H_
#include "cocos2d.h"
USING_NS_CC;
class StringUtil:public Ref
{
public:
	static StringUtil* getInstance();
	virtual bool init();
	ValueVector split(const char* srcStr,const char* sSep);
private:
	static StringUtil* m_StringUtil;
};
#endif
cpp文件
#include"StringUtil.h"
StringUtil* StringUtil::m_StringUtil = NULL;
StringUtil* StringUtil::getInstance()//单例
{
	if(m_StringUtil == NULL)
	{
		m_StringUtil = new StringUtil();
		if(m_StringUtil && m_StringUtil->init())
		{
			m_StringUtil->autorelease();
			return m_StringUtil;
		}
		else
		{
			CC_SAFE_DELETE(m_StringUtil);
			m_StringUtil = NULL;
		}
	}
	return m_StringUtil;
}
bool StringUtil::init()
{
	return true;
}
ValueVector StringUtil::split(const char* strSrc,const char* sSep)//和java的spit函数类似
{
	ValueVector stringList;
	int size = strlen(strSrc);
	Value str = Value(strSrc);
	int startIndex = 0;
	int endIndex = 0;

	endIndex = str.asString().find(sSep);
	std::string lineStr;
	while(endIndex > 0)
	{
		lineStr = str.asString().substr(startIndex,endIndex);
		stringList.push_back(Value(lineStr));
		str = Value(str.asString().substr(endIndex+1,size));
		endIndex = str.asString().find(sSep);
	}
	if(str.asString().compare("") != 0)
	{
		stringList.push_back(Value(str.asString()));
	}
	return stringList;
}
测试用例,可以正常的输出了:
	auto strList = StringUtil::getInstance()->split("bingzong,dizong,tangzong,hongzong",",");
	for(auto value:strList)
	{
		log("value = %s",value.asString().c_str());
	}
(2)csv文件对象类

我们把csv文件当做一个对象来处理,新建一个CsvData对象,代表一份Csv文件数据。

#ifndef _CSVDATA_H_
#define _CSVDATA_H_
#include "cocos2d.h"
USING_NS_CC;
class CsvData:public Ref
{
public:
	CREATE_FUNC(CsvData);
	virtual bool init();
	void addLineData(ValueVector lineData);//添加一行数据
	ValueVector getStringLineData(int iLine);//获取某行数据
	Size getRowColNum();//获取行列数据
private:
	ValueVector m_allLinesVec;
	int m_iColNum;
};
#endif
#include"CsvData.h"
void CsvData::addLineData(ValueVector lineData)
{
	m_allLinesVec.push_back(Value(lineData));
	m_iColNum = m_allLinesVec.size();
}
ValueVector CsvData::getStringLineData(int iLine)
{
	return m_allLinesVec.at(iLine).asValueVector();
}
Size CsvData::getRowColNum()
{
	Size size = Size();
	size.width = m_allLinesVec.size();
	if(size.width > 0)
	{
		size.height = m_allLinesVec.at(0).asValueVector().size();
	}
	return size;
}
bool CsvData::init()
{
	return true;
}
(3)csv文件读取的工具类
#ifndef _CSVUTIL_H_
#define _CSVUTIL_H_
#include "cocos2d.h"
#include "CsvData.h"
#include "StringUtil.h"
USING_NS_CC;
class CsvUtil:public Ref
{
public:
	static CsvUtil* getInstance();
	virtual bool init();
	void loadFile(const char* path);//加载配置文件
	Value getValue(int iRow,int iCol,const char* csvFilePath);//获取某行某列的值
	const std::string getString(int iRow,const char* csvFilePath);//获取值并转化为字符串
	const int getInt(int iRow,const char* csvFilePath);//获取值并转换为整型
	const Size getFileRowColNum(const char* csvFilePath);//获取行和列的数量
	const int findValueIntWithLine(const char* chValue,int iValueCol,const char* csvFilePath);
	const float getFloat(int iRow,const char* csvFilePath);
	const bool getBool(int iRow,const char* csvFilePath);
private:
	static CsvUtil* m_CsvUtil;
	Map<std::string,CsvData*> mCsvMap;
};
#endif
#include "CsvUtil.h"
CsvUtil* CsvUtil::m_CsvUtil = NULL;
CsvUtil* CsvUtil::getInstance()
{
	if(m_CsvUtil == NULL)
	{
		m_CsvUtil = new CsvUtil();
		if(m_CsvUtil && m_CsvUtil->init())
		{
			m_CsvUtil->autorelease();
			m_CsvUtil->retain();
		}
		else
		{
			CC_SAFE_DELETE(m_CsvUtil);
			m_CsvUtil = NULL;
		}
	}
	return m_CsvUtil;
}

bool CsvUtil::init()
{
	return true;
}

const std::string CsvUtil::getString(int iRow,const char* csvFilePath)
{
	Value colValue = getValue(iRow,iCol,csvFilePath);
	return colValue.asString();
}
const int CsvUtil::getInt(int iRow,csvFilePath);
	return colValue.asInt();
}
const float CsvUtil::getFloat(int iRow,csvFilePath);
	return colValue.asFloat();
}
const bool CsvUtil::getBool(int iRow,csvFilePath);
	return colValue.asBool();
}
void CsvUtil::loadFile(const char* sPath)
{
	CsvData* csvdata = CsvData::create();
	std::string str = FileUtils::getInstance()->getStringFromFile(sPath);
	ValueVector lineList = StringUtil::getInstance()->split(str.c_str(),"\n");
	for(auto value:lineList)
	{
		ValueVector tArr = StringUtil::getInstance()->split(value.asString().c_str(),");
		csvdata->addLineData(tArr);
	}
	mCsvMap.insert(sPath,csvdata);
}

const Size CsvUtil::getFileRowColNum(const char* csvFilePath)
{
	auto csvData = mCsvMap.at(csvFilePath);
	if(csvData == nullptr)
	{
		loadFile(csvFilePath);
		csvData = mCsvMap.at(csvFilePath);
	}
	Size size = csvData->getRowColNum();
	return size;
}
Value CsvUtil::getValue(int iRow,const char* csvFilePath)
{
	auto csvData = mCsvMap.at(csvFilePath);
	if(csvData == nullptr)
	{
		loadFile(csvFilePath);
		csvData = mCsvMap.at(csvFilePath);		
	}
	ValueVector rowVector = csvData->getStringLineData(iRow);
	Value colValue = rowVector.at(iCol);
	return colValue;
}
例子简单,不在赘述。

测试文件中,调用如下语句,便可:

	const char* sPath = "Monster.csv";//文件必须是存放在resource文件目录下,才能加载成功
	CsvUtil::getInstance()->loadFile(sPath);
	Value firstMonsterName = CsvUtil::getInstance()->getValue(2,1,sPath);
	Value secondMonsterHP = CsvUtil::getInstance()->getValue(3,3,sPath);
	log("firstmonstername = %s",firstMonsterName.asString().c_str());
	log("secondMonsterName = %s",secondMonsterHP.asString().c_str());

4.Json简介

Json也是一种文本文件,一种配置文件,他的格式与csv不一样,类似如下:
{
    “id”:1,"name":"bingzai","IQ":99.5
}
有点类似map容器,一个key对应一个值。
那么如何解析json文件呢,我们直接用JsonCpp的库。可到这里下载:http://sourceforge.net/projects/jsoncpp/

下载解压后,只需要src/lib_json和include/json这两个目录。最后这两个文件的所有文件都都放到Class/Json目录下面。

这样进行编译还会报错,那么尝试在项目上选择属性,配置属性,C/C++,常规,附加包含目录,最前面加上../Classes

#include "json\writer.h"
#include "json\reader.h"
#include "json\value.h"

void HelloWorld::readJson()
{
	Json::Reader reader;
	Json::Value root;
	std::string data = FileUtils::getInstance()->getStringFromFile("test1.json");
	if(reader.parse(data,root,false))
	{
		log("id = %d",root["id"].asInt());
		log("name = %s",root["name"].asCString());
		log("IQ = %f",root["IQ"].asDouble());
	}
}
另外,读取嵌套,数组类的JSON,还有写入的,直接参考以下接口:
void HelloWorld::readChildJson()//读取嵌套
{
    Json::Reader reader;
    Json::Value root;

    std::string data = FileUtils::getInstance()->getStringFromFile("test2.json");

    if (reader.parse(data,false) == true)
    {
        log("id=%d",root["id"].asInt());
        log("name=%s",root["name"].asCString());
        log("IQ=%f",root["IQ"].asDouble());

        log("msg value money=%d",root["msg"]["money"].asInt());
        log("msg value say=%s",root["msg"]["say"].asCString());
    }
}

void HelloWorld::readArrayJson()//数组类的JSON
{
    Json::Reader reader;
    Json::Value root;

    std::string data = FileUtils::getInstance()->getStringFromFile("test3.json");

    if (reader.parse(data,false) == true)
    {
        int iNum = root.size();
        for (int i = 0; i < iNum; i++)
        {
            log("id=%d",root[i]["id"].asInt());
            log("model=%s",root[i]["model"].asCString());
            log("atk=%d",root[i]["atk"].asInt());
        }
    }
}

void HelloWorld::writeJson()//写入
{

    Json::Value root;
    Json::FastWriter writer;

    root["name"] = "Who";
    root["IQ"] = 999;

    std::string json_file = writer.write(root);

    FILE* file = fopen("testWirte.json","w");
    fprintf(file,json_file.c_str());
    fclose(file);
}

相关文章

    本文实践自 RayWenderlich、Ali Hafizji 的文章《...
Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@1...
第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从C...
    Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发...
1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《...
   Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试...