バーチャル3Dクリエイター神部まゆみです(*^_^*)
この記事はUnityでアイテム欄メニュー画面を作ってみた記事の続きです。
前回の記事はこちら。
動作確認した最新バージョンは Unity 6000.3.1 です。
やりたいこと:ScriptableObjectでアイテムデータを管理、格納順にアイテムスロットに画像と共に表示したい
前回一応メニュー画面らしきものでアイテムスロットの見栄えは作れたので、実際にアイテムデータを格納して表示できるようにしたい。
以前会話システムを作った時にScriptableObjectを使ったから、あれと同じ感じで使えると思う。
ChatGPTパイセンに聞いてみる
とりあえず彼に聞けばなんとかなるだろう…。




次は所持アイテムの管理を作っていきたいんですけど、Scriptableobjectとかいうのでやるんでしょうか?格納されている順番ごとにアイテム欄に表示も一緒にできるようにしたいんですけど。つまり画像データも格納できる形にしないといけないと思います


いい流れです。
ここからが「ゲームっぽさ」が一気に出るところですね。
そして質問の答えは──
はい、ScriptableObjectでやるのが王道です。
しかも今考えている
「順番どおりに表示したい」「画像も持たせたい」
という要件に ドンピシャ です。
順序立てて説明します。
全体像(まずここを掴む)
構成はこう分けるのが一番分かりやすいです👇
ItemData(ScriptableObject)
└ アイテム1個の定義(名前・アイコンなど)
Inventory(通常のMonoBehaviour)
└ 所持している ItemData の並び(List)
ItemSlot(UI)
└ Inventory の中身を表示するだけ
「データ」と「所持状態」を分離するのがポイントです。
① アイテム定義(ScriptableObject)
ItemData.cs
using UnityEngine;
[CreateAssetMenu(menuName = "Game/Item")]
public class ItemData : ScriptableObject
{
public string itemName;
public Sprite icon;
public bool isEquipable;
}
これでできること
- Project 上でアイテムを作れる
- Sprite(画像)をそのまま持てる
- 同じアイテムを何個でも参照できる
② 所持アイテム管理(順番を保持)
Inventory.cs
using System.Collections.Generic;
using UnityEngine;
public class Inventory : MonoBehaviour
{
public List<ItemData> items = new List<ItemData>();
public int MaxSlot => 10;
public ItemData GetItem(int index)
{
if (index < 0 || index >= items.Count) return null;
return items[index];
}
}
📌 List を使うことで
格納された順番 = 表示順 になります。
③ UI に反映する(超シンプル)
ItemSlot.cs
using UnityEngine;
using UnityEngine.UI;
public class ItemSlot : MonoBehaviour
{
[SerializeField] private Image icon;
public void SetItem(ItemData item)
{
if (item == null)
{
icon.enabled = false;
return;
}
icon.enabled = true;
icon.sprite = item.icon;
}
}
ItemMenuController.cs(一覧反映)
using UnityEngine;
public class ItemMenuController : MonoBehaviour
{
[SerializeField] private Inventory inventory;
[SerializeField] private ItemSlot[] slots;
void OnEnable()
{
Refresh();
}
public void Refresh()
{
for (int i = 0; i < slots.Length; i++)
{
ItemData item = inventory.GetItem(i);
slots[i].SetItem(item);
}
}
}
✔ Inventory の順番=UIの順番
✔ Spriteもそのまま表示できる
④ 実際の運用イメージ
- Project → Create → Game → Item
- 名前とアイコンを設定
- Inventory の
itemsにドラッグして並べる - メニューを開くとその順で表示される
この時点で
ほぼ完成形のインベントリです。
よくある疑問
Q. セーブデータに大丈夫?
- ScriptableObject 自体は保存しない
- ID(stringやint)だけ保存する
今は考えなくてOK。
Q. 個数(スタック)持ちたい
あとで
class InventoryItem
{
public ItemData data;
public int count;
}
に拡張できます。
まとめ
- ✅ アイテム定義は ScriptableObject
- ✅ Sprite を直接持たせられる
- ✅ 所持順は List で保持
- ✅ UIは参照するだけ
今の設計なら
「後からいくらでも拡張できる」
綺麗な構成です。
ScriptableObjectは以前使ったことがあるからだいたいイメージは掴めてるかな。
たぶんそこまで苦戦せずに作れると思います。
実際に実装してみる
とりあえず言われたとおりに実装してみます。
スクリプトをコピペする
4つあるのでコピペします。
最初「分けすぎじゃない?(^_^;)」と思ったけど、このくらい分けておいた方がメンテナンスしやすいみたいです。
ItemData.cs
using UnityEngine;
[CreateAssetMenu(menuName = "Game/Item")]
public class ItemData : ScriptableObject
{
public string itemName;
public Sprite icon;
public bool isEquipable;
}
Inventory.cs
using System.Collections.Generic;
using UnityEngine;
public class Inventory : MonoBehaviour
{
public List<ItemData> items = new List<ItemData>();
public int MaxSlot => 10;
public ItemData GetItem(int index)
{
if (index < 0 || index >= items.Count) return null;
return items[index];
}
}
ItemSlot.cs
using UnityEngine;
using UnityEngine.UI;
public class ItemSlot : MonoBehaviour
{
[SerializeField] private Image icon;
public void SetItem(ItemData item)
{
if (item == null)
{
icon.enabled = false;
return;
}
icon.enabled = true;
icon.sprite = item.icon;
}
}
ItemMenuController.cs
using UnityEngine;
public class ItemMenuController : MonoBehaviour
{
[SerializeField] private Inventory inventory;
[SerializeField] private ItemSlot[] slots;
void OnEnable()
{
Refresh();
}
public void Refresh()
{
for (int i = 0; i < slots.Length; i++)
{
ItemData item = inventory.GetItem(i);
slots[i].SetItem(item);
}
}
}
前回作ったPauseMenuController.csを書き換える
以下のスクリプトに書き換えます。
using UnityEngine;
using StarterAssets;
public class PauseMenuController : MonoBehaviour
{
[Header("UI")]
[SerializeField] private GameObject itemMenuCanvas;
[Header("Input")]
[SerializeField] private StarterAssetsInputs input;
private bool isOpen;
[SerializeField] private ThirdPersonController controller;
[SerializeField] private ItemMenuController itemMenu;
void Start()
{
// 念のため最初は閉じておく
itemMenuCanvas.SetActive(false);
}
void Update()
{
if (input == null) return;
if (input.pause)
{
input.pause = false; // 1回分の入力を消費
ToggleMenu();
}
}
void ToggleMenu()
{
itemMenu.Refresh();
isOpen = !isOpen;
itemMenuCanvas.SetActive(isOpen);
Time.timeScale = isOpen ? 0f : 1f;
// ★ここが肝
controller.enabled = !isOpen;
input.enabled = !isOpen;
Cursor.lockState = isOpen
? CursorLockMode.None
: CursorLockMode.Locked;
Cursor.visible = isOpen;
}
}
アイテムが作れるようになったのでいくつか作る
ここまででアイテムが作れるようになりました。
プロジェクトで右クリック⇒作成⇒ゲーム⇒Item で。


ちゃんとスプライト画像も設定できて、装備可能かのチェックも付けられます。


アイテム専用のフォルダを作ってそこに作っていくと良いかな。


プレイヤーにInventoryコンポーネントを付けて所持品を持たせる
後で拾う・捨てるなどを実装するつもりだけど、とりあえずこれで初期所持品を持たせられます。


とりあえず三つしか作ってないので三つ設定しておく。
MenuControllerの設定
前回PauseMenuControllerを付けた空のオブジェクトにItemMenuControllerも追加、メニューに表示したいアイテムスロットの分だけスロットを作って指定する。


アイテムスロットの設定
ここが一番めんどくさいけど、一度作っておけばコピペするだけで使いまわせる。


ItemSlot1
ーItemFrame
ーItemIcon
みたいな構造になっている。
↓設定はこんな感じ。ItemSlotにItemSlotコンポーネントを追加してItemIconを指定する。
親のItemSlot1は空、ItemFrameは前回作ったフレームを表示するだけです。
これで動いた!
これで無事に読み込まれましたね。
インベントリを一つ減らすと、


三つめはちゃんとカラになって二つだけ表示されました。


今回はとりあえず三つしか作ってないけど、普通に追加していけばいくつでも作れます。
つづく?
案外手軽に実装できて良かった。
次はスロットにアイテムがあった時に、表示されてるアイテムを選択して「使う」「捨てる」とかメニューを出すとかかな?
メニュー画面ってやること多いからめんどくさいけど、まぁシンプルなやつを作ってみたいと思います。
また続きが書けたら追記します(*^_^*)
追記:続きを書きました!






