最近在学习一个飞行射击类的单机游戏,其中涉及到了对于游戏数据的存储,由于数据比较简单,所以就使用了cocos2d-x自带的CCUserDefault类,使用起来比较简单,但是好奇的我还是比较想更深层次的去了解一下这个类,因此就在网上研读了himi的一篇博文,看了之后发现,果然好奇还是对的,其中有一点需要注意,那就是设置或改变了键值对后,需要我们手动的调用一下该类的flush方法,用于将新的内容写入文件保存,否则我们之前新建及修改的内容将不会被保存至文件中(说来也怪,我之前使用的时候并未注意到这一点,但写入的内容却得到了保存,不知道是不是我的版本中(2.0.4),set的时候内部已经调用了flush或其他类似的操作,当然保险起见最好还是flush一下),另外himi还实现了一个类似于CCUserDefault类的私有函数isXMLFileExist,用于判定当前项目是否已经有存储数据的xml文件存在,对于新手朋友应该很有帮助,这里贴出himi的关于此话题的博文。
[转载自himi]:http://www.himigame.com/iphone-cocos2dx/653.html
本篇跟大家分享下Cocos2dX中的存储,其中也介绍些细节容易犯错的问题;
在Cocos2dX中提供了自带存储类:CCUserDefault ,当然了这里Himi强调一点,如果你的数据量比较大,建议使用SQL存储比较适合,另外一点要注意的是,尽可能不要在Cocos2dX中使用与平台相关的api进行开发,例如Xcode使用Cocos2dX进行开发游戏时不小心使用了iOS的控件/组件在项目中,那么当移植到Android等平台的时候就肯定异常费劲,估计连正常运行都不可能,因为其他平台不可能正好有iOS的这些控件,即使有也肯定底层实现不一样!换句话而言,神马功能都使用Cocos2dX api实现,尽量都向X靠拢吧,所以这里的存储我也使用X自带的CCUserDefault;至少使用Cocos2dX自带的对于跨平台这一块肯定支持的比较好啦;
言归正传,先大致介绍一下这个类的API:
Public Member Functions ~CCUserDefault () bool getBoolForKey (const char *pKey, bool defaultValue=false) Get bool value by key, if the key doesn't exist, a default value will return. int getIntegerForKey (const char *pKey, int defaultValue=0) Get integer value by key, if the key doesn't exist, a default value will return. float getFloatForKey (const char *pKey, float defaultValue=0.0f) Get float value by key, if the key doesn't exist, a default value will return. double getDoubleForKey (const char *pKey, double defaultValue=0.0) Get double value by key, if the key doesn't exist, a default value will return. std::string getStringForKey (const char *pKey, const std::string &defaultValue="") Get string value by key, if the key doesn't exist, a default value will return. void setBoolForKey (const char *pKey, bool value) Set bool value by key. void setIntegerForKey (const char *pKey, int value) Set integer value by key. void setFloatForKey (const char *pKey, float value) Set float value by key. void setDoubleForKey (const char *pKey, double value) Set double value by key. void setStringForKey (const char *pKey, const std::string &value) Set string value by key. void flush () Save content to xml file. Static Public Member Functions static CCUserDefault * sharedUserDefault () static void purgeSharedUserDefault () static const std::string & getXMLFilePath ()
从以上可以一目了然CCUserDefault的使用和功能,哈希表结构,Key -Value,key索引Value值;
提供的存储都是些基础类型,bool,int,string,double,float,方法很容易懂:存储使用set ,获取使用get !
那么最后static方法中可以看到CCUserDefault类留出了一个sharedUserDefault作为接口供开发者使用,那么大概介绍后,下面我们来写几段代码验证下:
//我们这里简单存储条数据 CCUserDefault::sharedUserDefault()->setStringForKey("key", "himi"); CCUserDefault::sharedUserDefault()->flush();//这里一定要提交写入哦,否则不会记录到xml中,下次启动游戏你就获取不到value了。 //这里随便定义一个string为了验证我们的存储 string str= "wahaha"; //取出我们刚存储的himi,然后赋值给str验证下; str= CCUserDefault::sharedUserDefault()->getStringForKey("key"); CCLog("打印str=:%s",str.c_str());
这里要注意,CCUserDefault中有个 flush()的函数,这个用来将数据写入xml文件中,也就是说当你使用setXX的一些函数后记得提交(调用一下flush函数)
OK,下面是控制台输入的结果:
Cocos2d: cocos2d: cocos2d-1.0.1-x-0.12.0 Cocos2d: cocos2d: GL_VENDOR: Imagination Technologies Cocos2d: cocos2d: GL_RENDERER: PowerVR SGX 543 Cocos2d: cocos2d: GL_VERSION: OpenGL ES-CM 1.1 IMGSGX543-63.14.2 Cocos2d: cocos2d: GL_MAX_TEXTURE_SIZE: 4096 Cocos2d: cocos2d: GL_MAX_MODELVIEW_STACK_DEPTH: 16 Cocos2d: cocos2d: GL supports PVRTC: YES Cocos2d: cocos2d: GL supports BGRA8888 textures: NO Cocos2d: cocos2d: GL supports NPOT textures: YES Cocos2d: cocos2d: GL supports discard_framebuffer: YES Cocos2d: cocos2d: compiled with NPOT support: NO Cocos2d: cocos2d: compiled with VBO support in TextureAtlas : NO Cocos2d: 打印str=:himi
最后一句验证了我们的存储没问题,那么我们现在验证是否真的存在xml中了,首先停止当前运行的项目,然后删除刚才代码替换如下代码:
CCLog("打印str=:%s",CCUserDefault::sharedUserDefault()->getStringForKey("key").c_str());
然后重新运行此项目,观察控制台打印如下:
Cocos2d: cocos2d: cocos2d-1.0.1-x-0.12.0 Cocos2d: cocos2d: GL_VENDOR: Imagination Technologies Cocos2d: cocos2d: GL_RENDERER: PowerVR SGX 543 Cocos2d: cocos2d: GL_VERSION: OpenGL ES-CM 1.1 IMGSGX543-63.14.2 Cocos2d: cocos2d: GL_MAX_TEXTURE_SIZE: 4096 Cocos2d: cocos2d: GL_MAX_MODELVIEW_STACK_DEPTH: 16 Cocos2d: cocos2d: GL supports PVRTC: YES Cocos2d: cocos2d: GL supports BGRA8888 textures: NO Cocos2d: cocos2d: GL supports NPOT textures: YES Cocos2d: cocos2d: GL supports discard_framebuffer: YES Cocos2d: cocos2d: compiled with NPOT support: NO Cocos2d: cocos2d: compiled with VBO support in TextureAtlas : NO Cocos2d: 打印str=:himi
通过刚才的key->”key”,正常获取到“himi”这个字符串了,OK,监测没问题;
那么一般情况下我们会需要一个方法就是判定当前项目是否已经有存储数据的xml文件存在了,那么Himi这里说下,Cocos2dX默认源码中有这个方法,但是并没有提供给开发者使用,因为此函数被private私有了,此函数源码如下图所示:
那么既然如此Himi这里就自定义了一个检测是否已存在数据xml的函数提供大家使用:(提醒:很多童鞋该说啦,为什么不直接修改源码将其public呢?!其实Himi也这么想,但是如果你后期使用了新的Cocos2dX的版本,或者同事机器的Cocos2dX并没有这么修改源码都会产生错误,反过来说,既然能很容易的写出一个判断的方法何必去动它呢,不是么?哈哈!)
.h文件: bool isHaveSaveFile(); .cpp文件: //当前项目是否存在存储的xml文件 bool HelloWorld::isHaveSaveFile(){ if(!CCUserDefault::sharedUserDefault()->getBoolForKey("isHaveSaveFileXml")) { CCUserDefault::sharedUserDefault()->setBoolForKey("isHaveSaveFileXml", true); CCUserDefault::sharedUserDefault()->flush();//提交 // CCLog("存储文件不存在,头次开始加载游戏"); return false; }else{ // CCLog("存储文件已存在"); return true; } }
备注:当存储数据的xml不存在的时候,你的第一次存储数据的时候默认会创建,路径在你的app下的documents,如下图所示:
那么这里Himi强调一点!大家要注意setXX的函数的参数,例如以下这个函数:
setStringForKey (const char *pKey, const std::string &value)
第一个参数是const char*类型,不是string!!!!(Himi因为这个原因浪费不少时间,悲剧阿。)
Himi当时存储写了如下代码,造成错误,如下:
CCUserDefault::sharedUserDefault()->setStringForKey(""+823, sKey);
错误截图如下:(存储的key变成了路径。。。。《数据是Himi加密后的》)
哎,郁闷,这里Himi犯错希望童鞋们不要再范此错误,之前Himi一直想找 itoa 找个函数,但是怎么都找不到!(c++ 应该存在的整形转字符串),但是Cocos2dX中没有,并且最后Himi使用了与Cocos2dX引擎中的实现itoa的源码,发现如下:
Cocos2dX自带的这个CCUserDefault并不是加密的,而是明文并且是.xml格式的,所以后续Himi准备写一篇使用base64来进行加密的文章供大家参考;
本篇源码下载: ”SaveDataForCocos2dx.zip”
下载地址: http://vdisk.weibo.com/s/hq2Ys