はじめに
今回行う作業は2つです。まず初めにアイテムスロットをクリックされた際の挙動を自在に設定でいるよう、
ItemDetailクラスを作成します。次にバッグから所持アイテムの取得メソッドの作成を行います。設計図では、下図の「
★」印を付けた箇所に相当します。

アイテムスロットのクリック
初めに作業の流れを説明します。まずは
ItemDetail抽象クラスを作成し、中で
OnClickCallback()抽象メソッドを定義します。アイテムスロットのクリックがされたタイミングで、この抽象メソッドを呼ぶように設定できれば完成です。ところで、アイテムをクリックした際の挙動は
ItemBagクラスの次の部分で指定していました。
ItemBag.cs
void Awake()
{
for (int i = 0; i < slotNumber; i++)
{
//slotNumber の数だけスロットを生成し、ItemBagの子オブジェクトとして配置する
var slot = Instantiate(slotPrefab, this.transform, false)
.GetComponent<ItemSlot>();
// ItemSlotの初期化
slot.Initialize((item, number, slotObj) => Debug.Log("クリックされました"));
AllSlots.Add(slot);
}
UpdateItem();
}
つまり10行目の
Initialize()メソッドの引数にコールバクメソッドを渡すことで、目標の機能を実現できそうです。
作業をはじめます。まずはUnityの「Inventory/Scripts/」フォルダに「ItemDetailBase」スクリプトを作成してください。抽象クラスとなるため「Base」というワードを追加しています。

次に、スクリプトの中身を下記コードで上書きしてください。
ItemDetailBase.cs
using UnityEngine;
namespace FlMr_Inventory
{
/// <summary>
/// スロットがクリックされたときの挙動を制御する抽象クラス
/// </summary>
public abstract class ItemDetailBase : MonoBehaviour
{
/// <summary>
/// スロットがクリックされた際に呼ばれるコールバックメソッド
/// </summary>
/// <param name="itemBag">アイテムバッグ</param>
/// <param name="item">スロットに入っているアイテム</param>
/// <param name="number">そのアイテムの所持数</param>
/// <param name="slotObj">スロットのゲームオブジェクト</param>
protected internal abstract void OnClickCallback
(ItemBag itemBag, ItemBase item, int number, GameObject slotObj);
}
}
上で説明した通り、抽象メソッドを一つ持ったシンプルなクラスになっています。このような場合インターフェースを用いるのが普通ですし、事実、後にインターフェースによる実装も解説予定です。にもかかわらずここでは抽象クラスを用いた理由は、インターフェースをシリアル化し、インスペクター上で登録するためには工夫を要するためです。
では
ItemBagクラス内で
OnClickCallback()メソッドを参照しましょう。まずはインスペクター上から
ItemDetailBaseクラス(のサブクラス)のインスタンスを登録できるようにします。
ItemBagクラス内に次のコードを追加してください。
ItemBag.cs
/// <summary>
/// スロットがクリックされた際の挙動
/// </summary>
[SerializeField] private ItemDetailBase itemDetail;
続けて
Awake()メソッドを次のように編集します。
ItemBag.cs
void Awake()
{
for (int i = 0; i < slotNumber; i++)
{
//slotNumber の数だけスロットを生成し、ItemBagの子オブジェクトとして配置する
var slot = Instantiate(slotPrefab, this.transform, false)
.GetComponent<ItemSlot>();
// ItemSlotの初期化
slot.Initialize(
// スロットがクリックされた際に呼ばれる関数
(item, number, slotObj) => itemDetail.OnClickCallback(this,item,number,slotObj)
);
AllSlots.Add(slot);
}
UpdateItem();
}
この変更により、スロットがクリックされた際に12行で作成している関数が呼ばれ、
OnClickCallback()が実行されようになりました。
一度動作確認をしたいのですが、
ItemDetailBaseクラスは抽象クラスであるためインスタンスを生成することができません。そこでUnityに戻り「Inventory/Demo/Scripts/」フォルダにItemDetailTestスクリプトを作成してください。その中を次のコードを上書きします。
ItemDetailDemo.cs
using UnityEngine;
namespace FlMr_Inventory.Demo
{
/// <summary>
/// ItemDetailの動作確認のためのクラス
/// </summary>
public class ItemDetailDemo : ItemDetailBase
{
protected internal override void OnClickCallback
(ItemBag itemBag, ItemBase item, int number, GameObject slotObj)
{
Debug.Log($"{itemBag}内の{item.ItemName}がクリックされました。現在{number}個持っています。");
}
}
}
全てのスクリプトが保存されていることを確認し、Unityに戻ってください。そこでシーン上に空のオブジェクトを作成し、ItemDetailと名付けてください。さらに先ほど作成した「ItemDetailDemo」スクリプトをアタッチしてください。

さらにItemBagオブジェクトのコンポーネントに、ItemDetailオブジェクトを登録します。

ではゲームを実行し、スロットをクリックしてみてください。下図のように、正しくアイテム等の情報が表示されることを確認してください。

バッグアイテムの情報取得メソッド
次にバッグから所持アイテムの取得メソッドの作成を行います。次のコードを
ItemBagクラスに追加してください。
ItemBag.cs
/// <summary>
/// バッグ内の全てのアイテムとその個数を取得する
/// </summary>
/// <returns></returns>
public Dictionary<ItemBase, int> GetAllItems()
{
return Data.Ids
.ToDictionary(id => ItemUtility.Instance.ItemIdTable[id], id=>Data.GetQty(id));
/***** Linqを使わない記述 ******
Dictionary<ItemBase, int> result = new Dictionary<ItemBase, int>();
foreach (var id in Data.Ids)
{
ItemBase item = ItemUtility.Instance.ItemIdTable[id];
result.Add(item,Data.GetQty(id));
}
return result;
*****************************/
}
/// <summary>
/// idを指定して個数を取得
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public int Find(int id)
{
return Data.GetQty(id);
}
/// <summary>
/// アイテムを指定して個数を取得
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public int Find(ItemBase item) => Find(item.UniqueId);
2つの便利メソッドを追加しました。まず一つ目はバッグの全情報を返す
GetAllItems()メソッドです。
Keyをアイテム、
Valueを個数とした辞書を作成し返しています。Linqが苦手な方は、コメントアウトしたコードを見ると分かりやすいと思います。
次の二つは(引数は異なりますが)同じメソッドです。アイテム(又はアイテムid)を指定して個数を受け取るメソッドです。
今回は以上です。
ここまでの状況は
Github から確認できます。