18歩目 ステージ制を実装! Unityで1日1ステップ!ノンフィールドRPG開発日記

1日1歩開発日記

ふりかえり

前回は攻撃やダメージ時の効果音(SE)を実装し、バトルに臨場感が生まれました!

ステージ制を実装

これまでのバトルは、ひたすらスライムを倒し続ける単調なものでした。

今回は「10体倒すと次のステージに進む」というステージ制を導入し、プレイヤーに明確な目標を持たせていきます!

  • 各ステージは10体のモンスターで構成
  • ステージが進むとモンスターのレベルが上がっていく

BattleManagerの変更

ステージの進行はすべてBattleManagerで管理します。

新たに追加された主な要素:

  • currentStage:現在のステージ番号
  • monstersDefeatedInStage:そのステージで倒した敵の数
  • StageClear():ステージクリア時の処理
  • NextStage():次のステージに進む処理

敵を倒すたびにOnMonsterDefeated()が呼ばれ、10体倒すとStageClear()が発動し、クリアUI・SEが再生されます。

using UnityEngine;

public class BattleManager : MonoBehaviour
{
    public PlayerManager playerManager;
    public EnemyManager enemyManager;
    public AudioManager audioManager; // AudioManagerの参照を追加
    public BattleUIManager battleUIManager; // BattleUIManagerの参照を追加
    public AnimationManager animationManager; // AnimationManagerの参照を追加

    private bool isPlayerTurn = true; // プレイヤーのターンかどうかを管理
    private int currentStage = 1; // 現在のステージ
    private int monstersDefeatedInStage = 0; // 現在のステージで倒したモンスター数
    private int monstersPerStage = 10; // 1ステージあたりのモンスター数

    void Start()
    {
        // 初期化処理
        StartPlayerTurn(); // プレイヤーのターン開始時にログを出力
        Debug.Log($"ステージ{currentStage}開始!{monstersPerStage}匹のモンスターを倒してください!");
    }

    public void PlayerAttack()
    {
        // アニメーション中は攻撃できない
        if (animationManager != null && animationManager.IsAnyAnimationPlaying())
        {
            Debug.Log("アニメーション中です。しばらくお待ちください。");
            return;
        }

        if (isPlayerTurn)
        {
            // 敵が倒されている場合は攻撃しない
            if (enemyManager != null && enemyManager.IsEnemyDead())
            {
                Debug.Log("敵は既に倒されています。新しい敵が出現するまで待ってください。");
                return;
            }

            playerManager.Attack(enemyManager);
            isPlayerTurn = false; // プレイヤーのターン終了
            Invoke("EnemyAttack", 1.0f); // 1秒後に敵の攻撃を呼び出す
        }
    }

    public void EnemyAttack()
    {
        if (!isPlayerTurn)
        {
            // アニメーション中は攻撃を遅延させる
            if (animationManager != null && animationManager.IsAnyAnimationPlaying())
            {
                Invoke("EnemyAttack", 0.1f); // 0.1秒後に再試行
                return;
            }

            // 敵が倒されている場合は攻撃しない
            if (enemyManager != null && enemyManager.IsEnemyDead())
            {
                Debug.Log("敵は倒されているため、攻撃をスキップします。");
                isPlayerTurn = true; // プレイヤーのターンに戻る
                StartPlayerTurn();
                return;
            }

            enemyManager.Attack(playerManager);
            isPlayerTurn = true; // 敵のターン終了、プレイヤーのターンに戻る
            StartPlayerTurn(); // プレイヤーのターン開始時にログを出力
        }
    }

    private void StartPlayerTurn()
    {
        Debug.Log(playerManager.player.name + "のターン:攻撃ボタンをタップして下さい");
    }

    // モンスターが倒された時の処理
    public void OnMonsterDefeated()
    {
        monstersDefeatedInStage++;
        Debug.Log($"モンスターを倒しました!({monstersDefeatedInStage}/{monstersPerStage})");

        // ステージクリアのチェック
        if (monstersDefeatedInStage >= monstersPerStage)
        {
            StageClear();
        }
    }

    // ステージクリア時の処理
    private void StageClear()
    {
        Debug.Log($"ステージ{currentStage}クリア!お疲れさまでした!");
        
        // ステージクリアUIを表示
        if (battleUIManager != null)
        {
            battleUIManager.ShowStageClearUI();
        }
        
        // ステージクリアSEを再生
        if (audioManager != null)
        {
            audioManager.PlayStageClearSE();
        }
    }

    // 次のステージに進む(UIから呼び出される)
    public void NextStage()
    {
        currentStage++;
        monstersDefeatedInStage = 0;
        
        Debug.Log($"ステージ{currentStage}開始!{monstersPerStage}匹のモンスターを倒してください!");
        
        // 敵のレベルアップ
        if (enemyManager != null)
        {
            enemyManager.LevelUpEnemy();
        }
    }

    // 現在のステージ情報を取得
    public int GetCurrentStage()
    {
        return currentStage;
    }

    // 現在のステージで倒したモンスター数を取得
    public int GetMonstersDefeatedInStage()
    {
        return monstersDefeatedInStage;
    }

    // 1ステージあたりのモンスター数を取得
    public int GetMonstersPerStage()
    {
        return monstersPerStage;
    }

    // ステージ進行度を取得(0.0f~1.0f)
    public float GetStageProgress()
    {
        return (float)monstersDefeatedInStage / monstersPerStage;
    }
}

BattleUIManagerの作成

ゲーム画面にステージ情報進行度バーを表示するために、BattleUIManager.csを新規作成します。

表示内容:

  • 現在のステージ番号
  • 倒したモンスター数(例:3/10)
  • 進行度バー(Slider+Gradient)
  • ステージクリアパネル+「次のステージへ」ボタン

進行度はDOTweenでアニメーションし、徐々にバーが伸びていく演出が加わります。

ボタンを押すとBattleManager.NextStage()が呼び出され、ステージが進行します。

using UnityEngine;
using UnityEngine.UI;
using TMPro;
using DG.Tweening;

public class BattleUIManager : MonoBehaviour
{
    [Header("Battle Manager Reference")]
    public BattleManager battleManager;

    [Header("Stage Information UI")]
    public TextMeshProUGUI stageText; // ステージ番号を表示
    public TextMeshProUGUI monsterCountText; // 倒したモンスター数を表示

    [Header("Progress Bar UI")]
    public Slider progressBar; // 進行度バー
    public Image progressBarFill; // 進行度バーのフィル部分
    public Gradient progressBarGradient; // 進行度バーのグラデーション

    [Header("Stage Clear UI")]
    public GameObject stageClearPanel; // ステージクリアパネル
    public TextMeshProUGUI stageClearText; // ステージクリアテキスト
    public Button nextStageButton; // 次のステージボタン

    [Header("Animation Settings")]
    public float progressBarAnimationDuration = 0.5f; // 進行度バーのアニメーション時間

    private int lastMonstersDefeated = 0; // 前回の倒したモンスター数
    private int lastStage = 1; // 前回のステージ

    void Start()
    {
        InitializeUI();
        SetupEventListeners();
    }

    void Update()
    {
        UpdateUI();
    }

    // UIの初期化
    private void InitializeUI()
    {
        if (battleManager == null)
        {
            Debug.LogError("BattleManager is not assigned to BattleUIManager!");
            return;
        }

        // 進行度バーの初期化
        if (progressBar != null)
        {
            progressBar.minValue = 0f;
            progressBar.maxValue = 1f;
            progressBar.value = 0f;
        }

        // ステージクリアパネルを非表示
        if (stageClearPanel != null)
        {
            stageClearPanel.SetActive(false);
        }

        // 初期UIを更新
        UpdateStageInfo();
        UpdateProgressBar();
    }

    // イベントリスナーの設定
    private void SetupEventListeners()
    {
        if (nextStageButton != null)
        {
            nextStageButton.onClick.AddListener(OnNextStageButtonClicked);
        }
    }

    // UIの更新
    private void UpdateUI()
    {
        if (battleManager == null) return;

        int currentMonstersDefeated = battleManager.GetMonstersDefeatedInStage();
        int currentStage = battleManager.GetCurrentStage();

        // モンスター数が変わった場合
        if (currentMonstersDefeated != lastMonstersDefeated)
        {
            UpdateMonsterCount();
            UpdateProgressBar();
            lastMonstersDefeated = currentMonstersDefeated;
        }

        // ステージが変わった場合
        if (currentStage != lastStage)
        {
            UpdateStageInfo();
            lastStage = currentStage;
        }
    }

    // ステージ情報の更新
    private void UpdateStageInfo()
    {
        if (battleManager == null) return;

        int currentStage = battleManager.GetCurrentStage();
        int monstersPerStage = battleManager.GetMonstersPerStage();

        // ステージテキストの更新
        if (stageText != null)
        {
            stageText.text = $"ステージ {currentStage}";
        }

        // モンスター数テキストの更新
        if (monsterCountText != null)
        {
            monsterCountText.text = $"{battleManager.GetMonstersDefeatedInStage()}/{monstersPerStage}";
        }
    }

    // モンスター数の更新
    private void UpdateMonsterCount()
    {
        if (battleManager == null) return;

        int currentMonstersDefeated = battleManager.GetMonstersDefeatedInStage();
        int monstersPerStage = battleManager.GetMonstersPerStage();

        if (monsterCountText != null)
        {
            monsterCountText.text = $"{currentMonstersDefeated}/{monstersPerStage}";
        }
    }

    // 進行度バーの更新
    private void UpdateProgressBar()
    {
        if (battleManager == null || progressBar == null) return;

        float targetProgress = battleManager.GetStageProgress();
        
        // 進行度バーをアニメーション付きで更新
        DOTween.To(() => progressBar.value, x => progressBar.value = x, targetProgress, progressBarAnimationDuration)
            .SetEase(Ease.OutQuad);

        // グラデーションの適用
        if (progressBarFill != null && progressBarGradient != null)
        {
            progressBarFill.color = progressBarGradient.Evaluate(targetProgress);
        }
    }

    // ステージクリアUIの表示
    public void ShowStageClearUI()
    {
        if (stageClearPanel != null)
        {
            stageClearPanel.SetActive(true);
        }

        if (stageClearText != null)
        {
            stageClearText.text = $"ステージ{battleManager.GetCurrentStage()}クリア!";
        }
    }

    // ステージクリアUIの非表示
    public void HideStageClearUI()
    {
        if (stageClearPanel != null)
        {
            stageClearPanel.SetActive(false);
        }
    }

    // 次のステージボタンがクリックされた時の処理
    private void OnNextStageButtonClicked()
    {
        HideStageClearUI();
        
        // BattleManagerで次のステージに進む
        if (battleManager != null)
        {
            battleManager.NextStage();
        }
    }

    // 進行度バーの色を設定
    public void SetProgressBarColor(Color color)
    {
        if (progressBarFill != null)
        {
            progressBarFill.color = color;
        }
    }

    // 進行度バーのグラデーションを設定
    public void SetProgressBarGradient(Gradient gradient)
    {
        progressBarGradient = gradient;
        UpdateProgressBar();
    }
}

EnemyManagerの対応

BattleManager.NextStage()から呼び出される形で、EnemyManager.LevelUpEnemy()を実装し、

敵のレベルやステータスがステージごとに強化される仕組みも整えます。

Unityでの設定

  1. 新しい空のオブジェクトを作成し、BattleUIManager.csをアタッチ
  2. 必要なUI(Text、Slider、Panel、Buttonなど)を用意
  3. Inspectorで各UIコンポーネントを正しくアサイン
  4. BattleManagerにもBattleUIManagerを接続

動作確認

ゲームを開始して敵を倒すと:

  • ステージ番号が「ステージ1」
  • モンスター討伐数が「1/10」
  • プログレスバーが10%ほど進行

という風に、視覚的に進行状況がわかるようになりました!

まとめ

今回は、ステージ制の導入UIによる進行表示を実装しました!

バトルに「目標」ができたことで、ゲームのモチベーションもグッと高まりますね!

次回予告

次回は、キャラクターのHPバーを実装していきます!

より直感的で迫力あるバトル画面を目指しましょう!

お楽しみに!

コメント

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