Unity シリアライズ化されたインスタンスを配列として扱う

2023年1月27日

シリアライズ化されたインスタンス配列をインスペクターから確認、登録する方法について説明します。

まず、次のリンクにあるインスタンスのシリアライズについて、学習しておいて下さい。

基本となるクラス

次のクラスを前提にインスタンスの配列を作成してみましょう。

class Product
{
    // 商品名
    public string Name;

    // 賞味期限
    public string Expiry;

    // 大きさの種類
    public string[] Sizes;
}

クラスのシリアライズ化

インスペクターで登録できるようにシリアライズ化します。

// 商品のインスタンスをインスペクターに表示させます。
[SerializeField]
Product product;

// 商品クラスをシリアライズ
[Serializable]
class Product
{
    // 商品名
    public string Name;

    // 賞味期限
    public string Expiry;

    // 大きさの種類
    public string[] Sizes;
}

作成したクラスを配列として扱う

基本となるクラスを変更して、配列で管理するようにします。

上記では、

Product product;

と、宣言しているところをProduct型の配列に変更します。
名前は、sをつけ、productsとしています。

Product[] products;

配列をシリアライズ化したコード

変更したクラスが次のようになります。

宣言を配列型にしています!

using System;
using UnityEngine;

public class SirializeClass : MonoBehaviour
{
    // 商品のインスタンスをインスペクターに表示させます。productsは、Product型の配列になります。
    [SerializeField]
    Product[] products;

    // 商品クラスをシリアライズ
    [Serializable]
    class Product
    {
        // 商品名
        public string Name;

        // 賞味期限
        public string Expiry;

        // 大きさの種類
        public string[] Sizes;
    }
}

インスペクターから、初期値を代入する

ここまでコードを入力したら、このスクリプトを空のゲームオブジェクトにアタッチしましょう。

スクリプトのアタッチ

データの初期値代入

では、この配列のインスタンスに値を代入していきます。
代入は、コードではなくインスペクターから入力する方法を試していきます。
もちろん、コードで代入していく方法でもできます。

2つ目のインスタンスの追加

同じように牛肉を追加しましょう

  • Name 牛肉
  • Expiry2100/01/10
  • Size 100g, 300g, 500g

確認するコード

for文を使って確認

それでは、インスタンスの情報を取り出すコードを書いてみましょう。
まず、シンプルに記述してみましょう。

コード抜粋

void Start()
{
    // 全てのインスタンスを表示
    for (int i = 0; i < products.Length; i++)
    {
        Debug.Log($"{products[i].Name} {products[i].Expiry}");
    }
}

size(サイズ)の表示がありませんね。sizeは、クラスのメンバーです。型の宣言はstring型の配列です。
なので、確認するには、ネスト(入れ子)されたfor文を使うとうまくいきそうです。

ネストの内側は、sizeの取得になります

コード抜粋(ネスト部分)

// インスタンスごとのサイズを全て表示
for (int j = 0; j < products[i].Sizes.Length; j++)
{
    Debug.Log($"{products[商品の要素番号].Sizes[j]}");
}

コード抜粋(ネストのコードをまとめると・・・)

void Start()
{
    // 全てのインスタンスを表示
    for (int i = 0; i < products.Length; i++)
    {
        Debug.Log($"{products[i].Name} {products[i].Expiry}");

        // インスタンスごとのサイズを全て表示
        for (int j = 0; j < products[i].Sizes.Length; j++)
        {
            Debug.Log($"{products[i].Sizes[j]}");
        }
    }
}

動作するスクリプト

今回のサンプルでは、Productクラスは、インナークラスを使ってSirializeClassクラスに内包するようにしています。
実際のコードでは、分離しても構いません。

全コード

using System;
using UnityEngine;

public class SirializeClass : MonoBehaviour
{
    [SerializeField]
    // 商品のインスタンス(配列)をインスペクターに表示させます。productは、Product型のフィールドになります。
    Product[] products;

    [Serializable]
    // 商品クラスをシリアライズ
    class Product
    {
        // 商品名
        public string Name;

        // 賞味期限
        public string Expiry;

        // 大きさの種類
        public string[] Sizes;
    }

    void Start()
    {
        // 全てのインスタンスを表示
        for (int i = 0; i < products.Length; i++)
        {
            Debug.Log($"{products[i].Name} {products[i].Expiry}");

            // インスタンスごとのサイズを全て表示
            for (int j = 0; j < products[i].Sizes.Length; j++)
            {
                Debug.Log($"{products[i].Sizes[j]}");
            }
        }
    }
}

実行結果

(参考)foreachで確認する場合

using System;
using UnityEngine;

public class SirializeClass : MonoBehaviour
{
    [SerializeField]
    // 商品のインスタンス(配列)をインスペクターに表示させます。productは、Product型のフィールドになります。
    Product[] products;

    [Serializable]
    // 商品クラスをシリアライズ
    class Product
    {
        // 商品名
        public string Name;

        // 賞味期限
        public string Expiry;

        // 大きさの種類
        public string[] Sizes;
    }

    void Start()
    {
        // 全てのインスタンスを表示
        foreach (Product product in products)
        {
            Debug.Log($"{product.Name} {product.Expiry}");

            // インスタンスごとのサイズを全て表示
            foreach (string size in product.Sizes)
            {
                Debug.Log($”{size}");
            }
        }
    }
}

(応用)インスタンスの特定の情報を取得

C# LINQを使って、データの中から情報を抽出することもできます。

// products配列で、最初に見つかった商品名が牛肉のインスタンスの賞味期限を取得
// 見つからなければ、デフォルト値(stringの場合null)を返します
var expiry = products.FirstOrDefault(product => product.Name == "牛肉").Expiry;

Unity

Posted by hidepon