手に入れたアイテム、画面に出ている敵、クリアしたステージ・・・。ゲームには「いくつになるか最初は分からない」データがたくさんあります。6-5の記事で紹介した配列は、最初に決めた数から増やしたり減らしたり出来ません。今回は、あとから自由に増やしたり減らしたり出来るList(リスト)をご紹介します!
目次
この記事で学べること
- 配列とListの違い
- Listの作り方と、足す・消す・数える・取り出すの基本
- foreach文とfor文でListを順番に処理する方法
- 自分で作ったクラスをListに入れる方法(Inspectorで編集もできる)
- JsonUtilityで保存するときの注意(Dictionaryは保存できない)
前提知識
- 変数でデータを保存しよう
- 条件分岐と繰り返し処理の基本:配列とforeach文
- メソッドを作ろう:処理に名前を付けてまとめる
配列とListの違い
| 配列(6-5の記事) | List | |
|---|---|---|
| 書き方 | string[] names | List<string> names |
| 数 | 作るときに決めて、あとから変えられない | あとから自由に増やしたり減らしたり出来る |
| 数を調べる | names.Length | names.Count |
| 使いどころ | ステージ数、曜日など、数が決まっているもの | 持ち物、出ている敵、ログなど、数が変わるもの |
「数が決まっているなら配列、変わるならList」と覚えておけばOKです。
Listの基本
Listを作る
using System.Collections.Generic;
using UnityEngine;
// List の基本(足す・消す・数える・取り出す)
public class ListBasics : MonoBehaviour
{
private List<string> items = new List<string>();
private void Start()
{
items.Add("やくそう"); // 最後に足す
items.Add("どくけしそう");
items.Add("やくそう");
Debug.Log($"持ち物は{items.Count}個"); // 3
Debug.Log($"1番目は{items[0]}"); // やくそう(番号は0から)
bool hasAntidote = items.Contains("どくけしそう"); // 入っているか
Debug.Log($"どくけしそうを持っている:{hasAntidote}"); // True
items.Remove("やくそう"); // 最初に見つかった1つを消す
Debug.Log($"やくそうを使った。持ち物は{items.Count}個"); // 2
items.RemoveAt(0); // 0番目を消す
items.Clear(); // 全部消す
Debug.Log($"全部なくなった。持ち物は{items.Count}個"); // 0
}
}
- using System.Collections.Generic;
- ListはC#の機能なので、一番上にこの1行が必要
- List
< >の中に、入れるものの型を書く。List<int>なら整数のリスト、List<GameObject>ならGameObjectのリスト
- new List
() - 空のリストを作る。newを忘れると、使ったときにNullReferenceExceptionになる
| 書き方 | 意味 |
|---|---|
Add(値) | 最後に足す |
Insert(番号, 値) | 番号の位置に割り込ませる |
Remove(値) | 最初に見つかった1つを消す(見つかればtrue) |
RemoveAt(番号) | 番号の位置のものを消す |
Clear() | 全部消す |
Count | 入っている数 |
リスト[番号] | 番号の位置のものを取り出す・書き換える(0から) |
Contains(値) | 入っていればtrue |
IndexOf(値) | 何番目にあるか(なければ-1) |
順番に取り出す(foreach・for)
配列と同じように、foreach文で全部を順番に取り出せます。
foreach (string item in items)
{
Debug.Log(item);
}
番号も使いたいときは、for文でCountまで回します。
for (int i = 0; i < items.Count; i++)
{
Debug.Log($"{i}番目:{items[i]}");
}
スライム配列はLength、ListはCount。ここだけ名前が違うので、よく間違えます・・・
自分で作ったクラスをListに入れる
「アイテムの名前」と「持っている数」のように、1つの項目に情報がいくつかあるときは、クラスを作ってListに入れます。
using System;
using System.Collections.Generic;
using UnityEngine;
// 持ち物の一覧。「アイテムの名前」と「持っている数」を1組にして List に入れる
public class Inventory : MonoBehaviour
{
[Serializable]
public class Slot
{
public string itemName; // アイテムの名前
public int count; // 持っている数
}
[SerializeField] private List<Slot> slots = new List<Slot>();
private void Start()
{
AddItem("やくそう", 3);
AddItem("どくけしそう", 1);
AddItem("やくそう", 2); // すでに持っているので、数が増える
UseItem("どくけしそう"); // 0個になったら一覧から消える
foreach (Slot slot in slots)
{
Debug.Log($"{slot.itemName}:{slot.count}個"); // やくそう:5個
}
}
public void AddItem(string itemName, int amount)
{
Slot slot = FindSlot(itemName);
if (slot == null)
{
// まだ持っていないアイテムなら、新しく足す
slot = new Slot { itemName = itemName, count = 0 };
slots.Add(slot);
}
slot.count += amount;
}
public bool UseItem(string itemName)
{
Slot slot = FindSlot(itemName);
if (slot == null)
{
return false; // 持っていない
}
slot.count--;
if (slot.count <= 0)
{
slots.Remove(slot); // 0個になったら一覧から消す
}
return true;
}
// 名前が同じ Slot を探す(なければ null)
private Slot FindSlot(string itemName)
{
foreach (Slot slot in slots)
{
if (slot.itemName == itemName)
{
return slot;
}
}
return null;
}
}
- [Serializable]を付けたクラス
[SerializeField] private List<Slot>にすると、Inspectorで中身が見える・編集できる- JsonUtilityで保存するときにも必要
- FindSlot
- foreachで1つずつ見て、名前が同じものを返す。見つからなければnull
- AddItem
- 持っていなければ新しいSlotを足し、持っていれば数を増やす
- UseItem
- 数を減らして、0個になったらRemoveで一覧から消す
InspectorのListは、右下の「+」「−」で項目を増やしたり減らしたりも出来ます。最初から持たせておくアイテムを、Inspectorで設定しておくことも出来ます。
JsonUtilityで保存するときの注意
12-6 データを保存しようで紹介したJsonUtilityは、Listを中身ごと保存できます。
[Serializable]
public class SaveData
{
public List<Inventory.Slot> slots = new List<Inventory.Slot>();
}
{
"slots": [
{ "itemName": "やくそう", "count": 5 }
]
}
C#には「名前から値を引ける」Dictionaryという便利なものもありますが、JsonUtilityはDictionaryを保存できません。保存したいときは、今回のSlotのように「名前と数を1組にしたクラス」のListにしておくのがオススメです。



「名前と数のセットをListに入れる」形は、持ち物・ショップ・図鑑など、いろんなところで使えるよ!
よくあるエラーと対処法
エラー:ArgumentOutOfRangeException: Index was out of range.
- 原因:
items[3]のように、入っている数より大きい番号を指定した(3個なら最後は[2]) - 対処法:番号は0から
Count - 1まで。for文はi < items.Countにする
エラー:NullReferenceException
- 原因:
new List<string>()を書き忘れて、Listが作られていない - 対処法:変数を作るときに
= new List<string>()を付ける
エラー:InvalidOperationException: Collection was modified; enumeration operation may not execute.
- 原因:foreach文で回している途中に、同じListでAddやRemoveをした
- 対処法:消したいものを探すだけにして、foreach文が終わってから消す。または、for文で後ろから回して
RemoveAtする
// 0個のものを全部消す(後ろから回すと、消しても番号がずれない)
for (int i = slots.Count - 1; i >= 0; i--)
{
if (slots[i].count <= 0)
{
slots.RemoveAt(i);
}
}
エラー:The type or namespace name ‘List<>’ could not be found
- 原因:
using System.Collections.Generic;を書いていない - 対処法:スクリプトの一番上に足す
まとめ
- 数が決まっているなら配列、あとから増えたり減ったりするならList
using System.Collections.Generic;とnew List<型>()を忘れずにAddで足す、Remove・RemoveAtで消す、Countで数える、[番号]で取り出す- 1つの項目に情報がいくつかあるときは、
[Serializable]のクラスを作ってListに入れる - JsonUtilityはListを保存できるが、Dictionaryは保存できない
次のステップ
- Listで持ち物を管理して、ショップで買ったアイテムを保存する回:ペット育成ゲームの作り方#7
- Unityスタートガイド 目次
コメント