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

1日1歩開発日記

ふりかえり

前回はセーブ機能の第一歩として、ゴールド(G)とダイヤのデータを保存・読み込みできるようにしました。今回はその続きです!

セーブ機能を実装!②

今回はゲーム内のさまざまなデータも含めて、完全なセーブ機能を実装していきます!

対象は以下の通りです:

  • プレイヤーのステータス
  • 戦闘状況(HPやスキルのクールタイム)
  • ステージの進行度
  • 強化状態
  • 所持アーティファクト
  • 音量設定(SE/BGM)

スクリプトの変更

SaveManager.cs

アーティファクトのセーブとロード

アーティファクトの数とそれぞれの情報(名前、説明、効果、ランク、レベルなど)を保存。読み込み時にはそれらを復元し、効果も適用します。

スキルの状態保存

各スキルのレベル、解放状態、クールタイムなどをセーブ&ロードします。

ステージ進行度

現在のステージ番号と、そのステージで倒したモンスターの数を記録します。

プレイヤーと敵のHP情報

バトル中にゲームを中断しても、次回起動時にその状態から再開できるようにHPを保存。

敵データの保存

敵の名前、ステータス、画像サイズなどを保存・復元します。バトル中の敵の状態が完全に復元されます。

音量設定

SEとBGMの音量設定も保存しておくことで、次回起動時も前回の音量で再生されます。

// アーティファクトデータをセーブ
    private void SaveArtifactData()
    {
        try
        {
            // 所持アーティファクトの数を保存
            ES3.Save("OwnedArtifactsCount", artifactManager.ownedArtifacts.Count, saveFileName);
            
            // 各アーティファクトのデータを保存
            for (int i = 0; i < artifactManager.ownedArtifacts.Count; i++)
            {
                var artifact = artifactManager.ownedArtifacts[i];
                string prefix = $"Artifact_{i}_";
                
                ES3.Save(prefix + "Name", artifact.name, saveFileName);
                ES3.Save(prefix + "Description", artifact.description, saveFileName);
                ES3.Save(prefix + "Type", artifact.artifactType, saveFileName);
                ES3.Save(prefix + "BaseValue", artifact.baseValue, saveFileName);
                ES3.Save(prefix + "ValuePerLevel", artifact.valuePerLevel, saveFileName);
                ES3.Save(prefix + "Level", artifact.level, saveFileName);
                ES3.Save(prefix + "Rank", artifact.rank, saveFileName);
            }
            
            Debug.Log("アーティファクトデータをセーブしました");
        }
        catch (System.Exception e)
        {
            Debug.LogError($"アーティファクトセーブエラー: {e.Message}");
        }
    }

    // アーティファクトデータをロード
    private void LoadArtifactData()
    {
        try
        {
            // 所持アーティファクトの数をロード
            if (ES3.KeyExists("OwnedArtifactsCount", saveFileName))
            {
                int count = ES3.Load<int>("OwnedArtifactsCount", saveFileName);
                artifactManager.ownedArtifacts.Clear();
                
                // 各アーティファクトのデータをロード
                for (int i = 0; i < count; i++)
                {
                    string prefix = $"Artifact_{i}_";
                    
                    if (ES3.KeyExists(prefix + "Name", saveFileName))
                    {
                        ArtifactData artifact = new ArtifactData
                        {
                            name = ES3.Load<string>(prefix + "Name", saveFileName),
                            description = ES3.Load<string>(prefix + "Description", saveFileName),
                            artifactType = ES3.Load<ArtifactType>(prefix + "Type", saveFileName),
                            baseValue = ES3.Load<float>(prefix + "BaseValue", saveFileName),
                            valuePerLevel = ES3.Load<float>(prefix + "ValuePerLevel", saveFileName),
                            level = ES3.Load<int>(prefix + "Level", saveFileName),
                            rank = ES3.Load<ArtifactRank>(prefix + "Rank", saveFileName)
                        };
                        
                        artifactManager.ownedArtifacts.Add(artifact);
                    }
                }
                
                // アーティファクト効果を適用
                artifactManager.ApplyAllArtifactEffects();
                
                Debug.Log($"アーティファクトデータをロードしました: {count}個");
            }
        }
        catch (System.Exception e)
        {
            Debug.LogError($"アーティファクトロードエラー: {e.Message}");
        }
    }

    // スキルデータをセーブ
    private void SaveSkillData()
    {
        try
        {
            // 各スキルのレベルとクールタイムを保存
            for (int i = 0; i < skillManager.skills.Count; i++)
            {
                var skill = skillManager.skills[i];
                string prefix = $"Skill_{i}_";
                
                ES3.Save(prefix + "Level", skill.skillLevel, saveFileName);
                ES3.Save(prefix + "IsUnlocked", skill.isUnlocked, saveFileName);
                ES3.Save(prefix + "CurrentCooldown", skill.currentCooldown, saveFileName);
            }
            
            Debug.Log("スキルデータをセーブしました");
        }
        catch (System.Exception e)
        {
            Debug.LogError($"スキルセーブエラー: {e.Message}");
        }
    }

    // スキルデータをロード
    private void LoadSkillData()
    {
        try
        {
            // 各スキルのレベルとクールタイムをロード
            for (int i = 0; i < skillManager.skills.Count; i++)
            {
                string prefix = $"Skill_{i}_";
                
                if (ES3.KeyExists(prefix + "Level", saveFileName))
                {
                    skillManager.skills[i].skillLevel = ES3.Load<int>(prefix + "Level", saveFileName);
                }
                
                if (ES3.KeyExists(prefix + "IsUnlocked", saveFileName))
                {
                    skillManager.skills[i].isUnlocked = ES3.Load<bool>(prefix + "IsUnlocked", saveFileName);
                }
                
                if (ES3.KeyExists(prefix + "CurrentCooldown", saveFileName))
                {
                    skillManager.skills[i].currentCooldown = ES3.Load<int>(prefix + "CurrentCooldown", saveFileName);
                }
            }
            
            Debug.Log("スキルデータをロードしました");
        }
        catch (System.Exception e)
        {
            Debug.LogError($"スキルロードエラー: {e.Message}");
        }
    }

    // 音量設定をセーブ
    private void SaveAudioSettings()
    {
        try
        {
            ES3.Save("SEVolume", audioManager.seVolume, saveFileName);
            ES3.Save("BGMVolume", audioManager.bgmVolume, saveFileName);
            
            Debug.Log("音量設定をセーブしました");
        }
        catch (System.Exception e)
        {
            Debug.LogError($"音量設定セーブエラー: {e.Message}");
        }
    }

    // 音量設定をロード
    private void LoadAudioSettings()
    {
        try
        {
            if (ES3.KeyExists("SEVolume", saveFileName))
            {
                audioManager.seVolume = ES3.Load<float>("SEVolume", saveFileName);
                audioManager.SetSEVolume(audioManager.seVolume);
            }
            
            if (ES3.KeyExists("BGMVolume", saveFileName))
            {
                audioManager.bgmVolume = ES3.Load<float>("BGMVolume", saveFileName);
                audioManager.SetBGMVolume(audioManager.bgmVolume);
            }
            
            Debug.Log("音量設定をロードしました");
        }
        catch (System.Exception e)
        {
            Debug.LogError($"音量設定ロードエラー: {e.Message}");
        }
    }

    // ステージ進行度をセーブ
    private void SaveStageProgress()
    {
        try
        {
            ES3.Save("CurrentStage", stageManager.GetCurrentStage(), saveFileName);
            ES3.Save("MonstersDefeatedInStage", stageManager.GetMonstersDefeatedInStage(), saveFileName);
            
            Debug.Log("ステージ進行度をセーブしました");
        }
        catch (System.Exception e)
        {
            Debug.LogError($"ステージ進行度セーブエラー: {e.Message}");
        }
    }

    // ステージ進行度をロード
    private void LoadStageProgress()
    {
        try
        {
            int currentStage = 1;
            int monstersDefeated = 0;
            
            if (ES3.KeyExists("CurrentStage", saveFileName))
            {
                currentStage = ES3.Load<int>("CurrentStage", saveFileName);
                Debug.Log($"セーブデータからステージをロード: {currentStage}");
            }
            
            if (ES3.KeyExists("MonstersDefeatedInStage", saveFileName))
            {
                monstersDefeated = ES3.Load<int>("MonstersDefeatedInStage", saveFileName);
                Debug.Log($"セーブデータから敵撃破数をロード: {monstersDefeated}");
            }
            
            // StageManagerにステージ進行度を設定
            stageManager.SetStageProgress(currentStage, monstersDefeated);
            
            Debug.Log("ステージ進行度をロードしました");
        }
        catch (System.Exception e)
        {
            Debug.LogError($"ステージ進行度ロードエラー: {e.Message}");
        }
    }

    // プレイヤーの現在HPをセーブ
    private void SavePlayerCurrentHP()
    {
        try
        {
            ES3.Save("PlayerCurrentHP", playerManager.player.currentHP, saveFileName);
            Debug.Log($"プレイヤーの現在HPをセーブ: {playerManager.player.currentHP}");
        }
        catch (System.Exception e)
        {
            Debug.LogError($"プレイヤーの現在HPセーブエラー: {e.Message}");
        }
    }

    // プレイヤーの現在HPをロード
    private void LoadPlayerCurrentHP()
    {
        try
        {
            if (ES3.KeyExists("PlayerCurrentHP", saveFileName))
            {
                playerManager.player.currentHP = ES3.Load<int>("PlayerCurrentHP", saveFileName);
                Debug.Log($"セーブデータからプレイヤーの現在HPをロード: {playerManager.player.currentHP}");
            }
        }
        catch (System.Exception e)
        {
            Debug.LogError($"プレイヤーの現在HPロードエラー: {e.Message}");
        }
    }

    // 敵の情報をセーブ
    private void SaveEnemyData()
    {
        try
        {
            if (enemyManager.enemy != null)
            {
                ES3.Save("EnemyName", enemyManager.enemy.name, saveFileName);
                ES3.Save("EnemyLevel", enemyManager.enemy.level, saveFileName);
                ES3.Save("EnemyCurrentHP", enemyManager.enemy.currentHP, saveFileName);
                ES3.Save("EnemyMaxHP", enemyManager.enemy.maxHP, saveFileName);
                ES3.Save("EnemyAttack", enemyManager.enemy.attack, saveFileName);
                ES3.Save("EnemyDefense", enemyManager.enemy.defense, saveFileName);
                ES3.Save("EnemyExpReward", enemyManager.enemy.expReward, saveFileName);
                ES3.Save("EnemyGoldReward", enemyManager.enemy.goldReward, saveFileName);
                ES3.Save("EnemyCriticalRate", enemyManager.enemy.criticalRate, saveFileName);
                ES3.Save("EnemyCriticalDamageMultiplier", enemyManager.enemy.criticalDamageMultiplier, saveFileName);
                
                // 敵のサイズ情報をセーブ
                if (enemyManager.enemyUIManager != null)
                {
                    Vector2 enemyScale = enemyManager.enemyUIManager.GetEnemyImageScale();
                    ES3.Save("EnemyImageScaleX", enemyScale.x, saveFileName);
                    ES3.Save("EnemyImageScaleY", enemyScale.y, saveFileName);
                }
                
                Debug.Log($"敵の情報をセーブ: {enemyManager.enemy.name} Lv.{enemyManager.enemy.level} HP:{enemyManager.enemy.currentHP}/{enemyManager.enemy.maxHP}");
            }
        }
        catch (System.Exception e)
        {
            Debug.LogError($"敵の情報セーブエラー: {e.Message}");
        }
    }

    // 敵の情報をロード
    private void LoadEnemyData()
    {
        try
        {
            if (ES3.KeyExists("EnemyName", saveFileName))
            {
                // 敵のデータをロード
                string enemyName = ES3.Load<string>("EnemyName", saveFileName);
                int enemyLevel = ES3.Load<int>("EnemyLevel", saveFileName);
                int enemyCurrentHP = ES3.Load<int>("EnemyCurrentHP", saveFileName);
                int enemyMaxHP = ES3.Load<int>("EnemyMaxHP", saveFileName);
                int enemyAttack = ES3.Load<int>("EnemyAttack", saveFileName);
                int enemyDefense = ES3.Load<int>("EnemyDefense", saveFileName);
                int enemyExpReward = ES3.Load<int>("EnemyExpReward", saveFileName);
                int enemyGoldReward = ES3.Load<int>("EnemyGoldReward", saveFileName);
                float enemyCriticalRate = ES3.Load<float>("EnemyCriticalRate", saveFileName);
                float enemyCriticalDamageMultiplier = ES3.Load<float>("EnemyCriticalDamageMultiplier", saveFileName);
                
                // 敵のサイズ情報をロード
                Vector2 enemyScale = Vector2.one; // デフォルトサイズ
                if (ES3.KeyExists("EnemyImageScaleX", saveFileName) && ES3.KeyExists("EnemyImageScaleY", saveFileName))
                {
                    enemyScale.x = ES3.Load<float>("EnemyImageScaleX", saveFileName);
                    enemyScale.y = ES3.Load<float>("EnemyImageScaleY", saveFileName);
                }
                
                // EnemyManagerに敵のデータを復元
                enemyManager.RestoreEnemyFromSaveData(enemyName, enemyLevel, enemyCurrentHP, enemyMaxHP, 
                                                   enemyAttack, enemyDefense, enemyExpReward, enemyGoldReward,
                                                   enemyCriticalRate, enemyCriticalDamageMultiplier);
                
                // 敵のサイズ情報を設定
                if (enemyManager.enemyUIManager != null)
                {
                    enemyManager.enemyUIManager.SetEnemyImageScale(enemyScale);
                }
                
                Debug.Log($"敵の情報をロード: {enemyName} Lv.{enemyLevel} HP:{enemyCurrentHP}/{enemyMaxHP} サイズ:{enemyScale}");
            }
        }
        catch (System.Exception e)
        {
            Debug.LogError($"敵の情報ロードエラー: {e.Message}");
        }
    }

Unityでの設定

  • SaveManagerに、PlayerManagerやSkillManagerなどの各マネージャーをInspectorで紐付けます。
  • 保存や読込のタイミングでSaveManagerの各メソッドが実行されるようにします。

動作確認

ゲームをプレイして、

  • ステージを進める
  • 強化やアーティファクトを入手
  • BGM音量を変更

…などの操作を行ってからアプリを再起動してみたところ、しっかりすべて復元されていました!

まとめ

今回は、ゲーム全体を通して重要な情報をセーブできるようにする処理を実装しました!

これで、プレイヤーの進行がしっかり保存される、“ゲームらしい”完成度に一歩近づきました!

次回予告

次回は「転生機能」を実装します!

繰り返し遊べるゲームにしていきましょう。

お楽しみに!

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