Unityで装備と使うメニューを作り装備変更したメモ。装備変更や回復アイテム消費など

unity 装備 変更 3Dゲームの作り方
※記事内に広告が含まれています。

バーチャル3Dクリエイター神部まゆみです(*^_^*)

この記事はUnityで装備変更や使うメニューを実装したので、それについての記事です。

前回の記事はこちら。

動作確認した最新バージョンは Unity 6000.3.1 です。

●PRスペース●

〇まゆみマート|BOOTH

BOOTHでVRoidテクスチャやVRChat向けオブジェクトなどを販売しています。いいねしてくれると励みになります(*^-^*)

やりたいこと:アイテムメニューに装備と使うボタンを作り、装備変更やアイテム消費を実装する

前回まででアイテムを捨てる・拾うまで実装できたので、今度は装備ボタンや使うボタンを実装したい。

ChatGPTパイセンに聞いてみる

とりあえず彼に聞けばなんとかなるだろう…。

unity 装備 変更
神部まゆみ
神部まゆみ

じゃあまずアイテムメニュー欄に装備スロットを用意して、デフォルトで銃を装備している設定を実装したほうがいいですかね。まだアイテムを捨てる設定しか作っていないため、装備ボタンすらないのです

ChatGPT
ChatGPT

うん、その流れがいちばん気持ちいいし、あとあと楽になります。
いまのあなたの進み方を見てると、

「まず装備スロット」→「デフォルト装備」→「装備ボタン」

この順番がベストです。

① まず「装備スロット」を作る

インベントリとは別に、プレイヤー側にこういう枠を持たせます。

public ItemData equippedWeapon;

これが
「いま手に持ってる武器」。

Inventory は「持ち物」、
equippedWeapon は「今使ってるもの」
と役割を分けます。


② デフォルトで銃を装備

Player(または GameManager)に

[SerializeField] private ItemData defaultWeapon;

を用意して、Start で

void Start()
{
    Equip(defaultWeapon);
}

これだけで
ゲーム開始時に銃を持っている状態になります。


③ アイテムメニューに「装備」ボタン

今ある「捨てる」の隣に置くのが自然です。

UI的にも

[ 装備 ]
[ 捨てる ]

って並ぶと直感的。


④ 装備ボタンがやること

選択中のアイテムを

  • Inventory からは消さない
  • equippedWeapon にセット
  • 見た目の武器を切り替える
public void EquipSelected()
{
    if (selectedItem == null) return;

    if (selectedItem.weaponType != WeaponType.Gun &&
        selectedItem.weaponType != WeaponType.Melee)
        return;

    player.Equip(selectedItem);
}

⑤ UI に「装備中」表示が出せる

ここが気持ちいいポイントです。

  • 装備中のスロットに
    • 枠の色
    • 「E」マーク
    • アイコン

をつけられる。

これ、あとでクイック切り替えにもそのまま使えます。


なぜこの順番がいいか

いきなりクイック切り替えを作ると👇

  • 装備の概念が曖昧
  • UI とロジックがズレる
  • バグりやすい

でも

メニューで装備 → 攻撃が変わる

ができていれば、
ショートカットは「同じ処理を呼ぶだけ」になります。

ちょっと一気に進める感じだから少し大変そう?

でもこれができれば装備・使うのコアな部分が完成してしまうので、あとはメソッドを呼ぶだけでアイテム関連のほとんどは作れそうです。

実装していってみる

とりあえず実装していってみます。

ItemData.csを書き換える

ItemData.csを書き換え、アイテムタイプを追加します。

using UnityEngine;

[CreateAssetMenu(menuName = "Game/Item")]
public class ItemData : ScriptableObject
{
    public string itemName;
    public Sprite icon;
    [TextArea]
    public string description;

    [Header("World Drop")]
    public GameObject dropPrefab;

    public ItemUseType useType;

    public EquipSlot equipSlot;


    public enum ItemUseType
    {
        None,
        Equip,   // 武器・防具
        Consume // 回復など
    }
}

これでアイテムタイプを指定できるようになったので設定しておきます。

後で装備するところまでやるので武器はEquipにしておく。

unity 装備 変更

装備・使うボタンを配置する

アイテムタイプがEquip(武器・防具)なら装備ボタンを、回復アイテムなど使えるアイテムなら使うボタンを、素材などなら何も表示しないようにします。

そのため装備ボタンと使うボタンは同じ位置に配置しておく。とりあえず動作確認のために少しずらしておくけど。

unity 装備 変更

以前作った詳細説明パネルの子に配置するけど、以前作った捨てるボタンをコピペして作るとラク。

unity 装備 変更

アイテムスロットにE(テキストメッシュプロ)を配置する

現在はとりあえずアイテムスロットを8個設置しているけど、そのすべての子にEって書いたテキストメッシュプロを配置しておきます。

最初はスクリプトで非表示にし、装備されたアイテムにEを表示します。

unity 装備 変更

EquipManager.csを作る

武器しか装備させないならここまでは必要ないかもしれないけど、後から防具やアクセサリとかも装備させたくなったら装備を管理するスクリプトがあったほうが良いので作っておきます。

using UnityEngine;
using System.Collections.Generic;

public class EquipManager : MonoBehaviour
{
    public static EquipManager Instance;

    // どの装備スロットに、インベントリの何番を装備しているか
    private Dictionary<EquipSlot, int> equippedIndex = new();

    void Awake()
    {
        if (Instance == null)
            Instance = this;
        else
            Destroy(gameObject);
    }

    // そのスロットに装備されているインデックス(なければ -1)
    public int GetEquippedIndex(EquipSlot slot)
    {
        return equippedIndex.TryGetValue(slot, out var index) ? index : -1;
    }

    // 装備する(インベントリの何番目かを渡す)
    public void Equip(ItemData item, int inventoryIndex)
    {
        var slot = item.equipSlot;

        equippedIndex[slot] = inventoryIndex;

        Debug.Log($"装備: {item.itemName} (slot {slot}, index {inventoryIndex})");
    }

    // スロットから外す
    public void Unequip(EquipSlot slot)
    {
        if (!equippedIndex.ContainsKey(slot)) return;

        equippedIndex.Remove(slot);

        Debug.Log($"外した: {slot}");
    }

    // このインベントリスロットは装備中?
    public bool IsEquipped(EquipSlot slot, int inventoryIndex)
    {
        return equippedIndex.TryGetValue(slot, out var i) && i == inventoryIndex;
    }
}

EquipSlot.csを作る

装備用のスクリプトを作っておく。これは作るだけでOK。

public enum EquipSlot
{
    Weapon,
    Head,
    Body,
    Arms,
    Legs,
    Accessory
}

ItemDetailPanel.csを書き換える

以前作ったItemDetailPanel.csを書き換えます。

using UnityEngine;
using UnityEngine.UI;
using TMPro;

public class ItemDetailPanel : MonoBehaviour
{
    [SerializeField] private Image icon;
    [SerializeField] private TMP_Text nameText;
    [SerializeField] private TMP_Text descriptionText;

    [SerializeField] private Inventory inventory;
    [SerializeField] private ItemMenuController menuController;

    private ItemData currentItem;

    [SerializeField] private DropItemSpawner dropSpawner;

    [SerializeField] private Button equipButton;
    [SerializeField] private Button useButton;
    [SerializeField] private Button dropButton;

    private int currentSlotIndex;

    public void Show(ItemData item, int slotIndex)
    {
        currentItem = item;
        currentSlotIndex = slotIndex;

        icon.sprite = item.icon;
        nameText.text = item.itemName;
        descriptionText.text = item.description;

        bool isEquip = item.useType == ItemData.ItemUseType.Equip;
        bool isUse = item.useType == ItemData.ItemUseType.Consume;

        equipButton.gameObject.SetActive(isEquip);
        useButton.gameObject.SetActive(isUse);

        if (isEquip)
        {
            bool equipped = EquipManager.Instance.IsEquipped(item.equipSlot, slotIndex);
            equipButton.GetComponentInChildren<TMP_Text>().text =
                equipped ? "外す" : "装備";
        }
        gameObject.SetActive(true);
    }

    public void Hide()
    {
        currentItem = null;
        gameObject.SetActive(false);
    }

    // ★捨てるボタン用
    public void OnClickDrop()
    {
        if (currentItem == null) return;

        dropSpawner.Drop(currentItem);
        inventory.Remove(currentItem);
        menuController.Refresh();
        Hide();
    }

    public void OnClickEquip()
    {
        if (currentItem == null) return;

        var slot = currentItem.equipSlot;

        bool isEquipped = EquipManager.Instance.IsEquipped(slot, currentSlotIndex);

        if (isEquipped)
        {
            EquipManager.Instance.Unequip(slot);
        }
        else
        {
            EquipManager.Instance.Equip(currentItem, currentSlotIndex);
        }

        menuController.Refresh();
        Show(currentItem, currentSlotIndex);
    }


    public void OnClickUse()
    {
        if (currentItem == null) return;

        Debug.Log($"{currentItem.itemName} を使用");

        inventory.Remove(currentItem);
        menuController.Refresh();
        Hide();
    }

}

ItemSlot.csを書き換える

ItemSlot.csも書き換えます。

using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class ItemSlot : MonoBehaviour
{
    [SerializeField] private Image icon;
    [SerializeField] private GameObject normalFrame;
    [SerializeField] private GameObject selectedFrame;
    [SerializeField] private TMP_Text equippedText;

    private ItemData currentItem;

    public void SetSelected(bool selected)
    {
        normalFrame.SetActive(!selected);
        selectedFrame.SetActive(selected);
    }

    public bool HasItem()
    {
        return currentItem != null;
    }

    public void SetItem(ItemData item, bool isEquipped)
    {
        currentItem = item;

        if (item == null)
        {
            icon.enabled = false;
            equippedText.gameObject.SetActive(false);
            return;
        }

        icon.enabled = true;
        icon.sprite = item.icon;
        equippedText.gameObject.SetActive(isEquipped);
    }


    public ItemData GetItem()
    {
        return currentItem;
    }
}

ItemMenuController.csを書き換える

ItemMenuController.csも書き換えます。

using UnityEngine;

public class ItemMenuController : MonoBehaviour
{
    [SerializeField] private Inventory inventory;

    [SerializeField] private ItemSlot[] slots;
    private int selectedIndex = -1;

    [SerializeField] private ItemDetailPanel detailPanel;

    [SerializeField] private EquipManager equipManager;


    public void SelectSlot(int index)
    {
        if (!slots[index].HasItem()) return;

        if (selectedIndex >= 0)
            slots[selectedIndex].SetSelected(false);

        selectedIndex = index;
        slots[selectedIndex].SetSelected(true);

        var item = slots[index].GetItem();
        detailPanel.Show(item, index);
    }

    void OnEnable()
    {
        Refresh();
    }

    public void Refresh()
    {
        int equippedIndex = equipManager.GetEquippedIndex(EquipSlot.Weapon);

        for (int i = 0; i < slots.Length; i++)
        {
            ItemData item = i < inventory.items.Count ? inventory.items[i] : null;
            bool isEquipped = (i == equippedIndex);
            slots[i].SetItem(item, isEquipped);
        }
    }
}

インスペクターで設定する

新しい項目が増えているところがあるのでインスペクターで指定します。

unity 装備 変更

MenuControllerにEquipManagerをアタッチし、ItemMenuControllerのEquipManagerに指定します。

unity 装備 変更

8個のアイテムスロットにEを指定する。上で作ったテキストメッシュプロのやつ。

unity 装備 変更

追加した装備ボタンと使うボタンのクリック時にItemDetailPanelのスクリプトを指定します。

装備はOnClickEquip()、使うはOnClickUse()で。

これで動いた!

これで装備が切り替えられましたね。

と言ってもまだハンドガンしか作っていないので、装備を切り替えてもグラフィックも何も変わらないけど(^_^;)

あと使って消費する処理も実装できました。まだ作りこんでないから消えるだけだけど。

つづく?

長いので少し戸惑ったけど、これで装備とアイテム消費の核となる機能は作れたので、あとはこれをベースに作っていけば行けそうです。

だいたいできたら武器やアイテムを作りこんでいく感じかなぁ。

また続きが書けたら追記します(*^_^*)

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