【Unity】Unityでインベントリシステムを作る:ScriptableObjectアイテム・スロット・スタック・グリッドUI

作成: 2026-07-12最終更新: 2026-07-13

RPGやサバイバルの持ち物欄を、データとUIを分離して作ります。アイテムをScriptableObjectで定義し、スロットのデータ構造、追加・削除・スタック処理、グリッドUIへの反映、セーブまで、拡張しやすいインベントリシステムの土台を解説します。

RPGの持ち物、サバイバルゲームの所持品、クラフトゲームの素材欄——インベントリは、多くのゲームに欠かせないシステムです。ところがいざ作ろうとすると、アイテムの定義・所持数の管理・スタック・UIの更新が絡み合って、どこから手を付けるか迷いがちです。ここでも鍵は データとUIの分離 。「何をいくつ持っているか(データ)」と「それをどう並べて見せるか(UI)」を分けると、驚くほどすっきり作れます。

この記事では、拡張しやすいインベントリの土台を組み立てます。アイテムを ScriptableObject で定義し、スロットのデータ構造追加・削除・スタック 処理、グリッドUI への反映、そして セーブ まで。RPGの持ち物欄、サバイバルのバッグ、ローグライクの所持品——どれもこの設計から作れます。

インベントリシステムのイメージ。左にアイテムマスタ(ScriptableObject)、中央にスロットのリスト(アイテム+個数)、右にグリッド状のインベントリUIが並び、データがUIに反映されている様子

この記事でわかること

  • アイテムを ScriptableObject で定義する(アイテムマスタ)
  • インベントリの データ構造(スロットのリスト)
  • 追加・削除・スタック の処理
  • グリッドUI への反映(Layout Group)
  • 実践:拾う・使う・並べるインベントリ

動作確認環境: Unity 2022.3 LTS / Unity 6

Sponsored

データとUIを分離する

インベントリ作りで最初に決めるべきは、 「持ち物のデータ」と「表示するUI」をはっきり分ける ことです。ここを混ぜると、アイテムを1個拾うたびにUIコードを直す羽目になります。

インベントリの構成図。アイテム��マスタ(ScriptableObject:名前・アイコン・最大スタック)→ インベントリデータ(スロットのリスト:アイテム参照+個数)→ グリッドUI(スロットUIの並び)という、データからUIへの一方向の流れを示す

構成は3つの層に分かれます。

役割
アイテムマスタ(ScriptableObject)アイテムの「設計図」。名前・アイコン・最大スタック数など、種類ごとに1つ
インベントリデータ(スロットのリスト)プレイヤーが「何をいくつ持っているか」。アイテムへの参照+個数の集まり
グリッドUI(Canvas)データを見て、アイコンと個数をマス目に並べて表示する

大事なのは、 データが変わったらUIを再描画する という一方向の流れです。「拾う」「使う」といった操作はデータだけを変え、UIはそれを見て描き直すだけ。この分離が、あとでスタックや並べ替えを足すときに効いてきます。

アイテムをScriptableObjectで定義する

まずアイテムの「設計図」を ScriptableObject で定義します。「ポーション」「鉄の剣」といった アイテムの種類ごとに1つのアセット を作ります。

アイテ��ムマスタの図。ItemData(ScriptableObject)が、アイテム名・アイコン画像・最大スタック数・説明といったフィールドを持つ。ポーション・剣・鉱石など、種類ごとにアセットが並ぶ様子を示す
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 の操作に慣れておくと、この辺りがスムーズです。

Sponsored

グリッドUIへ反映する

データができたら、それを マス目状のUI に並べます。ここで uGUIのLayout Group が活躍します。

グリッドUIの図。Grid Layout Groupを付けた親の下に、スロットUI(アイコン+個数テキスト)が自動で格子状に並ぶ。インベントリデータのスロット数だけスロットUIを生成・更新する流れを示す

Grid Layout Group コンポーネントを付けた親オブジェクトの下に、スロットUI(アイコン画像+個数テキスト)を並べると、 自動で格子状に整列 します。マスの大きさや間隔もコンポーネントで設定でき、自分で座標計算する必要はありません。

UIの更新は、先ほどの RefreshUI() で行います。流れはシンプルです。

  1. インベントリの slots を1つずつ見る
  2. スロットUIに、item.icon(アイコン)と count(個数)をセットする
  3. 空きスロットは、アイコンを消して空欄にする

「データを見て、UIを描き直す」だけ。データが増えても減っても、この RefreshUI() を呼べば表示が同期します。 UIはデータの鏡 、という関係を保つのがコツです。

実践:拾う・使う・並べるインベントリ

ここまでを組み合わせて、 アイテムを拾うと増え、使うと減る インベントリを完成させましょう。RPGの持ち物、サバイバルのバックパック、ローグライクの所持品——どれも同じ土台です。

実践の完成イメージ。フィールドでポーションを拾うとインベントリのスロットに追加�され、グリッドUIにアイコンと個数「×3」が表示される。ポーションを使うと個数が減り、0になるとスロットが消える完成したインベントリを示す

全体の流れはこうです。

  1. フィールドのアイテム(ItemData を持つ)に触れる → inventory.AddItem(itemData) を呼ぶ。戻り値が0でなければ満杯なので「持ちきれない!」を表示
  2. AddItem がスタックを判定してデータを更新し、RefreshUI() でグリッドを描き直す
  3. スロットUIをクリック → inventory.UseItem(slot) で効果を発動し、個数を1減らす
  4. 個数が0になったらスロットを削除し、再描画
// アイテムを使う(個数を減らし、0で削除)
public void UseItem(InventorySlot slot)
{
    // ここで効果を発動(回復・装備など)
    ApplyEffect(slot.item);

    slot.count--;
    if (slot.count <= 0) slots.Remove(slot); // 使い切ったらスロット削除
    RefreshUI();
}

ポイントは2つです。 「操作はデータだけを変える」(拾う・使う・捨てるは slots を書き換えるだけ。UIは触らない)と、 「変更のたびにRefreshUIを呼ぶ」(データを変えたら必ずUIを描き直す。この習慣で表示ズレが起きなくなる)。装備品・所持金・クラフト素材なども、ItemData に種別(enum)や追加パラメータを足していけば、同じ仕組みで拡張できます。持ち物の中身は セーブ&ロード で保存すれば、次回起動時も引き継げます。

おまけ:先に知っておくと良いこと

  • セーブは「参照」でなく「ID」で: ItemData(ScriptableObject)そのものはJSONで保存できません。セーブ時は各アイテムに一意のID(文字列)を振り、「IDと個数」を保存します。ロード時にIDから ItemData を引き直す仕組みにしましょう。詳しくは セーブ&ロード を参照。
  • ドラッグ&ドロップの並べ替え: スロットの入れ替えは、slots の要素を入れ替えて RefreshUI() するだけです。UIのドラッグは IBeginDragHandler などのインターフェースで実装します。
  • アイテム種別で色分け: ItemData に種別(武器・消耗品・素材)を持たせ、スロットUIの枠色を変えると、一気にゲームUIらしくなります。
  • 重量・所持数制限: スロット数の上限や、アイテムの重量合計で制限をかけると、サバイバルらしい駆け引きが生まれます。いずれもデータ側のチェックで実装できます。

まとめ

  • インベントリは アイテムマスタ(ScriptableObject)・データ(スロットのリスト)・UI(グリッド) の3層に分ける
  • アイテムは ScriptableObject で種類ごとに定義。最大スタック数などの性質をアセットに持たせる
  • 所持品は スロットのリスト(アイテム参照+個数)。追加は「既存の空きに入るぶんだけ→余りは新規スロットへ」。入りきらない個数は戻り値で返す
  • UIは Grid Layout Group で自動整列。データを見て RefreshUI() で描き直す
  • 操作は データだけを変え、変更のたびにUIを再描画。UIはデータの鏡

まずは「ポーションを3個持つ」だけの小さなインベントリを、スロットのリストとグリッドUIで表示してみてください。拾って増え、使って減るところまで作れば、もう立派な持ち物システムです。あなたのゲームのプレイヤーは、どんな宝物をカバンに詰め込みますか?

さらに学ぶために