【UnityJsonUtility】(クラスを配列にした場合)オブジェクトのデータを保存する方法

2023年1月29日

UnityのJsonUtilityでは、クラスの配列を保存することができません
今回は、ラッパーという方法でこの問題をクリアする手順を見ていきます

サンプル

参考(配列ではない場合)

シリアライズ化された単純なクラスの保存・読み出しは次のリンクで実現が可能です

では、このクラスの配列を保存したい場合はどのようにすればよいでしょうか

例えば、自分で持っているアイテム一覧を考えてみましょう
りんご、みかん、バナナを持っていて、それぞれ値段、個数の情報がある場合ですね
C#では、フルーツ型のクラスを作成し、りんご、みかん、バナナのインスタンスを作成することで実現します
このフルーツ型のクラスはメンバーとして値段と個数を持たせるでしょう

サンプルコード

では早速、次のような例を考えてみましょう
商品名、賞味期限、大きさ(複数)をメンバーとしています

  • インスペクタに表示したいので、クラスの宣言で、[SerializeField]属性をつけています
  • クラスから作られるインスタンスを文字列として保存したいので、シリアライズ化しておきます 。[Serializable]属性をつけておきます
// 商品のインスタンスをインスペクターに表示させます。productは、Product型のフィールドになります。
[SerializeField]
Product product;

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

    // 賞味期限
    public string Expiry;

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

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

参考

では、この基本クラスを配列にしたものはどうでしょうか?

失敗するコード

次のコードを実行すると、productJsonには、{}が代入されます。

うまくできていないようです。

using System;
using UnityEngine;

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

    // 賞味期限
    public string Expiry;

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

public class ArrayTest : MonoBehaviour
{
    [SerializeField]
    Product[] products;

    void Start()
    {
        var productJson = JsonUtility.ToJson(products, true);
    }
}

成功するコード

直接、配列をJsonフォーマットへの試みるのではなく、一旦、その配列をメンバーとするクラスを作成して、そのインスタンスをJsonフォーマットにします。
これを、ラッパー(ラップですね。食品を包み込むイメージです)と言います。

シーン構成

  • スクリプト名をArrayTestとしてC#ファイルを作成します
  • 新しくGameObjectをs作成し、ArrayTestスクリプトをアタッチします
  • インスペクターでデータを3つほど入力します

実行後では、次のコードにより、データが変更されています

productsWrap.products[1].Name = "キャベツ";

コード

using System;
using UnityEngine;

/// <summary>
/// ラッパー(包む、食品を包むラップのようなもの)
/// 配列は、UnityのJsonUtirityで直接管理できないため、配列をクラスのメンバーとして使います。
/// </summary>
[Serializable]
class ProductWrapper
{
    // 商品のインスタンス(配列)をインスペクターに表示させます。productは、Product型のフィールドになります。
    public Product[] products;
}

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

    // 賞味期限
    public string Expiry;

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


public class ArrayTest : MonoBehaviour
{
    [SerializeField]
    ProductWrapper productsWrap;

    void Start()
    {
        // データの更新は次のようにできます。
        productsWrap.products[1].Name = "キャベツ";

        var productsArray = JsonUtility.ToJson(productsWrap, true);

        Debug.Log(productsArray);

        // 保存処理をコーディング(略)PlayerPrefsなど・・

    }
}

(参考)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}");
            }
        }
        // 保存処理をコーディング(略)PlayerPrefsなど・・
    }
}

Unity

Posted by hidepon