【Unity】Androidで外部ストレージ&内部ストレージにデータを保存するときの話

データ保存.png

 ローカル保存する方法として、🔍PlayerPrefs でゲームデータのセーブ・ロードを実装するというエントリーで PlayerPrefs を使ってデータを保存する方法をご紹介しましたが、今回は PlayerPrefsを使わない方法をandroidで実行したときのお話です。

 PlayerPrefsを使わない保存方法の一つに、自分でファイルを作成して以下の関数を利用して取得したパスに StreamWriter で保存する方法があります。
  • UnityEngine.Application.persistentDataPath
  • UnityEngine.Application.temporaryCachePath
 これらを利用して取得できるパスは「外部ストレージ」になります。外部があるってことは内部もあるということで、そちらも合わせて見ていきましょう。

外部ストレージに保存する

    Debug.Log(Application.persistentDataPath);
    Debug.Log(Application.temporaryCachePath);
persistentDataPath では、/storage/emulated/0/Android/data/{Package Name}/files/ のような出力が得られます。このパスにデータを保存することで永続的なデータを保存できます。一方、temporaryCachePath には一時的なデータを保存できます。

実際に書き込むスクリプトはこちらです。
using System.IO;
この名前空間の宣言が必要です。
    void Start()
{
SaveText(
Application.persistentDataPath,
"SampleFile",
"保存したいテキストデータ"
);
}

public void SaveText(string filePath, string fileName, string textToSave)
{
var combinedPath = Path.Combine(filePath, fileName);
using (var streamWriter = new StreamWriter(combinedPath))
{
streamWriter.WriteLine(textToSave);
}
}
このスクリプトをandroid端末で実行すると SampleFile というファイルが出力され、「保存したいテキストデータ」という文字列が記録されます。persistentDataPath を temporaryCachePath に変更することで保存先を変えられます。

外部ストレージの特徴としてはこのようになっています。

外部ストレージ:

  • 常に利用できるとは限りません。ユーザーは USB ストレージなどを外部ストレージとしてマウントできますが、デバイスから取り外すこともあります。
  • ここに保存されたファイルは誰でも開くことができるため、作成元アプリの管理が及ばないところで読み取られる可能性があります。
  • ユーザーがアプリをアンインストールした場合、当該アプリのファイルが削除されるのは、getExternalFilesDir()からディレクトリ内に保存した場合に限られます。
デバイス ストレージにファイルを保存するより引用

つまり、外部ストレージに保存したファイルは誰でも閲覧、改竄することができます。他のアプリから参照することもできます。ここにユーザーIDやレベルなどの重要なデータを保存してしまうとチートや不正アクセスの原因になるというわけです。外部ストレージに保存するデータは端末設定などの情報に限るべきでしょう。

内部ストレージに保存する

内部ストレージ:

  • 常に利用できます。
  • ここに保存したファイルにアクセスできるのは、保存元のアプリだけに限られます。
  • ユーザーがアプリをアンインストールした場合、当該アプリのファイルはすべて自動的に内部ストレージから削除されます。

内部ストレージに保存されたデータは、ファイルを作成したアプリにしかアクセスが許されていません。重要データはこちらに保存するのが正解です。しかし、Unityのスクリプトリファレンスには内部ストレージのパスを取得する方法がありません。なんでやねん。

というわけで、ネイティブプラグインを利用して直接パスを取得します。
#if !UNITY_EDITOR && UNITY_ANDROID
using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
using (var currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
using (var getFilesDir = currentActivity.Call<AndroidJavaObject>("getFilesDir"))
{
string secureDataPath = getFilesDir.Call<string>("getCanonicalPath");
Debug.Log(secureDataPath);
}
#endif
すると、/data/data/{PackageName}/files という出力を得ました。
これが内部ストレージのパスです。手こずらせやがって。

書き込むスクリプトは先程の Savetext() を利用します。
    void Start()
{
SaveText(
GetInternalStoragePath(),
"SampleFile",
"保存したいテキストデータ"
);
}
先程の手法で内部ストレージのパスを取得します。
    private string GetInternalStoragePath()
{
#if !UNITY_EDITOR && UNITY_ANDROID
using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
using (var currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
using (var getFilesDir = currentActivity.Call<AndroidJavaObject>("getFilesDir"))
{
string secureDataPath = getFilesDir.Call<string>("getCanonicalPath");
return secureDataPath;
}
#else
return Application.persistentDataPath;
#endif
}
これで内部ストレージに SampleFile が出力されます。
今回はandroid用の処理だけを記載しましたが、iOSの開発環境がある方はiOS用の処理をelse ifで追記して下さい。


PlayerPrefsを使わずにセーブ・ロードを実現する方法の一つとして、ストレージにファイルを出力する方法の紹介でした。


ゲームのアンテナ