1.3 读写Plist文件

Plist是Apple公司提供的一种格式,只在iOS系统下使用,但Cocos2d-x将其发扬光大了,Plist是一种XML格式,所以XML存在的问题,它也存在。对于Plist而言,Cocos2d-x已经将接口封装好了,唯一存在的平台差异性问题,就是在iOS下的Dictionary和Windows/Android下的有些不同,在你遍历一个iOS下的字典时,是无序的,字典结构本身也是无序的,而在Windows/Android下,CCDictionary是Cocos2d-x自己实现的,在遍历的时候,字典里面的每一项,跟你的Plist文件里每一项的顺序是一致的。所以,你的代码不要依赖于字典的顺序。因为最恐怖的事情不是代码通不过,而是代码有时候可以通过,有时候又通不过。

Cocos2d-x里的粒子系统、动画、图集等大多都用到了Plist,粒子编辑器、拼图工具、动画编辑器等都可以直接导出Plist格式的文件,其中粒子格式比较特殊的一点是将粒子图片也直接放到Plist文件里了,因为粒子图片一般都比较小,放到一起管理起来非常方便。

1.3.1 Plist格式简介

Plist里面有一些特有的结构,主要包含以下标签。

❑ <string>:UTF-8字符串。

❑ <real>、<integer>:十进制的数字字符串。

❑ <true/>、<false />:真和假。

❑ <date>:日期字符串(ISO8601格式,例如2013-11-3)。

❑ <data>:Base64编码的数据。

❑ <array>:任意长度的数组。

❑ <dict>:key-value格式的字典,key是<key>标签,value可以是任意格式。

可以用Notepad++或者plist Editor之类的软件来编辑Plist文件,下面是一个粒子系统的Plist文件的一部分内容,我们可以看到和普通的XML文件不同的是,Plist文件多了一个DTD字段用于描述这个文件,而且标签的名字并不是随意的,基本由上面列出的标签组成,根节点是一个名为plist的节点

        <?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/
        DTDs/PropertyList-1.0.dtd"/>

        <plist version="1.0">
            <dict>
              <key>a</key>
              <string>string</string>
              <key>b</key>
              <false/>
              <key>c</key>
              <integer>123</integer>
              <key>d</key>
              <real>0.2500000</real>
              <key>e</key>
              <dict>
                  <key>a</key>
                  <string>string</string>
                  <key>b</key>
                  <false/>
                  <key>c</key>
                  <integer>123</integer>
                  <key>d</key>
                  <real>0.2500000</real>
              </dict>
            </dict>
        </plist>

1.3.2 读写Plist文件

在Cocos2d-x中读取一个Plist文件是一件非常简单的事情,通过FileUtils单例的一些方法直接从Plist文件中加载并创建ValueVector和ValueMap等容器,但是要加载的Plist文件的plist节点下必须是dict或者array节点

        //传入Plist文件名,解析后返回一个ValueMap对象
        virtual ValueMap getValueMapFromFile(const std::string& filename);
        //传入Plist文件名,解析后返回一个ValueVector对象
        virtual ValueVector getValueVectorFromFile(const std::string& filename);

下面介绍一下Plist文件的读取,通过FileUtils的getValueMapFromFile()方法将Plist文件解析成ValueMap并返回,然后调用自定义的dumpValueMap()方法,将容器的内容打印出来。

        string plistFile = FileUtils::getInstance()->getWritablePath() + "myplist.
        plist";
        ValueMap dict = FileUtils::getInstance()->getValueMapFromFile(plistFile);
        dumpValueMap(dict);

dumpValueMap()方法会遍历传入的ValueMap对象,根据对象的类型进行打印,dumpValueMap的实现如下。

        void dumpValueMap(ValueMap& vm)
        {
        //根据对象类型打印对象值
            for (auto& item : vm)
            {
              switch (item.second.getType())
              {
              case Value::Type::BOOLEAN:
                  CCLOG("%s is %d", item.first.c_str(), item.second.asBool());
                  break;
              case Value::Type::INTEGER:
                  CCLOG("%s is %d", item.first.c_str(), item.second.asInt());
                  break;
              case Value::Type::STRING:
                  CCLOG("%s is %s", item.first.c_str(), item.second.asString().
                  c_str());
                  break;
              case Value::Type::FLOAT:
              case Value::Type::DOUBLE:
                  CCLOG("%s is %f", item.first.c_str(), item.second.asFloat());
                  break;
              case Value::Type::MAP:
                  CCLOG("========== %s is ValueMap ==========", item.first.c_str());
                  dumpValueMap(item.second.asValueMap());
                  CCLOG("====================================");
              }
            }
        }

除了读取Plist文件之外,FileUtils还提供了写入Plist文件的接口,通过FileUtils的writeValueMapToFile()方法可以将一个ValueMap序列化到指定的文件中。

        ValueMap dict;
        dict.insert(pair<string, Value>(string("a"), Value("string")));
        dict.insert(pair<string, Value>(string("b"), Value(false)));
        dict.insert(pair<string, Value>(string("c"), Value(123)));
        dict.insert(pair<string, Value>(string("d"), Value(0.25f)));
        dict.insert(pair<string, Value>(string("e"), Value(dict)));
        string plistFile = FileUtils::getInstance()->getWritablePath() + "myplist.
        plist";
        if (FileUtils::getInstance()->writeValueMapToFile(dict, plistFile))
        {
            CCLOG("write plist %s success", plistFile.c_str());
        }