【转】CCUserDefault类深入分析——2013-08-25 22
http://game.dapps.net/gamedev/game-engine/8792.html
另:本章所用Cocos2d-x版本为: 2.1.1 (2013-01-28)
大家好,今天我们来学习一下如何使用XML文件方式来保存游戏中的用户数据。在使用Cocos2d-x开发游戏的过程中,我们经常会使用XML来存储用户存档数据,而这些XML我们该如何生成呢?Cocos2d-x提供了一个类CCUserDefault以方便我们随时将需要的数据生成XML文件。
打开CCUserDefault.h:
#ifndef __SUPPORT_CCUSERDEFAULT_H__
#define __SUPPORT_CCUSERDEFAULT_H__
//加入平台所用的头文件
#include "platform/CCPlatformMacros.h"
#include <string>
//使用Cocos2d命名空间
NS_CC_BEGIN //定义类CCUserDefault
class 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__
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
CCUserDefault.cpp:
#include "CCUserDefault.h"
#include "platform/CCCommon.h"
#include "platform/CCFileUtils.h"
#include <libxml/parser.h>
#include <libxml/tree.h> // 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("");
//初始化文件路径是否被初始化的标记值为false
bool 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
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
这引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;
}
【转】CCUserDefault类深入分析——2013-08-25 22的更多相关文章
- http://www.blogjava.net/xylz/archive/2013/08/05/402405.html
http://www.blogjava.net/xylz/archive/2013/08/05/402405.html
- http://www.cnblogs.com/flyoung2008/archive/2013/08/11/3251148.html
http://www.cnblogs.com/flyoung2008/archive/2013/08/11/3251148.html
- 北京设计模式学习组bjdp.org第7次活动(2013.08.04)回顾会纪要
时间:2013.08.04,9am-7pm 地点:北京龙泉寺(北京凤凰岭风景区内) 参加人数:北京龙泉寺信息中心(20人).北京设计模式学习组(9人) 活动要点: 1)寺院巡礼:义工师兄带领参观寺院. ...
- 〖Linux〗(2013.08.02)使用ctag+cscope查看Android源代码
1. 安装ctags和cscope sudo apt-get install -y exuberant-ctags cscope 2. vimrc中的配置 """&quo ...
- 软件工程项目组Z.XML会议记录 2013/09/25
软件工程项目组Z.XML会议记录 [例会时间]2013年9月25日周三21:30-22:10 [例会形式]小组讨论 [例会地点]三号公寓楼会客厅 [例会主持]李孟 [会议记录]肖俊鹏 会议整体流程 签 ...
- 更新整理本人所有博文中提供的代码与工具(C++,2013.08)
为了更方便地管理博文中涉及的各种代码与工具资源,现在把这些资源迁移到 Google Code 中,有兴趣者可前往下载. C++ 1.<通用高性能 Windows Socket 组件 HP-Soc ...
- Cheatsheet: 2013 08.20 ~ 08.31
.NET Protobuf-net: the unofficial manual 5 Common C# Misconceptions What is new in the Mono project ...
- [Buzz.Today]2013.08.06
# 用简单方案解决80%的问题,再慢慢解决剩下20% [先完成,后完美]美国工程院院士.Google Fellow辛格的哲学:先用个简单方案解决80%的问题,再慢慢解决剩下20%.许多优秀人才都败在这 ...
- JpGraph使用详解http://5ydycm.blog.51cto.com/115934/177498 http://www.cnblogs.com/txw1958/archive/2013/08/18/php-charts.html
下载 在官方网站 http://www.aditus.nu/jpgraph/ 下载jpgraph,其中1.X系列是用于PHP4的,2.X系列是用于PHP5的. 安装 将下载的得到的jpgraph压缩文 ...
随机推荐
- Log4delphi使用心得
因为delphi不是我的主力开发工具,所有一直没有使用一个正式的日志组件.偶尔要记日志时,就复制同事的一个简单的文件日志函数.现在又要用到delphi日志了,决定找个通用的日志组件,造福共事的Delp ...
- socket编程在windows和linux下的区别
如无其它说明,本文所指Linux均表示2.6内核Linux,GCC编译器,Windows均表示Windows XP系统,Visual Studio 2005 sp1编译环境. 下面大概分几个方面进行罗 ...
- keytool 错误: java.io.FileNotFoundException: 拒绝访问
keytool 错误: java.io.FileNotFoundException: 拒绝访问 打开命令行,切换到D:\AndroidStudioProjects\MyApplication\app目 ...
- 在 Windows Azure 上部署并定制化 FreeBSD 虚拟机镜像
发布于 2014-12-11 作者 陈阳 FreeBSD 基础镜像现已登陆中国的 VM Depot! 对于青睐 BSD 而非 Linux 的开源爱好者来说,这无疑是个好消息.同时,随着该基础镜像 ...
- 从零开始学习jQuery (八) 插播:jQuery实施方案
一.摘要 本系列文章将带您进入jQuery的精彩世界, 其中有很多作者具体的使用经验和解决方案, 即使你会使用jQuery也能在阅读中发现些许秘籍. 本篇文章属于临时插播, 用于介绍我在本公司的j ...
- tomcat7 使用log4j进行日志记录
将 tomcat-juli.jar 文件放置到 $CATALINA_BASE/bin 目录(实际上,该目录下已经有了) 从 log4j 网站下载 jar 包,并放置于 $CATALINA_BASE/l ...
- Spring概述--1
1.1.1 Spring是什么 Spring是一个开源的轻量级Java SE(Java 标准版本)/Java EE(Java 企业版本)开发应用框架,其目的是用于简化企业级应用程序开发.应用程序是由 ...
- [liu yanling]测试方法
1.定义 是把所有可能的输入数据,即程序的输入域划分成若干部分(子集),然后从每一个子集中选取少数具有代表性的数据作为测试用例.该方法是一种重要的,常用的黑盒测试用例设计方法. 2.划分等价类 等价类 ...
- Windows下搭建Nginx实现负载均衡
环境:本次测试,使用两台电脑,分别是 192.168.0.1,192.168.0.2. 其中Nginx也部署在 192.168.0.1 电脑上,所以 PC1 的IIS端口不能使用80,因为Nginx需 ...
- 2015年9月28日JQuery提前预习预热笔记
visual studio下载2010 2010与2008不一样,2008需要添加补丁,采用调用对象.2010可以直接用. JQuery=$ 是函数是方法是对象 念J快儿,念doler 开发人员工具( ...