• [置顶] Cocos2dx 深入解析系列:以XML文件方式保存用户数据


    [Cocos2d-x相关教程来源于红孩儿的游戏编程之路CSDN博客地址http://blog.csdn.net/honghaier

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

     

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

    另:本章所用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__
    

    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
    

                  这引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文件中。下课!



  • 相关阅读:
    小峰视频三:在eclipse中创建java类
    小峰视频二:java类的编译与解释
    小峰视频一:jdk环境变量的配置
    React/数据流
    React/组件
    React/生命周期
    React/虚拟DOM
    React/JSX简介
    12.朴素贝叶斯-垃圾邮件分类
    9、主成分分析
  • 原文地址:https://www.cnblogs.com/xinyuyuanm/p/3027118.html
Copyright © 2020-2023  润新知