【Unity】PlayerPrefsの落とし穴とは?データ保存の注意点と代替手段まとめ

当ブログではアフィリエイト広告を含みます
Unity

Unityのセーブ方法を検索すると、必ず出てくる PlayerPrefs
「これってそのまま使っていいの?」「本番環境でも安全?」
そんな疑問を感じたことがある方は多いはずです。

この記事では、

  • PlayerPrefsのメリットとデメリット
  • 使用時に注意すべきポイント
  • より安全で柔軟なデータ保存方法(代替案)
  • Unity Asset Storeで人気のセーブ系アセット

まで、Unityでゲームデータを保存するための基本と選択肢を網羅的に解説します。

PlayerPrefsとは

PlayerPrefs は Unity に最初から用意されている、小規模なデータを保存するための仕組みです。保存できるのは以下の3種類の型に限られます。

  • int
  • float
  • string

基本的に、これらの型以外の型は保存できません。

データセーブの使用例

// 保存
PlayerPrefs.SetInt("HighScore", 1000);
PlayerPrefs.Save();

// 読み込み
int score = PlayerPrefs.GetInt("HighScore", 0); // デフォルトは0

SetInt() Save() でデータをローカルに保存、GetInt() でデータをローカルから読み込みます。
とても手軽に使えるため、チュートリアルや小規模プロジェクトでのスコア記録などには最適です。

PlayerPrefsの注意点

便利なPlayerPrefsですが、ゲーム本番のセーブデータ保存としては不安要素が多いのも事実です。

  • データは暗号化されていないため、すぐ改ざんできる
  • 大量のデータ保存には向かない(設計上の想定がシンプルなため)
  • OSや端末ごとに保存先が異なり、互換性が保証されない

とくにオンラインでの公平性が求められるタイトルでは、セーブデータの保護が甘いことが致命的になるケースもあります。いわゆる「チート」によってスコアが荒らされる、といったことが起こり得ます。

要するに、向いているケース、向いていないケースを正しく理解する必要があります。

✅ PlayerPrefsが向いている用途

  • チュートリアルの進行フラグ保存
  • 音量設定やバイブ設定などの簡易なオプション設定
  • スコアやプレイ回数などのローカル限定・軽量な保存

❌ 避けたほうがいい用途

  • ゲームの進行状況(ステージクリア・所持アイテムなど)
  • 公平性が求められるデータ(仮想通貨・ランキング用データなど)
  • 複数ファイル・セーブスロットが必要なケース

PlayerPrefs以外の保存方法(代替案)

本格的なセーブ機能を実装する場合は、以下のような代替手段が考えられます。

1. JSONで保存

データを構造化して保存したいなら、JSON形式が手軽かつ柔軟です。

[System.Serializable]
public class SaveData
{
    public int level;
    public int coin;
}

void SaveToJson()
{
    SaveData data = new SaveData { level = 2, coin = 500 };
    string json = JsonUtility.ToJson(data);
    File.WriteAllText(Application.persistentDataPath + "/save.json", json);
}

シンプルな例ですが、 SaveData というクラスを作成し、メンバ変数として level coin があるというものです。
特徴として、フォーマットが分かりやすく人間が読むことができます。複数の値を1つの構造体で管理可能ですし、外部バックアップやクラウド保存と組み合わせやすいです。

KuroMikanは主に下記の用途に使用しています。

  • アイテムストレージの保存
  • キャラやオブジェクトの状態をまるっと保存

上記クラスの保存、読み込みを行うサンプルコードはこちら。

JSONで保存・読み込みするコード

using System.IO;
using UnityEngine;

[System.Serializable]
public class SaveData
{
    public int level;
    public int coin;
}

public class SaveManager : MonoBehaviour
{
    private string SavePath => Application.persistentDataPath + "/save.json";

    // データを保存する
    public void Save()
    {
        SaveData data = new SaveData
        {
            level = 3,
            coin = 500
        };

        string json = JsonUtility.ToJson(data, true);
        File.WriteAllText(SavePath, json);

        Debug.Log("セーブ完了:" + SavePath);
    }

    // データを読み込む
    public SaveData Load()
    {
        if (!File.Exists(SavePath))
        {
            Debug.LogWarning("セーブファイルが見つかりません:" + SavePath);
            return null;
        }

        string json = File.ReadAllText(SavePath);
        SaveData loadedData = JsonUtility.FromJson<SaveData>(json);

        Debug.Log($"読み込み完了:レベル{loadedData.level} / コイン{loadedData.coin}");

        return loadedData;
    }
}
  • SaveData クラスはセーブしたい値(今回はレベルとコイン)を持つだけの単純な構造です。
  • 保存時は JsonUtility.ToJson()、読み込み時は JsonUtility.FromJson() を使用。
  • ファイルの保存先は Application.persistentDataPath 配下に自動で設定され、プラットフォームごとに適切な場所になります。
  • 読み込み時はファイルの存在チェックも入れて、初回起動時などにエラーが出ないよう配慮しています。

2. バイナリ保存(BinaryFormatter、または自前のバイナリ処理)

改ざん対策や容量削減が必要な場合はバイナリ形式も選択肢に入ります。
(現在は BinaryFormatter は非推奨なので、古いコードを使い回すのはやめましょう)

バイナリを使った保存、読み込みのコードはこちら。

バイナリで保存・読み込みするコード

using System.IO;
using UnityEngine;

[System.Serializable]
public class SaveData
{
    public int level;
    public int coin;
}

public class BinarySaveManager : MonoBehaviour
{
    private string SavePath => Application.persistentDataPath + "/save.dat";

    // 保存
    public void Save()
    {
        SaveData data = new SaveData
        {
            level = 5,
            coin = 1234
        };

        using (BinaryWriter writer = new BinaryWriter(File.Open(SavePath, FileMode.Create)))
        {
            writer.Write(data.level);
            writer.Write(data.coin);
        }

        Debug.Log("バイナリ保存完了:" + SavePath);
    }

    // 読み込み
    public SaveData Load()
    {
        if (!File.Exists(SavePath))
        {
            Debug.LogWarning("セーブファイルが見つかりません:" + SavePath);
            return null;
        }

        SaveData data = new SaveData();

        using (BinaryReader reader = new BinaryReader(File.Open(SavePath, FileMode.Open)))
        {
            data.level = reader.ReadInt32();
            data.coin = reader.ReadInt32();
        }

        Debug.Log($"バイナリ読み込み完了:レベル{data.level} / コイン{data.coin}");
        return data;
    }
}
  • BinaryWriter / BinaryReader を使って、直接 int 値をバイナリ形式で保存・読み込みしています。
  • クラスをシリアライズ全体で保存するよりも、構造が分かりやすくエラーになりづらい書き方です。
  • 保存先は Application.persistentDataPath に固定し、プラットフォームごとに適切な場所に保存されます。
  • JSONに比べて読みやすさは下がるものの、ファイルサイズは小さく・改ざんもされにくくなるというメリットがあります。

3. Asset Storeで人気のセーブ系アセットを使う

もし自前で実装するのが難しい場合は、Unity Asset Store の完成度の高いアセットを活用するのも手です。評価の高い有名どころをご紹介します。

✅ Easy Save – The Complete Save & Load Asset

  • 【URL】Easy Save – The Complete Save Game & Data Serializer System
  • JSONもバイナリも、暗号化やクラウド対応まで揃ったオールインワン
  • ノーコード対応のビジュアルセーブ機能もあり
  • ロングセラーかつ信頼度も高い。ユーザーも多く困ったときに調べやすい

✅ Save Game Pro

  • 【URL】Save Game Pro – Gold Update
  • JSON・バイナリ形式に加え、暗号化やWeb保存にも対応
  • PlayerPrefs と同じような感覚で使えるが、機能は格段に上

まとめ:PlayerPrefsは“簡易保存用”と割り切って使うのが正解

PlayerPrefsは軽量な設定保存に最適ですが、やはり本格的なセーブには不向きです。
今後のアップデートからチーター対策、マルチプラットフォーム展開までを考えると、最初から「ちゃんとした堅牢なセーブ基盤」を整えることが、開発コストを抑える近道だと思います。

KuroMikanのおすすめは「JSON保存+ScriptableObject管理」です。これは柔軟性・視認性ともに高いと思います。バイナリ保存についてはまだ実践投入をしたことがありませんが、既にチートされてしまった経験があったり、超巨大なデータを扱う場合には、バイナリ保存の方がいいのかもしれません。

というわけで、「とりあえず全部PlayerPrefsで済ませる」のではなく、「このデータは将来どう扱いたいか?」を考えて保存方法を選ぶことが、開発者としてのスキルアップにもつながります!

Unity/C# をもっと体系的に学ぶには?
【現役メンターに質問しながら短期集中】
👉️ Unityコースで学んでみる [PR]
タイトルとURLをコピーしました