RPGの持ち物、サバイバルゲームの所持品、クラフトゲームの素材欄——インベントリは、多くのゲームに欠かせないシステムです。ところがいざ作ろうとすると、アイテムの定義・所持数の管理・スタック・UIの更新が絡み合って、どこから手を付けるか迷いがちです。ここでも鍵は データとUIの分離 。「何をいくつ持っているか(データ)」と「それをどう並べて見せるか(UI)」を分けると、驚くほどすっきり作れます。
この記事では、拡張しやすいインベントリの土台を組み立てます。アイテムを ScriptableObject で定義し、スロットのデータ構造、追加・削除・スタック 処理、グリッドUI への反映、そして セーブ まで。RPGの持ち物欄、サバイバルのバッグ、ローグライクの所持品——どれもこの設計から作れます。
この記事でわかること
- アイテムを ScriptableObject で定義する(アイテムマスタ)
- インベントリの データ構造(スロットのリスト)
- 追加・削除・スタック の処理
- グリッドUI への反映(Layout Group)
- 実践:拾う・使う・並べるインベントリ
動作確認環境: Unity 2022.3 LTS / Unity 6
データとUIを分離する
インベントリ作りで最初に決めるべきは、 「持ち物のデータ」と「表示するUI」をはっきり分ける ことです。ここを混ぜると、アイテムを1個拾うたびにUIコードを直す羽目になります。

構成は3つの層に分かれます。
| 層 | 役割 |
|---|---|
| アイテムマスタ(ScriptableObject) | アイテムの「設計図」。名前・アイコン・最大スタック数など、種類ごとに1つ |
| インベントリデータ(スロットのリスト) | プレイヤーが「何をいくつ持っているか」。アイテムへの参照+個数の集まり |
| グリッドUI(Canvas) | データを見て、アイコンと個数をマス目に並べて表示する |
大事なのは、 データが変わったらUIを再描画する という一方向の流れです。「拾う」「使う」といった操作はデータだけを変え、UIはそれを見て描き直すだけ。この分離が、あとでスタックや並べ替えを足すときに効いてきます。
アイテムをScriptableObjectで定義する
まずアイテムの「設計図」を ScriptableObject で定義します。「ポーション」「鉄の剣」といった アイテムの種類ごとに1つのアセット を作ります。

using UnityEngine;
[CreateAssetMenu(fileName = "NewItem", menuName = "Inventory/Item Data")]
public class ItemData : ScriptableObject
{
public string itemName; // アイテム名
public Sprite icon; // アイコン画像
[TextArea] public string description;
public int maxStack = 99; // 1スロットに重ねられる最大数
}
[CreateAssetMenu] で Create > Inventory > Item Data からアイテムを作れます。ポーションなら maxStack = 99、剣なら maxStack = 1(重ねられない)というように、種類ごとの性質をアセットに持たせます。この ItemData は アイテムの種類そのもの で、全プレイヤー・全ドロップで共有される「マスタデータ」です。プレイヤーが持つのは、この参照+個数だけになります。
スロットのリストとスタック処理
プレイヤーの持ち物は、 スロット(マス)のリスト で表します。各スロットは「どのアイテムを、いくつ持っているか」を持ちます。

using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class InventorySlot
{
public ItemData item; // どのアイテムか(種類への参照)
public int count; // いくつ持っているか
}
public class Inventory : MonoBehaviour
{
public List<InventorySlot> slots = new();
public int maxSlots = 20; // 持ち物欄のマス数上限
// アイテムを追加する(スタック対応)
// 戻り値:入りきらなかった個数(0なら全部入った)
public int AddItem(ItemData item, int amount = 1)
{
// 1. 同じアイテムで空きのあるスロットへ、「入るぶんだけ」詰める
foreach (var slot in slots)
{
if (slot.item == item && slot.count < item.maxStack)
{
int space = item.maxStack - slot.count; // このスロットの空き
int moved = Mathf.Min(space, amount); // 空きと追加数の小さい方だけ入れる
slot.count += moved;
amount -= moved;
if (amount == 0) break;
}
}
// 2. まだ余っていれば、新しいスロットへ(マスが空いている限り)
while (amount > 0 && slots.Count < maxSlots)
{
int moved = Mathf.Min(item.maxStack, amount);
slots.Add(new InventorySlot { item = item, count = moved });
amount -= moved;
}
RefreshUI(); // データが変わったのでUI更新
return amount; // 0でなければ「持ちきれなかった」
}
}
ポイントは スタック(重ね持ち)の判定 です。アイテムを追加するとき、 同じ種類のスロットが既にあれば「空きのぶんだけ」個数を増やし、余りは次のスロットへ 回します。ここで「空きのぶんだけ」を省いてslot.count += amountと書いてしまうと、たとえば残り9個しか入らないスロット(90/99)に20個追加したとき 110個になってmaxStackを突き破る バグになります。Mathf.Min(空き, 追加数)のひと手間が、この定番バグを防ぎます。
もう1つの工夫が 「入りきらなかった個数を返す」 ことです。戻り値が0でなければ持ち物が満杯だったということなので、呼び出し側は「持ち物がいっぱいだ!」の表示や、拾えなかったアイテムをフィールドに残す処理につなげられます。削除は逆に、個数を減らし、0になったらスロットごと取り除きます。配列とList の操作に慣れておくと、この辺りがスムーズです。
グリッドUIへ反映する
データができたら、それを マス目状のUI に並べます。ここで uGUIのLayout Group が活躍します。

Grid Layout Group コンポーネントを付けた親オブジェクトの下に、スロットUI(アイコン画像+個数テキスト)を並べると、 自動で格子状に整列 します。マスの大きさや間隔もコンポーネントで設定でき、自分で座標計算する必要はありません。
UIの更新は、先ほどの RefreshUI() で行います。流れはシンプルです。
- インベントリの
slotsを1つずつ見る - スロットUIに、
item.icon(アイコン)とcount(個数)をセットする - 空きスロットは、アイコンを消して空欄にする
「データを見て、UIを描き直す」だけ。データが増えても減っても、この RefreshUI() を呼べば表示が同期します。 UIはデータの鏡 、という関係を保つのがコツです。
実践:拾う・使う・並べるインベントリ
ここまでを組み合わせて、 アイテムを拾うと増え、使うと減る インベントリを完成させましょう。RPGの持ち物、サバイバルのバックパック、ローグライクの所持品——どれも同じ土台です。
