40歩目 セーブを実装!① Unityで1日1ステップ!ノンフィールドRPG開発日記

1日1歩開発日記

ふりかえり

前回はアーティファクトにS〜Cランクのレアリティを追加し、ガチャで高ランクが出にくくなるように調整しました!

セーブを実装!

今回はいよいよ「セーブ機能」を実装します!

これまでのゲームでは、アプリを閉じると進行状況が初期化されてしまい、毎回最初からプレイし直しになっていました。

これではゲームとして成立しないので、G(ゴールド)とダイヤの所持データをセーブ・ロードできるようにしていきます。

Easy Save

今回、ゲームのセーブ機能を実装するにあたり、Easy SaveというUnity Assetを使用します。

Easy Save - The Complete Save Game & Data Serializer System | ユーティリティ ツール | Unity Asset Store
Use the Easy Save - The Complete Save Game & Data Serializer System from Moodkie on your next project. Find this utility...

Easy Saveは、Unity向けの高機能かつ柔軟なセーブ・ロードシステムで、コードからはもちろん、PlayMakerなどのビジュアルスクリプトでも簡単に扱うことができます。

Unityには標準でPlayerPrefsというセーブ機能がありますが、以下のような制限があります:

  • 保存できるデータ型が限られている(int、float、stringのみ)
  • 保存先が端末内の分かりやすい場所にあり、ユーザーから容易にアクセスできてしまう
  • データを一括で保存・管理することができない
  • 大容量データの保存には不向き

これに対してEasy Saveは、多様なデータ型の保存暗号化圧縮ファイル操作の柔軟性など、セーブ処理を包括的にサポートしており、複雑なセーブ・ロード処理も簡単に実装できます。

スクリプトの変更

Save Manager.cs


このスクリプトでは、Easy Save 3(ES3)を使って、ゲームデータの保存と読み込みを行います。

アプリを閉じるタイミングで自動的にセーブされるようにもしてあります

また、データの読み込みはゲーム開始時に行います

using UnityEngine;

public class SaveManager : MonoBehaviour
{
    [Header("参照")]
    public PlayerManager playerManager;

    [Header("セーブ設定")]
    public string saveFileName = "GameData.es3"; // セーブファイル名

    void Start()
    {
        // ゲーム開始時にデータをロード
        LoadGameData();
    }

    // ゲームデータをセーブ
    public void SaveGameData()
    {
        if (playerManager == null || playerManager.player == null) return;

        try
        {
            // G(ゴールド)とダイヤをセーブ
            ES3.Save("PlayerGold", playerManager.player.gold, saveFileName);
            ES3.Save("PlayerDiamond", playerManager.player.diamond, saveFileName);

            Debug.Log("ゲームデータをセーブしました");
        }
        catch (System.Exception e)
        {
            Debug.LogError($"セーブエラー: {e.Message}");
        }
    }

    // ゲームデータをロード
    public void LoadGameData()
    {
        if (playerManager == null || playerManager.player == null) return;

        try
        {
            // G(ゴールド)とダイヤをロード
            if (ES3.KeyExists("PlayerGold", saveFileName))
            {
                playerManager.player.gold = ES3.Load<int>("PlayerGold", saveFileName);
                Debug.Log($"セーブデータからゴールドをロード: {playerManager.player.gold}");
            }

            if (ES3.KeyExists("PlayerDiamond", saveFileName))
            {
                playerManager.player.diamond = ES3.Load<int>("PlayerDiamond", saveFileName);
                Debug.Log($"セーブデータからダイヤをロード: {playerManager.player.diamond}");
            }

            Debug.Log("ゲームデータをロードしました");
        }
        catch (System.Exception e)
        {
            Debug.LogError($"ロードエラー: {e.Message}");
        }
    }

    // セーブデータを削除
    public void DeleteSaveData()
    {
        try
        {
            ES3.DeleteFile(saveFileName);
            Debug.Log("セーブデータを削除しました");
        }
        catch (System.Exception e)
        {
            Debug.LogError($"セーブデータ削除エラー: {e.Message}");
        }
    }

    // セーブデータが存在するかチェック
    public bool HasSaveData()
    {
        return ES3.FileExists(saveFileName);
    }

    // アプリケーション終了時にセーブ
    void OnApplicationPause(bool pauseStatus)
    {
        if (pauseStatus)
        {
            SaveGameData();
        }
    }

    // アプリケーション終了時にセーブ
    void OnApplicationQuit()
    {
        SaveGameData();
        DebugSaveData();
    }

    // SaveManagerにデバッグ用メソッドを追加
    public void DebugSaveData()
    {
        Debug.Log($"現在のゴールド: {playerManager.player.gold}");
        Debug.Log($"現在のダイヤ: {playerManager.player.diamond}");
        Debug.Log($"セーブファイル存在: {HasSaveData()}");
    }

    // PlayerDataを初期化(セーブデータを考慮)
    public PlayerData InitializePlayerData()
    {
        PlayerData playerData = new PlayerData
        {
            name = "勇者",
            level = 1,
            baseMaxHP = 100, // 基礎HP
            baseAttack = 10, // 基礎攻撃力
            baseDefense = 0, // 基礎防御力
            baseCriticalRate = 0.1f, // 基礎会心率
            baseCriticalDamageMultiplier = 2.0f, // 基礎会心ダメージ倍率
            currentHP = 100,
            exp = 0,   // 初期経験値
            gold = 0,  // 初期お金
            expToNextLevel = 10, // 次のレベルに必要な経験値
            // G強化ステータスとアーティファクトステータスは初期値0のまま
            gPowerUpHP = 0,
            gPowerUpAttack = 0,
            gPowerUpDefense = 0,
            gPowerUpCriticalRate = 0f,
            gPowerUpCriticalDamage = 0f,
            artifactPowerUpHP = 0,
            artifactPowerUpAttack = 0,
            artifactPowerUpDefense = 0,
            artifactPowerUpCriticalRate = 0f,
            artifactPowerUpCriticalDamage = 0f
        };

        // セーブデータがある場合はロード
        try
        {
            if (ES3.KeyExists("PlayerGold", saveFileName))
            {
                playerData.gold = ES3.Load<int>("PlayerGold", saveFileName);
                Debug.Log($"セーブデータからゴールドをロード: {playerData.gold}");
            }

            if (ES3.KeyExists("PlayerDiamond", saveFileName))
            {
                playerData.diamond = ES3.Load<int>("PlayerDiamond", saveFileName);
                Debug.Log($"セーブデータからダイヤをロード: {playerData.diamond}");
            }

            Debug.Log("PlayerDataの初期化が完了しました");
        }
        catch (System.Exception e)
        {
            Debug.LogError($"PlayerData初期化エラー: {e.Message}");
        }

        return playerData;
    }
} 

PlayerManager.cs

    public void GainRewards(int exp, int gold)
    {
        player.exp += exp;
        player.gold += gold;
        
        // 5%の確率でダイヤを獲得
        bool gotDiamond = false;
        if (Random.value < 1f)
        {
            player.diamond += 100;
            gotDiamond = true;
        }
        
        // バトルログに追加
        if (battleLogManager != null)
        {
            battleLogManager.AddRewardLog(exp, gold);
            if (gotDiamond)
            {
                battleLogManager.AddLog("ダイヤを1個獲得した!", Color.cyan);
            }
        }
        
        Debug.Log($"{player.name}は{exp}EXPと{gold}Gを獲得しました!");
        if (gotDiamond)
        {
            Debug.Log($"{player.name}はダイヤを1個獲得しました!");
        }
        
        // レベルアップのチェック
        CheckLevelUp();
        
        // 報酬獲得後にセーブ
        if (saveManager != null)
        {
            saveManager.SaveGameData();
        }
    }

報酬を受け取ったとき、ダイヤを獲得したときなどにもセーブを行うようにします。

今後は、Gやダイヤが増減するすべてのタイミングで SaveGameData() を呼び出すようにしていきます。

例:

  • 強化でGを消費(PowerUpManager)
  • ガチャやアーティファクト強化でダイヤを消費(ArtifactManager)など

Unityでの設定

  1. Hierarchyに新しいGameObjectを作成(例:SaveManager)
  2. 作成したGameObjectにSaveManager.csをアタッチ
  3. PlayerManagerの参照をInspectorでセット

動作確認

  • モンスターを倒してGとダイヤを獲得
  • ガチャや強化でG・ダイヤを消費
  • アプリを終了 → 再起動
  • しっかりセーブデータが読み込まれ、所持数が引き継がれていました!

まとめ

今回はゲームにとって重要な「セーブ機能」の第一歩として、Gとダイヤの保存を実装しました!

いよいよゲームらしくなってきましたね!

次回予告

次回は、さらにプレイヤーのステータス、進行ステージ、強化状態、アーティファクト所持情報など、すべてのデータをセーブできるようにしていきます!

お楽しみに!

タイトルとURLをコピーしました