インベントリ / インベントリシステムの利用方法

今回は作成したインベントリシステムの利用方法を説明します。パッケージ外からのアクセスになるため、基本的なシステムの見た目は次の図のようになります。


主にいじる箇所は、ItemBagクラスから生えている3つのメソッド、スロットがクリックされた先の挙動を定義するOnClickCallback()メソッド、そしてアイテム図鑑です。これらの機能を用いつつ、システムのユーザー目線でインベントリを実装します。

作業の順番は次の通りです。
  1. アイテムの作成
  2. アイテムバッグの作成
  3. スロットがスリックされた際にメニューを表示




アイテムの作成 (図鑑の作成)

Unityで全く新しいプロジェクトを作成します。


ここに作成したインベントリシステムをインポートします。package manager、または前回までで作成したInventoryフォルダをD&Dします。
作業のためのディレクトリを掘ります。


「Inventory/Resources/」フォルダにはItemUtilityクラスのインスタンスを作成します。


次に「Inventory/Scripts/」に「Medicine」スクリプトを作成し、中を開いてください。このクラスはアイテム図鑑の一ページで、お薬アイテムの定義をしています。そのためこのクラスには(MonoBehaviourクラスではなく)ItemBaseクラスを継承させます。この時エラーが出ると思います。これはItemBaseクラスが定義されている名前空間を使用していないことが原因です。そこでエラー部にハイライトを当て「Alt+Enter」をクリックしてください。表示されるメニューから「using ...」を選択してください。
※このショートカットコマンドを使用せず、「using ... ;」と手書きしてもOKです。


この変更によりMedicineクラスはもはやMonoBehaviourでは無くなりました。Start()Update()メソッドを削除してください。


ではこのクラスのインスタンスをファイルとして保存するためMedicineクラスに[CreateAssetMenu(menuName = "Item/Medicine", fileName = "Medicine")]属性を付与します。


このままでは薬を使用した際の効果が定義できていません。そこでこのクラスにIUsableインターフェースを実装してください。この時コンパイルエラーが発生します。エラー部にハイライトを当て「Alt+Enter」を入力します。この時表示されるメニューから「インターフェースを実装します」を選択します。


これにより二つのメソッドが作成されます。Check()メソッドはこのアイテムが現在使用可能か判定するメソッドです。回復アイテムの場合は「プレイヤーのHPがMaxか否か」で判定することが多いでしょう。しかしこのプロジェクトにはプレイヤーがないためtrueを返すだけのメソッドにします。

次にUse()メソッドです。本来プレイヤーのHPを回復する処理が書かれるはずですが、ここではログを出すだけでとどめます。



Unityに戻り「Inventory/Items/」フォルダ内にMedicineクラスのインスタンスを作成します。


次に適当にインスタンスの設定を行います


最後にこのアイテムのインスタンスを「Inventory/Resources/ItemUtility」に追加します。

他にもアイテムを作成したい場合は、以上の操作を繰り返すだけです。



アイテムバッグの作成

次にItemBagの作成を行います。シーン上にImageオブジェクトを作成し、名前を「ItemBag」に変更し、適当な大きさに調節します。さらに「GridLayoutGroup」「ContentSizeFitter」「ItemBag」コンポーネントを追加します。コンポーネントの設定は下図を参考にしてください。


では動作確認のためのスクリプトを作成します。「Inventory/Scripts/」フォルダに「InventoryTest」スクリプトを作成して以下のコードで上書きしてください。
InventoryTest.cs
1using FlMr_Inventory;
2using UnityEngine;
3
4public class InventoryTest : MonoBehaviour
5{
6    void Start()
7    {
8        var bag = this.GetComponent<ItemBag>();
9        bag.AddItem(1, 1);
10    }
11}
Unityに戻って、今作成したスクリプトを「ItemBagオブジェクト」にアタッチして下さい。ではゲームを実行してみてください。
もし下図の画面が表示された場合ゲームを停止してから「Import TMP Essentials」を選択してください。

正しくアイテムが追加されているでしょうか。ただ、スロットをクリックした際にエラーが生じるはずです。これはスロットをクリックした際の動作を設定していないためです。



スロットがスリックされた際にメニューを表示

そこでシーン上に新たにImageオブジェクトを作成して「ItemDetail」と名前を付けます。色は何でも構わないです。さらに子オブジェクトに「TextMeshProUGUI」「Image」「TextMeshProUGUI」「空のオブジェクト」を作成し、それぞれ「itemName」「itemIcon」「description」「buttons」と名前を付けます。位置は下図のように並べます。


buttonsオブジェクトに「VertialLayoutGroup」を追加し、設定と位置を下図のように設定します。


では「Inventory/Scripts/」フォルダに「ItemDetail」スクリプトを作成し、ItemDetailオブジェクトにアタッチしてください。中を開き編集していきましょう。
まずはStart()Update()メソッドは不要なので削除してください。次にItemDetailクラスをItemDetailBaseクラスのサブクラスにしてください。コンパイルエラーが生じますが、以前と同様インベントリシステムの名前空間を使用すると解決します。しかしクラス名部分にエラーが発生するため「Alt+Enter」から「抽象クラスの実装」を選択します。


このOnClickCallback()はスロットをクリックした際、自動的に呼ばれるコールバックメソッドです。引数にはクリックされたアイテムの情報が入っているため、自由に使うことができます。まずアイテム名を表示してみましょう。表示先のTextMeshProUGUIクラスのインスタンスを変数として保持し、メソッド内で文字を変更します。この時TMPro名前空間を使用したことに注意してください。


Unityに戻り、ItemDetailのコンポーネントの「ItemName」にitemNameオブジェクトを登録します。さらにItemBagオブジェクトのコンポーネントの「ItemDetail」にItemDetailオブジェクトを登録します。動作確認をし、正しくアイテム名が表示されていることを確認してください。


さらにアイテムの説明とアイコンも、全く同様の手順で表示させます。


UnityでItemDetailのコンポーネントの設定を忘れずに行い、動作確認をしてください。


最後にボタンの追加を行いましょう。次のコードで上書きしてください。
ItemDetail.cs
1using FlMr_Inventory;
2using TMPro;
3using UnityEngine;
4using UnityEngine.Events;
5using UnityEngine.UI;
6
7public class ItemDetail : ItemDetailBase
8{
9    [SerializeField] private TextMeshProUGUI itemName,description;
10    [SerializeField] private Image itemIcon;
11    [SerializeField] private Button buttonPrefab;
12    [SerializeField] private Transform buttonsTrn;
13
14    protected override void OnClickCallback(ItemBag itemBag, ItemBase item, int numebr, GameObject slotObj)
15    {
16        itemName.text = item.ItemName;
17        description.text = item.Description;
18        itemIcon.sprite = item.Icon;
19
20        if (item is IUsable usable)
21        {
22            AddButton("Use", () =>
23            {
24                if (usable.Check())
25                {
26                    usable.Use();
27                    itemBag.RemoveItem(item.UniqueId, 1);
28                }
29            });
30        }
31
32        if(item is IDeletable)
33        {
34            AddButton("Delete", () => itemBag.RemoveItem(item.UniqueId,1));
35        }
36    }
37
38    private void AddButton(string buttonName, UnityAction function)
39    {
40        var button = Instantiate(buttonPrefab, buttonsTrn);
41        button.onClick.AddListener(function);
42        button.GetComponentInChildren<TextMeshProUGUI>().text = buttonName;
43    }
44
45
46}
Unityに戻り、ItemDetailコンポーネントのButtonPrefabとButtonTrnを下図のように設定します。


動作確認をして、Useボタンのみが表示されており、クリックによりログが表示されることを確認してください。

最後にMedicineクラスに、IUsableに加えてIDeletableを実装し、再度動作確認をしてください。
今度はDeleteボタンが表示されており、クリックすると(ログが表示されることなく)アイテムが消えます。



改善点はたくさんありますが、以上で基本的な機能が実装できました。