装備機能の実装③ステータス保存機能編! Unityで1日1ステップ!ノンフィールドRPG開発日記

1日1歩開発日記

ふりかえり

前回は、装備を集めるための新しい挑戦場所「遺跡」を実装しました!

遺跡では特別なモンスターが出現し、防具がドロップする仕組みを導入しました。

これによりゲームの進行に新たな目標が生まれ、プレイに深みが増したと思います。

ステータス保存機能を実装!

今回は、遺跡挑戦をより快適にするためのキーフィーチャー「ステータス保存機能」を実装します。

このゲームには「転生」システムがあり、転生を行うと一度レベルや強化値がリセットされ、一時的に弱体化します。

その状態で遺跡に挑むと、敵に歯が立たない場合があります。

そこで登場するのが、この「ステータス保存」システムです。

ステータス保存機能とは?

要点は以下の通りです。

① 自分の最強だと思うタイミングでステータスを保存できる

・レベル

・基礎ステータス(HP / 攻撃 / 防御 / 会心系)

・ゴールド強化、アーティファクト強化、マナ強化

・装備している防具

・装備しているオーブ

これらを「スナップショット」として記録します。

② 保存したステータスで遺跡に挑戦できる

転生直後でも、記録しておいた全盛期のステータスで遺跡に出撃可能になります。

ステータス保存がもたらすメリット

● 遺跡挑戦のストレスが激減!

「鍵が回復してるのに転生直後だから弱くて行けない…」

という悩みを完全解消。

● SNSシェアで広がりやすくなる!

ステータス保存画面には

保存時のステータス+装備一覧(オーブ・防具) が表示されます。

スクショ映えするため、Xで自慢しやすく、

口コミでの拡散効果も狙えるようになります。

スクリプト

主に2つのスクリプトを使って実装しました。

  1. PlayerManager
    • RegisterPlayerStats() → 「今のプレイヤーのステータス+強化値+装備」を登録用スナップショットとして保存
    • GetRegisteredStats() → 登録済みステータスから最終的なHP/攻撃/防御/会心を計算して返す
    • SavePreDungeonStats() / RestorePreDungeonStats() → 遺跡に入る前の状態を保存/遺跡から帰ってきたら元に戻す
    • ApplyRegisteredStats() → 登録ステータスを実際の player に適用(遺跡挑戦用)
  2. DungeonUIManager
    • 「ステータス登録」ボタンで PlayerManager.RegisterPlayerStats() を呼ぶ
    • 遺跡ボタンを押したときに GetRegisteredStats() で 「登録済みか?」をチェック → 未登録なら警告パネルを出して遺跡に入れない

PlayerManager:ステータス登録のコアコード

登録用のデータとメインメソッド

public class PlayerManager : MonoBehaviour
{
    public PlayerData player;
    public PlayerUIManager playerUIManager;
    public BattleLogManager battleLogManager;
    public SaveManager saveManager;

    // --- 遺跡用に登録するステータス(スナップショット) ---
    private int registeredLevel;

    // 基礎ステータス
    private int   registeredBaseMaxHP;
    private int   registeredBaseAttack;
    private int   registeredBaseDefense;
    private float registeredBaseCriticalRate;
    private float registeredBaseCriticalDamageMultiplier;

    // G強化
    private int   registeredGPowerUpHP;
    private int   registeredGPowerUpAttack;
    private int   registeredGPowerUpDefense;
    private float registeredGPowerUpCriticalRate;
    private float registeredGPowerUpCriticalDamage;
    private float registeredGPowerUpHPPercentage;
    private float registeredGPowerUpAttackPercentage;
    private float registeredGPowerUpDefensePercentage;

    // AF強化
    private int   registeredArtifactPowerUpHP;
    private int   registeredArtifactPowerUpAttack;
    private int   registeredArtifactPowerUpDefense;
    private float registeredArtifactPowerUpCriticalRate;
    private float registeredArtifactPowerUpCriticalDamage;
    private float registeredArtifactPowerUpHPPercentage;
    private float registeredArtifactPowerUpAttackPercentage;
    private float registeredArtifactPowerUpDefensePercentage;

    // マナ強化
    private int   registeredPrestigePowerUpHP;
    private int   registeredPrestigePowerUpAttack;
    private int   registeredPrestigePowerUpDefense;
    private float registeredPrestigePowerUpCriticalRate;
    private float registeredPrestigePowerUpCriticalDamage;
    private float registeredPrestigePowerUpHPPercentage;
    private float registeredPrestigePowerUpAttackPercentage;
    private float registeredPrestigePowerUpDefensePercentage;

    // オーブ効果
    private float registeredOrbPowerUpHPPercentage;
    private float registeredOrbPowerUpAttackPercentage;
    private float registeredOrbPowerUpDefensePercentage;
    private float registeredOrbPowerUpCriticalRate;
    private float registeredOrbPowerUpCriticalDamage;

    // 防具効果
    private float registeredArmorPowerUpHPPercentage;
    private float registeredArmorPowerUpAttackPercentage;
    private float registeredArmorPowerUpDefensePercentage;
    private float registeredArmorPowerUpCriticalRate;
    private float registeredArmorPowerUpCriticalDamage;

    // 登録時の装備スナップショット
    private int registeredEquippedOrbIndex = -1;
    private Dictionary<ArmorSlotType, int> registeredEquippedArmorIndices
        = new Dictionary<ArmorSlotType, int>();
    private OrbData registeredEquippedOrb = null;
    private Dictionary<ArmorSlotType, ArmorData> registeredEquippedArmors
        = new Dictionary<ArmorSlotType, ArmorData>();

    // --- ステータス登録(現在のビルドをスナップショット) ---
    public void RegisterPlayerStats()
    {
        if (player == null) return;

        // レベル
        registeredLevel = player.level;

        // 基礎ステータス
        registeredBaseMaxHP               = player.baseMaxHP;
        registeredBaseAttack              = player.baseAttack;
        registeredBaseDefense             = player.baseDefense;
        registeredBaseCriticalRate        = player.baseCriticalRate;
        registeredBaseCriticalDamageMultiplier = player.baseCriticalDamageMultiplier;

        // G強化
        registeredGPowerUpHP              = player.gPowerUpHP;
        registeredGPowerUpAttack          = player.gPowerUpAttack;
        registeredGPowerUpDefense         = player.gPowerUpDefense;
        registeredGPowerUpCriticalRate    = player.gPowerUpCriticalRate;
        registeredGPowerUpCriticalDamage  = player.gPowerUpCriticalDamage;
        registeredGPowerUpHPPercentage    = player.gPowerUpHPPercentage;
        registeredGPowerUpAttackPercentage= player.gPowerUpAttackPercentage;
        registeredGPowerUpDefensePercentage= player.gPowerUpDefensePercentage;

        // AF強化
        registeredArtifactPowerUpHP       = player.artifactPowerUpHP;
        registeredArtifactPowerUpAttack   = player.artifactPowerUpAttack;
        registeredArtifactPowerUpDefense  = player.artifactPowerUpDefense;
        registeredArtifactPowerUpCriticalRate   = player.artifactPowerUpCriticalRate;
        registeredArtifactPowerUpCriticalDamage = player.artifactPowerUpCriticalDamage;
        registeredArtifactPowerUpHPPercentage   = player.artifactPowerUpHPPercentage;
        registeredArtifactPowerUpAttackPercentage= player.artifactPowerUpAttackPercentage;
        registeredArtifactPowerUpDefensePercentage= player.artifactPowerUpDefensePercentage;

        // マナ強化
        registeredPrestigePowerUpHP       = player.prestigePowerUpHP;
        registeredPrestigePowerUpAttack   = player.prestigePowerUpAttack;
        registeredPrestigePowerUpDefense  = player.prestigePowerUpDefense;
        registeredPrestigePowerUpCriticalRate   = player.prestigePowerUpCriticalRate;
        registeredPrestigePowerUpCriticalDamage = player.prestigePowerUpCriticalDamage;
        registeredPrestigePowerUpHPPercentage   = player.prestigePowerUpHPPercentage;
        registeredPrestigePowerUpAttackPercentage= player.prestigePowerUpAttackPercentage;
        registeredPrestigePowerUpDefensePercentage= player.prestigePowerUpDefensePercentage;

        // オーブ効果
        registeredOrbPowerUpHPPercentage  = player.orbPowerUpHPPercentage;
        registeredOrbPowerUpAttackPercentage = player.orbPowerUpAttackPercentage;
        registeredOrbPowerUpDefensePercentage = player.orbPowerUpDefensePercentage;
        registeredOrbPowerUpCriticalRate  = player.orbPowerUpCriticalRate;
        registeredOrbPowerUpCriticalDamage= player.orbPowerUpCriticalDamage;

        // 防具効果
        registeredArmorPowerUpHPPercentage    = player.armorPowerUpHPPercentage;
        registeredArmorPowerUpAttackPercentage= player.armorPowerUpAttackPercentage;
        registeredArmorPowerUpDefensePercentage= player.armorPowerUpDefensePercentage;
        registeredArmorPowerUpCriticalRate    = player.armorPowerUpCriticalRate;
        registeredArmorPowerUpCriticalDamage  = player.armorPowerUpCriticalDamage;

        // 装備中のオーブをスナップショット
        registeredEquippedOrb = null;
        registeredEquippedOrbIndex = -1;
        if (OrbManager.Instance != null && OrbManager.Instance.equippedOrb != null)
        {
            registeredEquippedOrb = OrbManager.Instance.equippedOrb.Clone();
            registeredEquippedOrbIndex = OrbManager.Instance.ownedOrbs
                .IndexOf(OrbManager.Instance.equippedOrb);
        }

        // 装備中の防具をスナップショット
        registeredEquippedArmors.Clear();
        registeredEquippedArmorIndices.Clear();
        if (ArmorManager.Instance != null)
        {
            foreach (ArmorSlotType slot in System.Enum.GetValues(typeof(ArmorSlotType)))
            {
                if (ArmorManager.Instance.equippedArmors.ContainsKey(slot) &&
                    ArmorManager.Instance.equippedArmors[slot] != null)
                {
                    var armor = ArmorManager.Instance.equippedArmors[slot];
                    registeredEquippedArmors[slot] = armor.Clone();
                    int idx = ArmorManager.Instance.ownedArmors[slot].IndexOf(armor);
                    registeredEquippedArmorIndices[slot] = idx;
                }
                else
                {
                    registeredEquippedArmors[slot] = null;
                    registeredEquippedArmorIndices[slot] = -1;
                }
            }
        }

        // セーブ
        if (saveManager != null)
        {
            saveManager.SaveRegisteredStatsImmediate();
        }

        // ログ
        if (battleLogManager != null)
        {
            battleLogManager.AddLog(
                $"<color=cyan>ステータスを登録しました(Lv.{registeredLevel})</color>",
                Color.cyan
            );
        }
    }

DungeonUIManager:UIからの登録&チェック部分

ステータス登録 UI

public class DungeonUIManager : MonoBehaviour
{
    [Header("ステータス登録")]
    public Button statusRegistrationPanelButton;
    public GameObject statusRegistrationPanel;
    public Button statusRegistrationCloseButton;
    public Button statusRegistrationButton;
    public TextMeshProUGUI registeredStatusText;

    [Header("ステータス未登録警告")]
    public GameObject statusNotRegisteredPanel;
    public TextMeshProUGUI statusNotRegisteredText;
    public Button statusNotRegisteredCloseButton;

    public PlayerManager playerManager;

    private DungeonType selectedDungeonType;

    private void Start()
    {
        // パネル開閉
        statusRegistrationPanelButton.onClick.AddListener(OpenStatusRegistrationPanel);
        statusRegistrationCloseButton.onClick.AddListener(CloseStatusRegistrationPanel);

        // 「ステータス登録」ボタン
        statusRegistrationButton.onClick.AddListener(RegisterPlayerStats);

        // 警告パネル
        statusNotRegisteredCloseButton.onClick.AddListener(CloseStatusNotRegisteredPanel);

        statusRegistrationPanel.SetActive(false);
        statusNotRegisteredPanel.SetActive(false);
    }

    // パネルを開く
    public void OpenStatusRegistrationPanel()
    {
        statusRegistrationPanel.SetActive(true);
        UpdateRegisteredStatusDisplay();
    }

    // パネルを閉じる
    public void CloseStatusRegistrationPanel()
    {
        statusRegistrationPanel.SetActive(false);
    }

    // ボタンから呼ばれる「ステータス登録」
    private void RegisterPlayerStats()
    {
        if (playerManager == null) return;

        playerManager.RegisterPlayerStats();
        UpdateRegisteredStatusDisplay();
    }

    // 登録済みステータスの表示更新
    private void UpdateRegisteredStatusDisplay()
    {
        if (playerManager == null || registeredStatusText == null) return;

        var stats = playerManager.GetRegisteredStats();
        string name = playerManager.player != null &&
                      !string.IsNullOrWhiteSpace(playerManager.player.name)
                      ? playerManager.player.name
                      : "勇者";

        if (stats.level > 0)
        {
            registeredStatusText.text =
                $"【登録ステータス】\n{name}\n" +
                $"Lv.{stats.level}\n" +
                $"HP: {stats.maxHP}\n" +
                $"攻撃: {stats.attack}\n" +
                $"防御: {stats.defense}\n" +
                $"会心率: {stats.criticalRate * 100f:F1}%\n" +
                $"会心ダメージ: {stats.criticalDamage * 100f:F1}%";
        }
        else
        {
            registeredStatusText.text =
                $"【登録ステータス】\n{name}\nステータス未登録";
        }
    }

    // ステータス未登録警告
    private void ShowStatusNotRegisteredPanel()
    {
        if (statusNotRegisteredPanel == null) return;

        statusNotRegisteredPanel.SetActive(true);
        if (statusNotRegisteredText != null)
        {
            statusNotRegisteredText.text = "ステータス保存してください。";
        }
    }

    private void CloseStatusNotRegisteredPanel()
    {
        if (statusNotRegisteredPanel == null) return;
        statusNotRegisteredPanel.SetActive(false);
    }
}

遺跡挑戦時の一連の流れ

  1. ステータス登録
    • プレイヤーは「ステータス登録」パネルから 現在のレベル・基礎ステータス・各種強化値・オーブ・防具構成を PlayerManager.RegisterPlayerStats() によってスナップショット保存する。
    • 登録された内容は GetRegisteredStats() で最終ステータスとして復元でき、 UI 上にも表示される。
  2. 遺跡入場前チェック
    • 遺跡の種類ボタンを押して難易度選択に進む前に、 GetRegisteredStats() の level を見て 登録済みかどうか を判定する。
    • 未登録なら警告パネルを表示し、遺跡には入れない。
  3. 遺跡に入るとき(DungeonManager側連携)
    • PlayerManager.SavePreDungeonStats() で現在の状態を保存。
    • その直後 PlayerManager.ApplyRegisteredStats() で 登録ビルドを実際の player に適用し、遺跡専用のステータスで挑戦する。
  4. 遺跡から戻るとき
    • クリア/敗北に関わらず、最後に PlayerManager.RestorePreDungeonStats() を呼び出し、 遺跡入場前のステータス・強化値・お金などを元通りに復元する。

Unityで動作確認

左上のステータス登録ボタンを押すと、保存された

  • ステータス
  • オーブ
  • 防具一覧

が見やすく表示されます。

この画面が“強さの証明”になるので、

スクショしてSNSに投稿するユーザー体験を想定しています。

まとめ

今回は装備機能のコアとなるステータス保存機能を実装しました!

遺跡挑戦がスムーズになるだけでなく、

SNSシェアにつながる魅力的な要素にもなったと思います。

次回予告

次はいよいよ、

実際の防具システムそのものを実装していきます!

自分だけの最強装備を揃えるワクワク感を

形にしていきますので、ぜひお楽しみに!

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