38歩目 ガチャを実装! Unityで1日1ステップ!ノンフィールドRPG開発日記

1日1歩開発日記

ふりかえり

前回は設定画面を実装しました!

プレイヤーの詳細なステータス確認や、BGM・SEの音量調整ができるようになり、ゲームがより快適に遊べるようになりました。

ガチャを実装!

いよいよ、RPGの醍醐味でもある「ガチャ」を実装します!

使うのは、今まで用途がなかったダイヤ

ガチャで排出されるのは「アーティファクト」という装備のような存在です。

  • アーティファクトとは? 攻撃力やHP、会心率など、ステータスを強化してくれる特殊な装備のようなものです。 さらに、強化することで効果量がアップします!

スクリプトの変更

ArtifactManager.cs

アーティファクトの管理・表示・ガチャ機能を統括するクラスです。主な機能は以下の通り:

  • ガチャでランダムにアーティファクトを排出
  • プレイヤーの所持ダイヤを消費して実行
  • 未所持のアーティファクトを優先して排出
  • 所持しているアーティファクトはリスト表示
  • アーティファクトごとに強化ボタンがあり、ダイヤでレベルアップ可能
  • アーティファクトの効果は攻撃力、HP、会心率などに反映
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using System.Collections.Generic;

public class ArtifactManager : MonoBehaviour
{
    [Header("UI References")]
    public GameObject artifactPanel; // アーティファクトパネル
    public Button artifactButton; // アーティファクトボタン(開閉用)

    [Header("ガチャUI")]
    public Button gachaButton; // ガチャボタン
    public TextMeshProUGUI gachaCostText; // ガチャコスト表示
    public TextMeshProUGUI gachaResultText; // ガチャ結果表示

    [Header("アーティファクトリストUI")]
    public GameObject artifactListContent; // アーティファクトリストのコンテンツ
    public GameObject artifactItemPrefab; // アーティファクトアイテムのプレハブ
    public ScrollRect artifactScrollRect; // スクロールエリア

    [Header("参照")]
    public PlayerManager playerManager;
    public AudioManager audioManager;

    [Header("ガチャ設定")]
    public int gachaCost = 10; // ガチャコスト(ダイヤ)
    public int upgradeCost = 5; // 強化コスト(ダイヤ)

    [Header("アーティファクトデータ")]
    public List<ArtifactData> availableArtifacts = new List<ArtifactData>();

    private bool isPanelOpen = false;
    private List<ArtifactData> ownedArtifacts = new List<ArtifactData>(); // 所持アーティファクトリスト
    private List<GameObject> artifactItemObjects = new List<GameObject>(); // アーティファクトアイテムのGameObjectリスト

    void Start()
    {
        // アーティファクトボタンのイベントリスナーを設定
        if (artifactButton != null)
        {
            artifactButton.onClick.AddListener(ToggleArtifactPanel);
        }

        // ガチャボタンのイベントリスナーを設定
        if (gachaButton != null)
        {
            gachaButton.onClick.AddListener(PerformGacha);
        }

        // 初期設定:アーティファクトパネルは閉じた状態
        if (artifactPanel != null)
        {
            artifactPanel.SetActive(false);
        }
        isPanelOpen = false;

        // アーティファクトデータを初期化
        InitializeArtifactData();

        // UIを更新
        UpdateUI();
    }

    // アーティファクトパネルの表示/非表示を切り替え
    public void ToggleArtifactPanel()
    {
        isPanelOpen = !isPanelOpen;
        
        if (artifactPanel != null)
        {
            artifactPanel.SetActive(isPanelOpen);
        }

        if (isPanelOpen)
        {
            UpdateArtifactList();
            UpdateUI();
        }
    }

    // アーティファクトデータを初期化
    private void InitializeArtifactData()
    {
        availableArtifacts.Clear();
        
        // アーティファクトデータを追加
        availableArtifacts.Add(new ArtifactData
        {
            name = "勇者の剣",
            description = "攻撃力を大幅に上昇させる伝説の剣",
            artifactType = ArtifactType.Attack,
            baseValue = 10,
            valuePerLevel = 5,
        });

        availableArtifacts.Add(new ArtifactData
        {
            name = "守護者の盾",
            description = "防御力を大幅に上昇させる神聖な盾",
            artifactType = ArtifactType.Defense,
            baseValue = 5,
        });

        availableArtifacts.Add(new ArtifactData
        {
            name = "生命の宝石",
            description = "HPを大幅に上昇させる神秘的な宝石",
            artifactType = ArtifactType.HP,
            baseValue = 50,
            valuePerLevel = 25,
        });

        availableArtifacts.Add(new ArtifactData
        {
            name = "会心の指輪",
            description = "会心率を上昇させる魔術的な指輪",
            artifactType = ArtifactType.CriticalRate,
            baseValue = 0.05f,
            valuePerLevel = 0.02f,
        });

        availableArtifacts.Add(new ArtifactData
        {
            name = "破壊の斧",
            description = "会心ダメージ倍率を上昇させる凶悪な斧",
            artifactType = ArtifactType.CriticalDamage,
            baseValue = 0.5f,
            valuePerLevel = 0.2f,
        });

        availableArtifacts.Add(new ArtifactData
        {
            name = "a",
            description = "a",
            artifactType = ArtifactType.CriticalDamage,
            baseValue = 0.5f,
            valuePerLevel = 0.2f,
        });
    }

    // ガチャを実行
    public void PerformGacha()
    {
        if (playerManager == null || playerManager.player == null) return;

        if (playerManager.player.diamond >= gachaCost)
        {
            playerManager.player.diamond -= gachaCost;
            
            // 指定されたランクの未所持アーティファクトを取得
            List<ArtifactData> unownedArtifacts = GetUnownedArtifacts();
            List<ArtifactData> rankArtifacts = new List<ArtifactData>();
            
            foreach (var artifact in unownedArtifacts)
            {
                if (artifact.rank == randomRank)
                {
                    rankArtifacts.Add(artifact);
                }
            }
            
            // 指定されたランクのアーティファクトがない場合は、他のランクから選択
            if (rankArtifacts.Count == 0)
            {
                rankArtifacts = unownedArtifacts;
            }
            
            if (rankArtifacts.Count > 0)
            {
                int randomIndex = Random.Range(0, rankArtifacts.Count);
                ArtifactData newArtifact = rankArtifacts[randomIndex];

                // すでに所持していないか念のためチェック
                bool alreadyOwned = false;
                foreach (var owned in ownedArtifacts)
                {
                    if (owned.name == newArtifact.name)
                    {
                        alreadyOwned = true;
                        break;
                    }
                }
                if (!alreadyOwned)
                {
                    ArtifactData ownedArtifact = new ArtifactData
                    {
                        name = newArtifact.name,
                        description = newArtifact.description,
                        artifactType = newArtifact.artifactType,
                        baseValue = newArtifact.baseValue,
                        valuePerLevel = newArtifact.valuePerLevel,
                        level = 1,
                        rank = newArtifact.rank // ランクもコピー
                    };
                    ownedArtifacts.Add(ownedArtifact);
                }

                // アーティファクト効果を適用
                ApplyAllArtifactEffects();

                // ガチャSEを再生
                if (audioManager != null)
                {
                    audioManager.PlayPowerUpSE();
                }

                if (gachaResultText != null)
                {
                    gachaResultText.text = $"獲得: {newArtifact.name} ({newArtifact.rank}ランク) Lv.1";
                }

                UpdateArtifactList();
                UpdateUI();

                Debug.Log($"アーティファクトガチャ成功: {newArtifact.name} ({newArtifact.rank}ランク) Lv.1");
            }
            else
            {
                if (gachaResultText != null)
                {
                    gachaResultText.text = "全てのアーティファクトを獲得済みです";
                }
                Debug.Log("全てのアーティファクトを獲得済みです");
            }
        }
        else
        {
            if (gachaResultText != null)
            {
                gachaResultText.text = "ダイヤが足りません";
            }
            Debug.Log("ダイヤが足りません");
        }
    }

    // 未所持のアーティファクトを取得
    private List<ArtifactData> GetUnownedArtifacts()
    {
        List<ArtifactData> unownedArtifacts = new List<ArtifactData>();
        
        foreach (var artifact in availableArtifacts)
        {
            bool isOwned = false;
            foreach (var ownedArtifact in ownedArtifacts)
            {
                if (ownedArtifact.name == artifact.name)
                {
                    isOwned = true;
                    break;
                }
            }
            
            if (!isOwned)
            {
                unownedArtifacts.Add(artifact);
            }
        }
        
        return unownedArtifacts;
    }

    // 指定されたランクのアーティファクトを取得
    private ArtifactData GetRandomArtifactByRank(ArtifactRank rank)
    {
        List<ArtifactData> rankArtifacts = new List<ArtifactData>();
        
        foreach (var artifact in availableArtifacts)
        {
            if (artifact.rank == rank)
            {
                rankArtifacts.Add(artifact);
            }
        }
        
        if (rankArtifacts.Count > 0)
        {
            return rankArtifacts[Random.Range(0, rankArtifacts.Count)];
        }
        
        return availableArtifacts[0];
    }

    // アーティファクトを強化(個別)
    public void UpgradeArtifact(int artifactIndex)
    {
        if (playerManager == null || playerManager.player == null) return;
        if (artifactIndex < 0 || artifactIndex >= ownedArtifacts.Count) return;

        if (playerManager.player.diamond >= upgradeCost)
        {
            playerManager.player.diamond -= upgradeCost;

            // 指定されたアーティファクトを強化
            ArtifactData artifactToUpgrade = ownedArtifacts[artifactIndex];
            artifactToUpgrade.level++;

            // アーティファクト効果を再適用
            ApplyAllArtifactEffects();

            // 強化SEを再生
            if (audioManager != null)
            {
                audioManager.PlayPowerUpSE();
            }

            UpdateArtifactList();
            UpdateUI();

            Debug.Log($"アーティファクト強化成功: {artifactToUpgrade.name} Lv.{artifactToUpgrade.level}");
        }
        else
        {
            Debug.Log("ダイヤが足りません");
        }
    }

    // 全てのアーティファクト効果を適用
    private void ApplyAllArtifactEffects()
    {
        if (playerManager == null || playerManager.player == null) return;

        // アーティファクトステータスをリセット
        ResetArtifactStats();

        // 所持している全てのアーティファクトの効果を適用
        foreach (var artifact in ownedArtifacts)
        {
            float effectValue = artifact.baseValue + (artifact.valuePerLevel * (artifact.level - 1));

            switch (artifact.artifactType)
            {
                case ArtifactType.Attack:
                    playerManager.player.artifactPowerUpAttack += Mathf.RoundToInt(effectValue);
                    break;
                case ArtifactType.Defense:
                    playerManager.player.artifactPowerUpDefense += Mathf.RoundToInt(effectValue);
                    break;
                case ArtifactType.HP:
                    playerManager.player.artifactPowerUpHP += Mathf.RoundToInt(effectValue);
                    break;
                case ArtifactType.CriticalRate:
                    playerManager.player.artifactPowerUpCriticalRate += effectValue;
                    break;
                case ArtifactType.CriticalDamage:
                    playerManager.player.artifactPowerUpCriticalDamage += effectValue;
                    break;
            }
        }

        // 最終ステータスを再計算
        UpdateFinalStats();
    }

    // アーティファクトステータスをリセット
    private void ResetArtifactStats()
    {
        if (playerManager == null || playerManager.player == null) return;

        playerManager.player.artifactPowerUpHP = 0;
        playerManager.player.artifactPowerUpAttack = 0;
        playerManager.player.artifactPowerUpDefense = 0;
        playerManager.player.artifactPowerUpCriticalRate = 0f;
        playerManager.player.artifactPowerUpCriticalDamage = 0f;
    }

    // 最終ステータスを更新
    private void UpdateFinalStats()
    {
        if (playerManager == null || playerManager.player == null) return;

        // 最終ステータスは自動的に計算されるため、特に何もしない
        // PlayerDataのプロパティが自動的に計算する
    }

    // アーティファクトリストを更新
    private void UpdateArtifactList()
    {
        if (artifactListContent == null) return;

        // 既存のアイテムを削除
        foreach (var item in artifactItemObjects)
        {
            if (item != null)
            {
                Destroy(item);
            }
        }
        artifactItemObjects.Clear();

        // 所持アーティファクトがない場合
        if (ownedArtifacts.Count == 0)
        {
            // 空のメッセージを表示する処理を追加できます
            return;
        }

        // 所持アーティファクトのアイテムを作成
        for (int i = 0; i < ownedArtifacts.Count; i++)
        {
            CreateArtifactItem(i, ownedArtifacts[i]);
        }
    }

    // アーティファクトアイテムを作成
    private void CreateArtifactItem(int index, ArtifactData artifact)
    {
        if (artifactItemPrefab == null || artifactListContent == null) return;

        GameObject itemObject = Instantiate(artifactItemPrefab, artifactListContent.transform);
        artifactItemObjects.Add(itemObject);

        // アーティファクト情報を設定
        ArtifactItemUI itemUI = itemObject.GetComponent<ArtifactItemUI>();
        if (itemUI != null)
        {
            itemUI.Initialize(artifact, index, this);
        }
    }

    // UIを更新
    private void UpdateUI()
    {
        if (playerManager == null || playerManager.player == null) return;

        // ガチャコスト表示
        if (gachaCostText != null)
        {
            gachaCostText.text = $"ガチャコスト: {gachaCost}ダイヤ";
        }

        // ボタンの有効/無効設定
        if (gachaButton != null)
        {
            bool hasUnownedArtifacts = GetUnownedArtifacts().Count > 0;
            gachaButton.interactable = playerManager.player.diamond >= gachaCost && hasUnownedArtifacts;
        }
    }

    // アーティファクトパネルが開いているかどうかを取得
    public bool IsPanelOpen()
    {
        return isPanelOpen;
    }
}

// アーティファクトデータクラス
[System.Serializable]
public class ArtifactData
{
    public string name;
    public string description;
    public ArtifactType artifactType;
    public float baseValue;
    public float valuePerLevel;
    public int level = 1;
}

// アーティファクトタイプ
public enum ArtifactType
{
    Attack,
    Defense,
    HP,
    CriticalRate,
    CriticalDamage
}

ArtifactItemUI.cs

  • アーティファクト1つ1つを表示するUIコンポーネント
  • 名前・効果・説明・レベル・強化ボタンを管理
  • ダイヤが足りない場合はボタンを無効にする処理もあり
using UnityEngine;
using UnityEngine.UI;
using TMPro;

public class ArtifactItemUI : MonoBehaviour
{
    [Header("UI References")]
    public TextMeshProUGUI artifactNameText;
    public TextMeshProUGUI artifactLevelText;
    public TextMeshProUGUI artifactEffectText;
    public TextMeshProUGUI artifactDescriptionText;
    public Button upgradeButton;
    public TextMeshProUGUI upgradeCostText;

    private ArtifactData artifactData;
    private int artifactIndex;
    private ArtifactManager artifactManager;

    public void Initialize(ArtifactData artifact, int index, ArtifactManager manager)
    {
        artifactData = artifact;
        artifactIndex = index;
        artifactManager = manager;

        UpdateDisplay();
        SetupUpgradeButton();
    }

    private void UpdateDisplay()
    {
        if (artifactNameText != null)
            artifactNameText.text = artifactData.name;

        if (artifactLevelText != null)
            artifactLevelText.text = $"レベル: {artifactData.level}";

        if (artifactEffectText != null)
        {
            float effectValue = artifactData.baseValue + (artifactData.valuePerLevel * (artifactData.level - 1));
            string effectDescription = GetEffectDescription(artifactData.artifactType, effectValue);
            artifactEffectText.text = effectDescription;
        }

        if (artifactDescriptionText != null)
            artifactDescriptionText.text = artifactData.description;

        if (upgradeCostText != null)
            upgradeCostText.text = $"強化コスト: {artifactManager.upgradeCost}ダイヤ";
    }

    private void SetupUpgradeButton()
    {
        if (upgradeButton != null)
        {
            upgradeButton.onClick.RemoveAllListeners();
            upgradeButton.onClick.AddListener(OnUpgradeButtonClicked);
            
            // ボタンの有効/無効設定
            bool canUpgrade = artifactManager.playerManager.player.diamond >= artifactManager.upgradeCost;
            upgradeButton.interactable = canUpgrade;
        }
    }

    private void OnUpgradeButtonClicked()
    {
        artifactManager.UpgradeArtifact(artifactIndex);
        UpdateDisplay();
        SetupUpgradeButton();
    }

    private string GetEffectDescription(ArtifactType type, float value)
    {
        switch (type)
        {
            case ArtifactType.Attack:
                return $"攻撃力 +{value}";
            case ArtifactType.Defense:
                return $"防御力 +{value}";
            case ArtifactType.HP:
                return $"HP +{value}";
            case ArtifactType.CriticalRate:
                return $"会心率 +{value * 100:F1}%";
            case ArtifactType.CriticalDamage:
                return $"会心倍率 +{value:F1}倍";
            default:
                return "";
        }
    }
} 

Unityでの設定

ArtifactManagerとArtifactItemUIのUnityでの設定手順を詳細に説明します。

アーティファクトプレハブ(ArtifactItemPrefab)の作成

  1. Hierarchy で右クリック → UIPanel を選択
  2. 作成されたPanelを ArtifactItemPrefab にリネーム
  3. Project ウィンドウで Prefabs フォルダを作成(なければ)
  4. ArtifactItemPrefabPrefabs フォルダにドラッグ&ドロップしてプレハブ化
  • 名前・効果・レベル・説明のText(TextMeshPro)を追加
  • 強化ボタン(Button)とそのコスト表示も設置
  • ArtifactItemUI スクリプトをアタッチし、各UI要素を紐付け

アーティファクトパネル

  • アーティファクト用のパネルを作成
  • ガチャボタンやガチャ結果表示用のTextも配置
  • ScrollView を使用して、所持アーティファクトを縦並びに表示

管理オブジェクト ArtifactManager

  • パネルやボタン、PrefabなどすべてをInspectorで接続
  • PlayerManager と AudioManager もドラッグ&ドロップで設定

動作確認

アーティファクトボタンを押すと専用パネルが開く

ガチャを回すと新しいアーティファクトが表示される

強化ボタンを押すとダイヤが消費され、アーティファクトの効果がアップ

アーティファクトの内容によって、プレイヤーの攻撃力・HP・会心系ステータスに反映!

まとめ

今回はガチャアーティファクトの実装を行いました!

強化要素が追加されて、育成のやりがいがアップしました!

ガチャがあることで「ゲームらしさ」もグッと引き立ちますね!

次回予告

次回はアーティファクトにランク(レア度)を設定し、ガチャの排出率調整を行います!

お楽しみに!

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