ふりかえり
前回は、放置報酬機能を実装しました!
「寝て起きたら強くなっている」そんな成長の実感が味わえる要素で、より何度もプレイしたくなるゲームになってきました。
復活機能を実装!
今回は、ゲームオーバー後にプレイヤーが復活できる機能を追加しました。
これまでプレイヤーのHPが0になると、以下のような重いペナルティが即時に適用されていました:
- 所持金(G)が半分に減る
- ステージの進行度がリセットされる
これではあまりにも厳しく、モチベーションも下がりかねません。
そこで新たに「復活パネル」を表示する仕組みを導入。
プレイヤーに対して、以下の選択肢を提示するようにしました:
- 広告を見る → ペナルティなしで復活
- 広告を見ない → 通常通りペナルティを受ける
広告視聴を選べば、所持Gも進行度もそのままキープ。
リトライ性とゲーム体験の柔軟性が大きく向上しました。
スクリプトの変更
復活機能の実装では、以下の2つのスクリプトを作成・拡張しました。
ReviveManager.cs
このスクリプトは復活処理の中心を担うマネージャークラスです。以下の処理を実装しています。
- プレイヤー死亡時に状態を保存(所持Gとステージ進行)
- 復活パネルの表示
- 復活選択に応じて以下の処理を分岐:
- 広告を見て復活(ペナルティなし)
- 見ない or キャンセル(ペナルティあり)
プレイヤーのHPを回復したり、エネミーをリスポーンさせたり、UIやログを更新する細かな処理もここに集約しています。
using UnityEngine; public class ReviveManager : MonoBehaviour { [Header("参照")] public PlayerManager playerManager; public StageManager stageManager; public EnemyManager enemyManager; public RevivePanel revivePanel; public BattleLogManager battleLogManager; public AnimationManager animationManager; public AdmobUnitReward admobUnitReward; // 広告ユニットを追加 public SaveManager saveManager; // SaveManager の参照を追加 // 復活時の状態保存用 private int savedGold; private int savedStageProgress; private int currentStage; void Start() { // 初期化処理 Debug.Log("復活システムが初期化されました。"); } // プレイヤーが死亡した時の処理 public void OnPlayerDeath() { // 現在の状態を保存 SaveCurrentState(); // 復活パネルを表示 if (revivePanel != null) { revivePanel.ShowRevivePanel(); } else { Debug.LogWarning("復活パネルが設定されていません。通常の復活処理を実行します。"); OnReviveCancelled(); // パネルがない場合は通常の復活処理 } } // 復活が選択された時の処理 public void OnReviveAccepted() { Debug.Log("復活が選択されました。所持金とステージ進行度を保持します。"); // バトルログに復活記録を追加 if (battleLogManager != null) { battleLogManager.AddLog("復活を選択しました!所持金とステージ進行度が保持されます。", Color.green); } // プレイヤーを復活させる(ペナルティなし) RevivePlayerWithoutPenalty(); } // 復活がキャンセルされた時の処理 public void OnReviveCancelled() { Debug.Log("復活がキャンセルされました。通常のデスペナルティーを適用します。"); // バトルログにキャンセル記録を追加 if (battleLogManager != null) { battleLogManager.AddLog("復活をキャンセルしました。デスペナルティーが適用されます。", Color.red); } // 通常のデスペナルティーを適用して復活 RevivePlayerWithPenalty(); } // 広告視聴で復活 public void OnReviveWithAd() { if (admobUnitReward == null) { Debug.LogWarning("広告ユニットが設定されていません。通常の復活処理を実行します。"); OnReviveAccepted(); return; } if (!admobUnitReward.IsReady) { Debug.LogWarning("広告が準備できていません。通常の復活処理を実行します。"); OnReviveAccepted(); return; } // 広告を表示 admobUnitReward.ShowRewardAd((reward) => { if (reward != null) { // 広告視聴成功 - ペナルティーなしで復活 Debug.Log("広告視聴完了!ペナルティーなしで復活します。"); OnReviveAccepted(); // 復活成功後にセーブ saveManager?.DebugSave(); } else { // 広告視聴失敗 - 通常の復活処理 Debug.Log("広告視聴に失敗しました。通常の復活処理を実行します。"); OnReviveAccepted(); } }); } // 現在の状態を保存 private void SaveCurrentState() { if (playerManager != null && playerManager.player != null) { savedGold = playerManager.player.gold; } if (stageManager != null) { currentStage = stageManager.GetCurrentStage(); savedStageProgress = stageManager.GetMonstersDefeatedInStage(); } Debug.Log($"現在の状態を保存: 所持金{savedGold}G, ステージ{currentStage}, 敵撃破数{savedStageProgress}"); } // ペナルティなしでプレイヤーを復活 private void RevivePlayerWithoutPenalty() { if (playerManager == null) return; // プレイヤーのHPを全回復 playerManager.player.currentHP = playerManager.player.maxHP; // 復活アニメーションを再生 if (animationManager != null) { animationManager.PlayerReviveAnimation(); } // モンスターを再びリスポーンさせる RespawnEnemy(); // バトルログに復活記録を追加 if (battleLogManager != null) { battleLogManager.AddLog($"{playerManager.player.name}が復活しました!", Color.green); } Debug.Log($"{playerManager.player.name}が復活しました!HP:{playerManager.player.currentHP}/{playerManager.player.maxHP}"); Debug.Log($"所持金: {playerManager.player.gold}G (保持), ステージ進行度: {savedStageProgress} (保持)"); // UIを更新 if (playerManager.playerUIManager != null) { playerManager.playerUIManager.UpdateUI(); } } // ペナルティありでプレイヤーを復活 private void RevivePlayerWithPenalty() { if (playerManager == null) return; // 所持金を半分にする playerManager.player.gold = Mathf.FloorToInt(playerManager.player.gold * 0.5f); // ステージの敵撃破数をリセット if (stageManager != null) { stageManager.ResetEnemiesDefeated(); } // プレイヤーのHPを全回復 playerManager.player.currentHP = playerManager.player.maxHP; // 復活アニメーションを再生 if (animationManager != null) { animationManager.PlayerReviveAnimation(); } // 新しいモンスターを生成 RespawnEnemy(); // バトルログにデスペナルティーを記録 if (battleLogManager != null) { int lostGold = savedGold - playerManager.player.gold; battleLogManager.AddLog($"デスペナルティー!所持金が{lostGold}G減少しました。", Color.red); battleLogManager.AddLog("ステージの敵撃破数がリセットされました。", Color.red); battleLogManager.AddLog($"{playerManager.player.name}が復活しました!", Color.green); } Debug.Log($"デスペナルティー適用: 所持金 {savedGold}G → {playerManager.player.gold}G"); Debug.Log("ステージの敵撃破数がリセットされました。"); Debug.Log($"{playerManager.player.name}が復活しました!HP:{playerManager.player.currentHP}/{playerManager.player.maxHP}"); // UIを更新 if (playerManager.playerUIManager != null) { playerManager.playerUIManager.UpdateUI(); } } // モンスターを再びリスポーンさせる private void RespawnEnemy() { if (enemyManager != null) { // 現在のステージレベルを取得 int stageLevel = (stageManager != null) ? stageManager.GetCurrentStage() : 1; // 新しいモンスターを生成 enemyManager.SetupNewEnemyForStage(stageLevel); // 敵のUIも更新 if (enemyManager.enemyUIManager != null) { enemyManager.enemyUIManager.InitializeHPBar(); enemyManager.enemyUIManager.ForceUpdateHPBar(); } Debug.Log($"ステージ{stageLevel}に新しいモンスターを生成しました"); } } // 復活システムが利用可能かチェック public bool IsReviveSystemAvailable() { return revivePanel != null && playerManager != null; } }
RevivePanel.cs
こちらはUIまわりを管理するクラスです。
- 復活パネルの表示/非表示
- 広告復活/通常復活/キャンセルの各ボタン処理
- 5秒のカウントダウン後、自動でキャンセルされる仕組み
- テキストメッセージの表示更新
- 各ボタンにイベントを割り当て
UI側の責務とゲームロジックをしっかり分けた構成になっています。
using UnityEngine; using UnityEngine.UI; using TMPro; using System.Collections; public class RevivePanel : MonoBehaviour { [Header("UI要素")] public GameObject panelObject; // パネル全体のGameObject public TextMeshProUGUI countdownText; // カウントダウン表示 public TextMeshProUGUI messageText; // メッセージ表示 public Button reviveButton; // 復活ボタン public Button reviveWithAdButton; // 広告視聴で復活ボタン public Button cancelButton; // キャンセルボタン [Header("参照")] public ReviveManager reviveManager; public AudioManager audioManager; private Coroutine countdownCoroutine; private bool isPanelActive = false; void Start() { // 初期状態では非表示 if (panelObject != null) { panelObject.SetActive(false); } // ボタンイベントを設定 if (reviveButton != null) { reviveButton.onClick.AddListener(OnReviveButtonClicked); } if (reviveWithAdButton != null) { reviveWithAdButton.onClick.AddListener(OnReviveWithAdButtonClicked); } if (cancelButton != null) { cancelButton.onClick.AddListener(OnCancelButtonClicked); } } // 復活パネルを表示 public void ShowRevivePanel() { isPanelActive = true; // パネルを表示 if (panelObject != null) { panelObject.SetActive(true); } // ボタンを有効化 if (reviveButton != null) { reviveButton.interactable = true; } if (reviveWithAdButton != null) { reviveWithAdButton.interactable = true; } if (cancelButton != null) { cancelButton.interactable = true; } // メッセージを設定 if (messageText != null) { messageText.text = "復活しますか?\n復活ボタンを押すと所持金とステージ進行度が保持されます。"; } // カウントダウンを開始 StartCountdown(); Debug.Log("復活パネルを表示しました。"); } // カウントダウンを開始 private void StartCountdown() { if (countdownCoroutine != null) { StopCoroutine(countdownCoroutine); } countdownCoroutine = StartCoroutine(CountdownCoroutine()); } // カウントダウン処理 private IEnumerator CountdownCoroutine() { int timeLeft = 5; // 5秒のカウントダウン while (timeLeft > 0 && isPanelActive) { if (countdownText != null) { countdownText.text = $"自動復活まで {timeLeft}秒"; } yield return new WaitForSeconds(1f); timeLeft--; } // 時間切れの場合は自動でキャンセル if (isPanelActive) { OnCancelButtonClicked(); } } // 復活ボタンがクリックされた時 private void OnReviveButtonClicked() { if (reviveManager != null) { reviveManager.OnReviveAccepted(); } // 復活音を再生 if (audioManager != null) { audioManager.PlayPowerUpSE(); // 復活音として使用 } ClosePanel(); Debug.Log("復活を選択しました!"); } // 広告視聴で復活ボタンがクリックされた時 private void OnReviveWithAdButtonClicked() { if (reviveManager != null) { reviveManager.OnReviveWithAd(); } ClosePanel(); } // キャンセルボタンがクリックされた時 private void OnCancelButtonClicked() { if (reviveManager != null) { reviveManager.OnReviveCancelled(); } ClosePanel(); Debug.Log("復活をキャンセルしました。"); } // パネルを閉じる private void ClosePanel() { isPanelActive = false; if (countdownCoroutine != null) { StopCoroutine(countdownCoroutine); countdownCoroutine = null; } if (panelObject != null) { panelObject.SetActive(false); } Debug.Log("復活パネルを閉じました。"); } // 外部からパネルを閉じる public void ForceClosePanel() { ClosePanel(); } // パネルが表示中かチェック public bool IsPanelActive() { return isPanelActive; } }
Unityでの設定
空のGameObjectを生成し、ReviveManager.csをアタッチします。
Inspector上でそれぞれの参照を設定します。

UIのPanelを生成しRevivePanel.csをアタッチします。
それぞれのUI要素と参照を設定します。

動作確認
実際にゲーム内で敵に倒されると復活パネルが表示され、5秒のカウントダウンが開始されます。
その間に広告を視聴すれば、所持Gもステージ進捗も保持された状態で復活できます!

まとめ
今回は、「復活機能」を導入し、プレイヤーが選択できる余地を用意しました。
ただ倒されて終わりではなく、「復活して続けるか」「潔くやり直すか」を選ばせることで、より戦略性や没入感が高まると感じています。
今後のプレイにも「もしもの時の選択肢」があることで、挑戦しやすくなり、ゲームにリズムと深みが出てきたと思います!
進捗
今回は復活機能を追加をしました!
- (済)オート戦闘:ボタン操作なしでもバトルが進むようにする。
- (済)オートスキル発動:スキルも自動で発動する機能。
- (済)UIのブラッシュアップ:デザインとレイアウトを整えて見やすくする。
- (済)ステージ数の拡充(最低20):遊びごたえを高めるためにボリュームアップ。
- (済)復活機能(広告視聴):ゲームオーバー時、広告視聴で復活できる仕組み。
- (済)広告報酬でダイヤ獲得:無課金でもダイヤが手に入る仕組み。
- (済)G強化・アーティファクト・転生の拡張:各システムにさらなる深みを持たせる。
- (済)回復薬の追加:ステージを進めやすくする。
- (済)アニメーション・エフェクトの追加:ゲーム画面にもっと動きを持たせる。
- (済)レアモンスターの追加:わくわく感を追加する。
- (済)放置要素の追加:プレイしていない時間も強くなる。
- (済)メニューの内容追加:ストーリー、記録、ガイドなどプレイヤーが遊びやすくする。
次回予告
ついに予定していたすべての要素を実装し終えました!
次回からはゲーム全体のバランス調整と最終ビルド作業に入ります。
より多くのプレイヤーに楽しんでもらえるよう、細部まで仕上げていきます!
お楽しみに!