博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[置顶] Cocos2d-x 深入解析系列:以XML文件方式保存用户数据
阅读量:6132 次
发布时间:2019-06-21

本文共 14647 字,大约阅读时间需要 48 分钟。

[Cocos2d-x相关教程来源于红孩儿的游戏编程之路CSDN博客地址

红孩儿Cocos2d-X学习园地QQ3群:205100149,47870848

 

           Cocos2d-x 深入解析系列:以XML文件方式保存用户数据

另:本章所用Cocos2d-x版本为: 

 

              大家好,今天我们来学习一下如何使用XML文件方式来保存游戏中的用户数据。在使用Cocos2d-x开发游戏的过程中,我们经常会使用XML来存储用户存档数据,而这些XML我们该如何生成呢?Cocos2d-x提供了一个类CCUserDefault以方便我们随时将需要的数据生成XML文件。

 

打开CCUserDefault.h:

#ifndef __SUPPORT_CCUSERDEFAULT_H__#define __SUPPORT_CCUSERDEFAULT_H__//加入平台所用的头文件#include "platform/CCPlatformMacros.h"#include 
//使用Cocos2d命名空间NS_CC_BEGIN//定义类CCUserDefaultclass CC_DLL CCUserDefault{public: //析构 ~CCUserDefault(); //从指定的键中取得布尔值 bool getBoolForKey(const char* pKey); //从指定的键中取得布尔值,如果没有则返回默认参数 bool getBoolForKey(const char* pKey, bool defaultValue); //从指定的键中取得整数值 int getIntegerForKey(const char* pKey); //从指定的键中取得整数值,如果没有则返回默认参数 int getIntegerForKey(const char* pKey, int defaultValue); //从指定的键中取得浮点值 float getFloatForKey(const char* pKey); //从指定的键中取得浮点值,如果没有则返回默认参数 float getFloatForKey(const char* pKey, float defaultValue); //从指定的键中取得双精度值 double getDoubleForKey(const char* pKey); //从指定的键中取得双精度值,如果没有则返回默认参数 double getDoubleForKey(const char* pKey, double defaultValue); //从指定的键中取得字符串值 std::string getStringForKey(const char* pKey); //从指定的键中取得字符串值,如果没有则返回默认参数 std::string getStringForKey(const char* pKey, const std::string & defaultValue); //设置指定键的布尔值 void setBoolForKey(const char* pKey, bool value); //设置指定键的整数值 void setIntegerForKey(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); //立即将XML数据写入文件 void flush(); //取得单例的指针 static CCUserDefault* sharedUserDefault(); //释放单例 static void purgeSharedUserDefault(); //取得保存后的XML文件路径 const static std::string& getXMLFilePath();private: //因为是单例,构造函数私有化 CCUserDefault(); //创建XML文件 static bool createXMLFile(); //XML文件是否存在 static bool isXMLFileExist(); //初始化XML文件 static void initXMLFilePath(); //单例的指针 static CCUserDefault* m_spUserDefault; //XML文件的路径 static std::string m_sFilePath; //XML文件是否已经被初始化 static bool m_sbIsFilePathInitialized;};NS_CC_END#endif // __SUPPORT_CCUSERDEFAULT_H__

CCUserDefault.cpp:

#include "CCUserDefault.h"#include "platform/CCCommon.h"#include "platform/CCFileUtils.h"#include 
#include
// XML的根节点名称#define USERDEFAULT_ROOT_NAME "userDefaultRoot"//默认的XML文件名称#define XML_FILE_NAME "UserDefault.xml"//使用C++标准库的命名空间using namespace std;//使用Cocos2d命名空间NS_CC_BEGIN//单例的指针static xmlDocPtr g_sharedDoc = NULL;//静态全局函数,用于取得一个键的XML结点指针static xmlNodePtr getXMLNodeForKey(const char* pKey, xmlNodePtr *rootNode){ //定义用于存储返回结果的临时指针变量并置空 xmlNodePtr curNode = NULL; //键值的有效性判断 if (! pKey) { return NULL; } do { //取得根结点 *rootNode = xmlDocGetRootElement(g_sharedDoc); if (NULL == *rootNode) { CCLOG("read root node error"); break; } //循环查询相应的键结点 curNode = (*rootNode)->xmlChildrenNode; while (NULL != curNode) { //如果键结点名称与查询键名称一致中断退出循环 if (! xmlStrcmp(curNode->name, BAD_CAST pKey)) { break; } //否则指针指向下一个结点继续循环 curNode = curNode->next; } } while (0); //返回结点指针 return curNode;}//取得相应的键值static inline const char* getValueForKey(const char* pKey){ //定义用于存储返回结果的临时字符指针变量并置空 const char* ret = NULL; //定义结点指针变量取得相应的键结点。 xmlNodePtr rootNode; xmlNodePtr node = getXMLNodeForKey(pKey, &rootNode); // 如果找到了相应的结点,取得结点的内存值转换为字符指针返回。 if (node) { ret = (const char*)xmlNodeGetContent(node); } return ret;}//设置相应的键值static void setValueForKey(const char* pKey, const char* pValue){ xmlNodePtr rootNode; xmlNodePtr node; // 有效性判断 if (! pKey || ! pValue) { return; } // 取得相应的键结点 node = getXMLNodeForKey(pKey, &rootNode); // 如果找到,设置结点的值为pValue if (node) { xmlNodeSetContent(node, BAD_CAST pValue); } else { //如果找不到键值,则生成相应的键结点和键值结点并放入根结点下。 if (rootNode) { //先创建键结点。 xmlNodePtr tmpNode = xmlNewNode(NULL, BAD_CAST pKey); //再创建健值结点。 xmlNodePtr content = xmlNewText(BAD_CAST pValue); //将键点点放到根结点下。 xmlAddChild(rootNode, tmpNode); //将键帧结点放到键结点下。 xmlAddChild(tmpNode, content); } }}//初始化单例指针置空CCUserDefault* CCUserDefault::m_spUserDefault = 0;//初始化XML文件路径为空string CCUserDefault::m_sFilePath = string("");//初始化文件路径是否被初始化的标记值为falsebool CCUserDefault::m_sbIsFilePathInitialized = false;//析构CCUserDefault::~CCUserDefault(){ //将数据写入文件 flush(); //释放相应的XML文件 if (g_sharedDoc) { xmlFreeDoc(g_sharedDoc); g_sharedDoc = NULL; } //单例指针置空 m_spUserDefault = NULL;}//构造CCUserDefault::CCUserDefault(){ //读取相应的XML文件。 g_sharedDoc = xmlReadFile(getXMLFilePath().c_str(), "utf-8", XML_PARSE_RECOVER);}//释放单例void CCUserDefault::purgeSharedUserDefault(){ CC_SAFE_DELETE(m_spUserDefault); m_spUserDefault = NULL;}//从指定的键中取得布尔值bool CCUserDefault::getBoolForKey(const char* pKey) { return getBoolForKey(pKey, false); }//从指定的键中取得布尔值,如果没有则返回默认参数。bool CCUserDefault::getBoolForKey(const char* pKey, bool defaultValue){ const char* value = getValueForKey(pKey); bool ret = defaultValue; if (value) { ret = (! strcmp(value, "true")); xmlFree((void*)value); } return ret;}//从指定的键中取得整数值int CCUserDefault::getIntegerForKey(const char* pKey){ return getIntegerForKey(pKey, 0);}//从指定的键中取得整数值,如果没有则返回默认参数int CCUserDefault::getIntegerForKey(const char* pKey, int defaultValue){ const char* value = getValueForKey(pKey); int ret = defaultValue; if (value) { ret = atoi(value); xmlFree((void*)value); } return ret;}//从指定的键中取得浮点值float CCUserDefault::getFloatForKey(const char* pKey){ return getFloatForKey(pKey, 0.0f);}//从指定的键中取得浮点值,如果没有则返回默认参数。float CCUserDefault::getFloatForKey(const char* pKey, float defaultValue){ float ret = (float)getDoubleForKey(pKey, (double)defaultValue); return ret;}//从指定的键中取得双精度值double CCUserDefault::getDoubleForKey(const char* pKey){ return getDoubleForKey(pKey, 0.0);}//从指定的键中取得双精度值,如果没有则返回默认参数。double CCUserDefault::getDoubleForKey(const char* pKey, double defaultValue){ const char* value = getValueForKey(pKey); double ret = defaultValue; if (value) { ret = atof(value); xmlFree((void*)value); } return ret;}//从指定的键中取得字符串值std::string CCUserDefault::getStringForKey(const char* pKey){ return getStringForKey(pKey, "");}//从指定的键中取得字符串值,如果没有则返回默认参数string CCUserDefault::getStringForKey(const char* pKey, const std::string & defaultValue){ const char* value = getValueForKey(pKey); string ret = defaultValue; if (value) { ret = string(value); xmlFree((void*)value); } return ret;}//设置指定键的布尔值void CCUserDefault::setBoolForKey(const char* pKey, bool value){ // save bool value as string if (true == value) { setStringForKey(pKey, "true"); } else { setStringForKey(pKey, "false"); }}//设置指定键的整数值void CCUserDefault::setIntegerForKey(const char* pKey, int value){ // check key if (! pKey) { return; } // format the value char tmp[50]; memset(tmp, 0, 50); sprintf(tmp, "%d", value); setValueForKey(pKey, tmp);}//设置指定键的浮点值void CCUserDefault::setFloatForKey(const char* pKey, float value){ setDoubleForKey(pKey, value);}//设置指定键的双精度值void CCUserDefault::setDoubleForKey(const char* pKey, double value){ // check key if (! pKey) { return; } // format the value char tmp[50]; memset(tmp, 0, 50); sprintf(tmp, "%f", value); setValueForKey(pKey, tmp);}//设置指定键的字符串值void CCUserDefault::setStringForKey(const char* pKey, const std::string & value){ // check key if (! pKey) { return; } setValueForKey(pKey, value.c_str());}//取得单例CCUserDefault* CCUserDefault::sharedUserDefault(){ //初始化XML文件 initXMLFilePath(); //如果文件不存在则创建,如果创建不成功返回失败。 if ((! isXMLFileExist()) && (! createXMLFile())) { return NULL; } //如果当前单例指针为空,创建单例 if (! m_spUserDefault) { m_spUserDefault = new CCUserDefault(); } //返回单例指针 return m_spUserDefault;}//XML文件是否存在。bool CCUserDefault::isXMLFileExist(){ //创建一个文件指针打开相应的文件。 FILE *fp = fopen(m_sFilePath.c_str(), "r"); bool bRet = false; //看是否能打开以判断是否存在。 if (fp) { bRet = true; fclose(fp); } return bRet;}//初始化XML文件路径void CCUserDefault::initXMLFilePath(){ //如果初始化的标记为false,组合出文件字符串。 if (! m_sbIsFilePathInitialized) { //文件路径名为文件系统的写入路径[后面详解]下的“UserDefault.xml”。 m_sFilePath += CCFileUtils::sharedFileUtils()->getWriteablePath() + XML_FILE_NAME; m_sbIsFilePathInitialized = true; } }//创建XML文件bool CCUserDefault::createXMLFile(){ bool bRet = false; //定义临时的XML文档指针 xmlDocPtr doc = NULL; //使用do-while框架结构来随时中断 do { // 创建一个新的1.0版的XML文档 doc = xmlNewDoc(BAD_CAST"1.0"); if (doc == NULL) { CCLOG("can not create xml doc"); break; } // 创建根结点 xmlNodePtr rootNode = xmlNewNode(NULL, BAD_CAST USERDEFAULT_ROOT_NAME); if (rootNode == NULL) { CCLOG("can not create root node"); break; } //设置创建的结点为XML文档中的根结点 xmlDocSetRootElement(doc, rootNode); //保存XML文件 xmlSaveFile(m_sFilePath.c_str(), doc); bRet = true; } while (0); // 释放文档指针 if (doc) { xmlFreeDoc(doc); } //返回成败 return bRet;}//取得XML文件路径const string& CCUserDefault::getXMLFilePath(){ return m_sFilePath;}//立即将XML数据写入文件void CCUserDefault::flush(){ // 如果文档有效则进行保存文档到文件中。 if (g_sharedDoc) { xmlSaveFile(CCUserDefault::sharedUserDefault()->getXMLFilePath().c_str(), g_sharedDoc); }}NS_CC_END

              这引CCUserDefault类写的真是不错。非常简洁好用。但我们要明白“文件系统的写入路径”是什么?

              在CCFileUtils.cpp中找到相应的函数:

//取得文件写入路径

string CCFileUtils::getWriteablePath(){	// 取得当前程序所在目录	char full_path[_MAX_PATH + 1];	::GetModuleFileNameA(NULL, full_path, _MAX_PATH + 1);	// 如果是Release模式#ifndef _DEBUG		// 取得移除路径的文件名		char *base_name = strrchr(full_path, '\\');		if(base_name)		{			char app_data_path[_MAX_PATH + 1];			// 取得系统文件夹应用程序数据目录,如C:\Documents and Settings\username\Local Settings\Application Data,可参看: http://wenku.baidu.com/view/412cfc02f78a6529647d53e5.html			if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, app_data_path)))			{				//创建字符串ret存放取出的路径				string ret((char*)app_data_path);				//字符串尾部加上文件名。				ret += base_name;				// 去除扩展名并加上”\\”				ret = ret.substr(0, ret.rfind("."));				ret += "\\";				// 创建相应的目录				if (SUCCEEDED(SHCreateDirectoryExA(NULL, ret.c_str(), NULL)))				{					//如果成功返回ret。					return ret;				}			}		}#endif // not defined _DEBUG	//创建字符串ret存放当前程序所在目录。	string ret((char*)full_path);	// 截取带”\\”部分的路径。	ret =  ret.substr(0, ret.rfind("\\") + 1);	//返回ret。	return ret;}

 

              这个函数对于DEBUG和RELEASE模式有区别处理,DEBUG模式取出的路径即为当前程序所在目录,RELEASE模式则在系统目录下创建当前程序名称的目录并返回。

 

              接下来我们看一下Cocos2d-x在例子中的具体使用,找到TestCpp中的UserDefaultTest。

UserDefaultTest.h:

#ifndef _USERDEFAULT_TEST_H_#define _USERDEFAULT_TEST_H_#include "cocos2d.h"#include "../testBasic.h"//创建一个层用于处理XML数据class UserDefaultTest : public CCLayer{public:    UserDefaultTest();    ~UserDefaultTest();private:    void doTest();};//演示用的场景class UserDefaultTestScene : public TestScene{public:    virtual void runThisTest();};#endif // _USERDEFAULT_TEST_H_

对应的CPP:

// 开启COCOS2D的DEBUG标记#define COCOS2D_DEBUG 1#include "UserDefaultTest.h"#include "stdio.h"#include "stdlib.h"//层的构造函数。UserDefaultTest::UserDefaultTest(){	//取得屏幕大小,创建文字标签提示。    CCSize s = CCDirector::sharedDirector()->getWinSize();CCLabelTTF* label = CCLabelTTF::create("CCUserDefault test see log", "Arial", 28);//将标签放到当前层中并置于屏幕中央。    addChild(label, 0);    label->setPosition( ccp(s.width/2, s.height-50) );	//调用测试函数。    doTest();}void UserDefaultTest::doTest(){	//开始打印日志。    CCLOG("********************** init value ***********************");    // 创建CCUserDefault单例并创建相应的数据类型键,设置其键值。    CCUserDefault::sharedUserDefault()->setStringForKey("string", "value1");    CCUserDefault::sharedUserDefault()->setIntegerForKey("integer", 10);    CCUserDefault::sharedUserDefault()->setFloatForKey("float", 2.3f);    CCUserDefault::sharedUserDefault()->setDoubleForKey("double", 2.4);    CCUserDefault::sharedUserDefault()->setBoolForKey("bool", true);    // 设置完后,打印各类型键取出的值。    string ret = CCUserDefault::sharedUserDefault()->getStringForKey("string");    CCLOG("string is %s", ret.c_str());    double d = CCUserDefault::sharedUserDefault()->getDoubleForKey("double");    CCLOG("double is %f", d);    int i = CCUserDefault::sharedUserDefault()->getIntegerForKey("integer");    CCLOG("integer is %d", i);    float f = CCUserDefault::sharedUserDefault()->getFloatForKey("float");    CCLOG("float is %f", f);    bool b = CCUserDefault::sharedUserDefault()->getBoolForKey("bool");    if (b)    {        CCLOG("bool is true");    }    else    {        CCLOG("bool is false");    }        //CCUserDefault::sharedUserDefault()->flush();    CCLOG("********************** after change value ***********************");    // 改变相应键的键值。    CCUserDefault::sharedUserDefault()->setStringForKey("string", "value2");    CCUserDefault::sharedUserDefault()->setIntegerForKey("integer", 11);    CCUserDefault::sharedUserDefault()->setFloatForKey("float", 2.5f);    CCUserDefault::sharedUserDefault()->setDoubleForKey("double", 2.6);    CCUserDefault::sharedUserDefault()->setBoolForKey("bool", false);	//将XML数据保存到相应文件中。    CCUserDefault::sharedUserDefault()->flush();    // 再次打印各键值。    ret = CCUserDefault::sharedUserDefault()->getStringForKey("string");    CCLOG("string is %s", ret.c_str());    d = CCUserDefault::sharedUserDefault()->getDoubleForKey("double");    CCLOG("double is %f", d);    i = CCUserDefault::sharedUserDefault()->getIntegerForKey("integer");    CCLOG("integer is %d", i);    f = CCUserDefault::sharedUserDefault()->getFloatForKey("float");    CCLOG("float is %f", f);    b = CCUserDefault::sharedUserDefault()->getBoolForKey("bool");    if (b)    {        CCLOG("bool is true");    }    else    {        CCLOG("bool is false");    }}//析构UserDefaultTest::~UserDefaultTest(){}//场景启动时调用。void UserDefaultTestScene::runThisTest(){	//创建一个演示用的层并放到当前演示场景中。    CCLayer* pLayer = new UserDefaultTest();    addChild(pLayer);	//启动当前场景。    CCDirector::sharedDirector()->replaceScene(this);    pLayer->release();}

当我们在DEBUG模式下运行此演示后程序截图为:

 

在程序的运行目录会出现:

 

              用IE打开后可以看到相应的键值数据。这样我们便学会了如何存储游戏中用到的数据到XML文件中。下课!

 

转载地址:http://fmxua.baihongyu.com/

你可能感兴趣的文章
Jquery 大纲
查看>>
swift内存管理
查看>>
log4j2研究
查看>>
可拖拽排序合并内容并分类的自定义控件
查看>>
mysql 权限管理
查看>>
无器械健身和健身房器械训健身比较 2
查看>>
基于Ping和Telnet/NC的监控脚本案例分析
查看>>
js下判断 iframe 是否加载完成的完美方法
查看>>
Visual Studio 2017 调试器的工作进程(msvsmon.exe)意外退出 调试将终止
查看>>
Express框架是什么
查看>>
C# install-package:"xx"已拥有为“xxx”定义的依赖项
查看>>
2018广东公考笔试成绩
查看>>
小程序之仿抖音短视频与分布式云部署的那些事儿~
查看>>
如何模拟alert/confirm/prompt实现阻断程序运行
查看>>
Linux日志每日备份脚本
查看>>
在Windows下搭建基于nginx的视频直播和点播系统
查看>>
springcloud之自定义简易消费服务组件
查看>>
WPF 流打印
查看>>
C#调用XmlSerializer序列化时生成CDATA节点解决方法
查看>>
Hive中创建S3的外部表
查看>>