FreelyApps

個人によるアプリ開発の日記です。アプリの収入だけで生活できるようになるのが目標です。UnityでAndroid向けのゲームアプリを作成しています。


    アプリ『トランプ・ジン・ラミー』を公開しました。
    Android/iOS https://goo.gl/PYKFYG

    タグ:セーブ

      このエントリーをはてなブックマークに追加 Clip to Evernote
    できればやらないことにこしたことはないのですが、セーブをバージョンアップしなければならないことがあります。
    古いバージョンのセーブに含まれているパラメータを新しいバージョンのセーブでは以前と別の意味で使いたいといった特別なときのみ対応が必要になります。このような時でも容量を無駄にしても良いなら、新しくパラメータを増やして保存した方が安全です。
    デバッグ用途でなければ、バージョンは増える方向にしかいきません。バージョン1、2があったら、1から2へはいけますが逆はいけません。

    対応方法は色々あると思います。今、私が使っている方法は次のような方法です。
    セーブにはバージョン情報(単なる数字int)を保存できるようにします。またプログラム中にセーブのバージョンを定義します。プログラム中に書いたバージョンが最新バージョンになります。プログラムにあるロード処理は当然最新バージョンのデータに対応したものになっています。
    バージョンが変わるごとに1つバージョンを上げる処理を作り、それを順に通すことにより古いバージョンのセーブから最新のバージョンのセーブを作りだします。

    バージョン3が最新のものとし、セーブされているのがバージョン1とします。
    バージョン2を作ったときに、バージョン1のセーブからバージョン2のセーブを作る処理Convert1To2を作ります。Convert1To2はバージョン1をロードして、2のデータを作り、2として保存します。PlayerPrefsで作ると、バージョン1のキーで必要な情報を変数に保持しておき、バージョン2のキーで作成したデータを保存することになります。
    同じ要領でバージョン2のセーブからバージョン3のセーブを作る処理Convert2To3を作ります。これらの変換関数は現在のセーブバージョンを見て変換を行い、処理の最後でバージョンを上げます。
    1. 保存されているバージョンを調べる 
    2. バージョンが最新でなかったら、バージョンを1つずつ上げていくための処理を行う
    3. 最新バージョンのデータが保存されている状態になったら、ロードを行う
    という手順でバージョンがなんでも対応できます。この方法は古いバージョンのものがあったら、それを逐次新しいバージョンのデータに変換していくというものです。ひとつずつバージョンが上がるためにいくつものバージョンが合っても正しく変換でき、バージョンが上がるときの対応も1つ前のものから最新のバージョンへの変換処理だけ追加すれば良いという利点があります。

    ややこしく感じるのはセーブがファイル上にあるのかメモリ上にあるのかが、文章からだと区別しにくいからです。 説明していて私もちょっと混乱してきました。

      このエントリーをはてなブックマークに追加 Clip to Evernote
    PlayerPrefsを使うとセーブが作れます。当然プラットフォームによらないので、一度コードを書けばAndroid/iOSはもちろんPC上でもセーブがされています。私はWindowsで開発をしているので今日はその問題について書こうと思います。

    セーブがどこに保存されているかというとWindowsではレジストリになります。レジストリはプログラムが設定情報などを書き込むデータの保存場所です。PlayerPrefsを使うと、このレジストリにセーブが保存されるというのはあまり良くありません。データの保存場所なのだからそこに保存することは自然に見えるのですが、大きな問題はセーブ内容を直接見れることと、編集できてしまうことです。特に編集できてしまうのが悪影響を与えます。範囲外のデータが保存されるとプログラムがおかしくなるというのはよくあることです。
    開発中はセーブを自由にいじりやすいというのはメリットなのですが、配布するゲームのセーブがいじれてしまうのはまずいです。Unityを使ってPCゲームを一般に配布するなら、セーブ方法は自前でファイルに保存するように実装しないと駄目でしょう。

    Androidとかだともう少しセーブがいじりづらい仕組みのようなので、気にしないで使うことにしています。とはいえ気になる人は自分でファイルを作ってデータを保存するのが良いのは間違いないです。セーブの改ざんをどこまで防ぎたいかにもよって手間の掛け方が違うということです。

    Windowsでセーブ内容を見るにはregedit.exeを使います。これはレジストリエディタというものでWindowsに付属しています。名前の通り簡単にレジストリがいじれます。 プログラムを検索すれば見つかるので、regeditで検索して開いてみてください。
    PlayerPrefsの解説ページにあるようにHKCU\Software\[company name]\[product name]キーのところに保存されています。このキーはレジストリエディタの左側のフォルダみたいなものです。HKCUというのが略でHKEY_CURRENT_USERのキーを表しています。
    下の図だとcompany nameがFreelyAppsでproduct nameがFreelyAppsキーの下にいくつか並んでいます。当然これはPlayer Settingsの値によって決まっています。プロジェクトごとにproduct nameが違えばちゃんと区別されているでしょう。
    playerprefs-reg
    名前と書いてあるものがPlayerPrefsのkeyに対応し、データが保存されている値になります。このデータを編集するとセーブを編集することが可能です。また項目を削除すればセーブからデータの一部がなくなったことになります。

    プロジェクトに対応するキーを削除するとセーブがなくなるので、いちからゲームを始めた状態を作りたいときはそうすると便利かもしれません。
    ただキーが削除できない場合があります。先ほどの画像のトランプ・ナポレオンというキーの下の謎の文字列はそういったキーです。選択すると下のようなエラーが発生します。もちろんキーを消すこともできません。
    regedit_nullerror

    この原因はキーにnull文字が含まれている場合だそうで、キーが文字化けしているのでわかりやすいエラーです。null文字ってC言語のやつと同じなのかとかなんで発生するのかはよくわかりませんでしたが、RegDelNullというマイクロソフトによるこの問題の対応ソフトがあるので使うと削除できます。regeditにこの機能を入れておいてよとか思いつつ、ダウンロードして実行してみました。

    exec-regdelnull
    こんな風に-sをつけておくとHKCU/Software/FreelyAppsキーの下を再帰的に検索してnull文字を検知してくれます。見つけると消すか聞いてくるのでYes答えましょう。これでレジストリエディタで消せなかった文字化けしているキーを消すことができます。

    たぶん日本語名のアプリだから起こる問題なのだと思います。英語のタイトルのアプリでは起きてないと思うので、その可能性が高いです。

    レジストリをいじるときは注意しましょう。
    最悪パソコンが起動しなくなる恐れがあります。 

      このエントリーをはてなブックマークに追加 Clip to Evernote
    ゲームは一般的にセーブが存在します。セーブとは何らかの方法でメモリにある情報を保存する仕組みです。
    プログラムはメモリの情報を見て動作するので、通常は前回の実行には全く関係なく実行されます。プログラムの実行を終えたら、メモリの情報は破棄されます。メモリから情報が消されるので、再開とかは不可能なわけです。電源を落としたら最初から遊ぶようなゲームはごく普通のことなのです。

    そうはいっても今のゲームは続きから遊べるようになっています。以前遊んだステージをクリアしていたり、取得したアイテムを持っていたりするのが普通です。これは前回までのことが記憶されているということです。
    メモリに保存ができないなら、どこか別のところに保存すればいいということでセーブというものがあるのです。セーブにメモリのデータをしまっておいて、次のときにセーブからデータを読み込んでメモリ上に復元します。そうすると以前の続きから遊べるようになります。

    セーブの仕組みは端末によります。PCで言えばテキストファイルかバイナリファイルを実行ファイルのあるフォルダに作るとかになります。
    このデータをセーブ(データ)とも呼んだりするので、セーブが機能を表しているのか実体としての保存されているデータ形式を表しているのかを気をつけなくてはいけません。
    セーブデータを作る機能は扱う端末によりわかれます。ファイルの読み書きなどはOSに依存する低レベルの処理なので、かなり面倒なものです。このあたりは取り扱いが非常に面倒でありゲーム内容に依存しにくいことなので、ゲームエンジンには端末ごとの処理を書かずに何らかのAPIを呼び出すだけでセーブができるようになっています。 

    UnityではPlayerPrefsという関数でセーブが作れます。 
    PlayerPrefs.GetIntとPlayerPrefs.SetIntがセットであり、GetIntでセーブからの取得を行いSetIntでセーブへの書き込みを行います。(SetIntは厳密には書き込みではなく、書き込みのときの値を設定しているものです。しかし書き込みと考えていても基本的には問題ありません。)
    PlayerPrefsはキーバリュー型の保存方法であり、簡単に使うことができます。キーは文字列でGetInt、SetIntで同じ文字列を使うことでキーに対応する値(バリュー)を読み書きできます。
    別の値を表すのにも関わらずキーが同じものを用いてしまうと、値が上書きされます。エラーが発生することはないようです。SetFloatやSetStringで同じキーを用いても問題なく動作し、最後に呼ばれたものだけがセーブに残るみたいです。

    PlayerPrefs.SetInt("test", 100);
    PlayerPrefs.SetFloat("test", 0.5f);
    PlayerPrefs.SetString("test", "hello");
    というように同じキーで3回セットを呼んでいますが、3つ目のみがセーブされます。この後にPlayerPrefs.GetInt("test")を呼んでも、デフォルト値の0が返り100が返ることはありません。保存する型が異なってもそれらはキーによってしか区別されないということに注意が要ります。
    PlayerPrefsを使う時に最も重要なのは、意図せずキーを重複させないということにつきます。実はセーブにデータ(キーとバリューのペア)が存在してなかったみたいなことが起きても、PlayerPrefsのGet系の関数はデフォルト値を返すためにバグに気付きづらいのです。デフォルト値をいじれるので、明らかな無効値を返すようにしてしまうとかがバグを防ぐ唯一の方法な気がします。その方法も無効値が存在しないデータだと検出できないです。名前の通りキーの取り扱いにはご注意をということなら、良いネーミングセンスです。

    PlayerPrefsを直接呼び出すことで非常に簡単にセーブ機能が作れますが、セーブが大きくなってくると管理クラスなどを作った方が良いでしょう。

    オートセーブを使うゲームの増加によりユーザーにはその存在がわかりにくくなっていますが、 開発者にはセーブの存在は大きいままです。セーブに関する話題は結構あって、正解ってなんだろうかと考えさせられます。

    このページのトップヘ