ふりかえり
前回は、放置報酬機能を実装しました!
「寝て起きたら強くなっている」そんな成長の実感が味わえる要素で、より何度もプレイしたくなるゲームになってきました。
復活機能を実装!
今回は、ゲームオーバー後にプレイヤーが復活できる機能を追加しました。
これまでプレイヤーの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強化・アーティファクト・転生の拡張:各システムにさらなる深みを持たせる。
- (済)回復薬の追加:ステージを進めやすくする。
- (済)アニメーション・エフェクトの追加:ゲーム画面にもっと動きを持たせる。
- (済)レアモンスターの追加:わくわく感を追加する。
- (済)放置要素の追加:プレイしていない時間も強くなる。
- (済)メニューの内容追加:ストーリー、記録、ガイドなどプレイヤーが遊びやすくする。
次回予告
ついに予定していたすべての要素を実装し終えました!
次回からはゲーム全体のバランス調整と最終ビルド作業に入ります。
より多くのプレイヤーに楽しんでもらえるよう、細部まで仕上げていきます!
お楽しみに!

