ふりかえり
前回はセーブ機能の第一歩として、ゴールド(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音量を変更
…などの操作を行ってからアプリを再起動してみたところ、しっかりすべて復元されていました!

まとめ
今回は、ゲーム全体を通して重要な情報をセーブできるようにする処理を実装しました!
これで、プレイヤーの進行がしっかり保存される、“ゲームらしい”完成度に一歩近づきました!
次回予告
次回は「転生機能」を実装します!
繰り返し遊べるゲームにしていきましょう。
お楽しみに!