cocos2d-x 勉強第9回「変数の保存/永続化」

木内智史之介(シャッチョー)
ミンカさんけっこんしてくださいおねがいします(ズザー SEGAさん、DIVAの筐体ください(ズザー

今回の課題

ゲームに限らずですが、大抵のアプリケーションであれば「アプリを落としても保持し続けたい情報」というものがあります。
落としたら全ての情報をキレイさっぱり忘れてしまう。
楽しかった日々、喜びも悲しみも共有したあの日さえも…。そんなのってないよ!

そこで「変数の保存/永続化」です。
忘れたくない情報を端末(あるいは外部)に保存できれば、思い出せるはず、人間の心さえ残っていれば…ッ!
(このノリ疲れるな…)

下準備、下調べ

cocos2d-xで、変数を永続化する方法はいくつかあるようです。

  • プリファレンス
  • Dictionary
  • SQLite
  • ネットワークを通じて外部

下二つはそれ単体で記事が書けそうなので、今回は上二つを試してみる事にします。

プリファレンス

プリファレンスとは?

端末内部にキーバリュー形式で保存されるデータのことを「プリファレンスデータ」と言うらしいよ。

書き込み

cocos2d::UserDefault* user = cocos2d::UserDefault::sharedUserDefault();
user->setStringForKey("stringKey", "value");
user->setIntegerForKey("intKey", 10);
user->setFloatForKey("floatKey", 10.2f);
user->setBoolForKey("boolKey", true);
user->flush(); // 実行しないと保存されない

読み込み

cocos2d::UserDefault* user = cocos2d::UserDefault::sharedUserDefault();
// 第二引数はデフォルト値
std::string stringValue = user->getStringForKey("stringkey", "default");
int intValue = user->getIntegerForKey("intKey", 2);
float floatValue = user->getFloatForKey("floatKey", 0.1f);
bool boolValue = user->getBoolForKey("boolKey", false);

Dictionary

Dictionaryとは?

直訳では「辞書」でしょうか…?
たしかに、何かしらのデータを出し入れできるようなイメージがあります。
リファレンスを見るに、どうやらファイルベース(plist形式)で保存されるようです。
まあ、ぶっちゃけると、plist形式がどういう形式なのか分かっていないんですがね!

書き込み

cocos2d::FileUtils* fileUtils = cocos2d::FileUtils::sharedFileUtils();
cocos2d::Dictionary* dictionary = cocos2d::Dictionary::create();
// 値の設定
dictionary->setObject(cocos2d::String::create("value"), "stringKey");
dictionary->setObject(Array::create(cocos2d::String::create("array_value1"), cocos2d::String::create("array_value2"), NULL), "arrayKey");
cocos2d::Dictionary* dictionaryValues = cocos2d::Dictionary::create();
dictionaryValues->setObject(cocos2d::String::create("dictionary_value1"), "dictionary_key1");
dictionaryValues->setObject(cocos2d::String::create("dictionary_value2"), "dictionary_key2");
dictionary->setObject(dictionaryValues, "dictionaryKey");
// 保存
std::string filepath = fileUtils->getWritablePath() + "foo.plist";
dictionary->writeToFile(filepath.c_str());

制限

どうやらcocos2d::Stringcocos2d::Arraycocos2d::Dictionaryの3種類のデータのみしか扱えないらしいよ。 本当だろうか!

cocos2d::Dictionary* dictionary = cocos2d::Dictionary::create();
dictionary->setObject(cocos2d::Integer(10), "intKey"); // エラーでコンパイルが通らない

本当だった。

読み込み

cocos2d::FileUtils* fileUtils = cocos2d::FileUtils::sharedFileUtils();
std::string filepath = fileUtils->getWritablePath() + "foo.plist";
// plistファイルから情報を読み込み
cocos2d::Dictionary* dictionary = cocos2d::Dictionary::createWithContentsOfFile(filepath.c_str());
// 文字列の取得
String* stringValue = (String*)dictionary->objectForKey("stringKey");
log("%s", stringValue->getCString());
// 配列の取得
Array* arrayValues = (Array*)dictionary->objectForKey("arrayKey");
Object* arrayValue = NULL;
CCARRAY_FOREACH(arrayValues, arrayValue) {
String* value = (String*)arrayValue;
log("%s", value->getCString());
}
// Dictionaryの取得
Dictionary* dictionaryValues = (Dictionary*)dictionary->objectForKey("dictionaryKey");
String* dicValue1 = (String*)dictionaryValues->objectForKey("dictionary_key1");
String* dicValue2 = (String*)dictionaryValues->objectForKey("dictionary_key2");
log("%s", dicValue1->getCString());
log("%s", dicValue2->getCString());

今回のまとめ

さくっと使えるという意味では、UserDefaultの方は本当に使いやすそうでした。
ただ、構造的なデータを持つのは不向きそうなので、構造化されたデータを持ちたい場合はDictionary一択ということになるのではないでしょうか。
その際、素のままのDictionaryではちょーっと色々面倒だなーと感じる部分が多いですので、何かしらを一枚噛んで使うのがよいかもしれません。
イチイチcocos2d::String::createするのも面倒ですので…。(ccsという短縮系があるといえども!)

次回に向けて

次回は「ローカルストレージにファイルを保存」という事をやってみたいと思います。

ソースコード

https://github.com/8823-scholar/cocos2d-x-study/commit/78391bf9562c4776e0b06c26e9c1ae103892e456