コガネブログ

平日更新を目標に Unity や C#、Visual Studio、ReSharper などのゲーム開発アレコレを書いていきます

【Unity】ScriptableObject の private 変数に設定した値は Unity エディタの再生を停止しても保持されてしまう

検証環境

  • Unity 2018.4.8f1

概要

using UnityEngine;

public class Example : ScriptableObject
{
    private string[] m_list;

    public string[] List
    {
        get
        {
            // はじめてアクセスされたら配列を作成する
            if ( m_list == null || m_list.Length <= 0 )
            {
                m_list = new[]
                {
                    "フシギダネ",
                    "フシギソウ",
                    "フシギバナ",
                };
            }

            return m_list;
        }
    }
}

例えば上記のように ScriptableObject に private な配列を定義して
プロパティにはじめてアクセスされた時に配列を作成するようにして

using UnityEngine;

public class TestScene : MonoBehaviour
{
    public Example m_example;

    private void Awake()
    {
        var list = m_example.List;
    }
}

他のクラスからそのプロパティにアクセスした場合、
Unity 再生中のみ配列の値が保持されると思っていたのですが、

f:id:baba_s:20190927173601p:plain

Unity の再生を停止して、Inspector を Debug モードで見てみると
配列に設定した値が保持されていました

using UnityEngine;

public class Example : ScriptableObject
{
    private string[] m_list;

    public string[] List
    {
        get
        {
            // 配列の値が保持されてしまっているため
            // null や空配列のチェックで if 文の中に入らない
            if ( m_list == null || m_list.Length <= 0 )
            {
                m_list = new[]
                {
                    // そのため、配列に設定する値を変更しても
                    // ここの処理には絶対に入ってこない
                    "ヒトカゲ",
                    "リザード",
                    "リザードン",
                };
            }

            // そして、すでに保持されてしまっている値が返される
            return m_list;
        }
    }
}

そのため、上記のようにプロパティにはじめてアクセスされた時に
配列に設定する値を変えても、すでに前回設定した値が保持されてしまっているため、
その古い値が常に返却されてしまいます

(Unity エディタを起動し直すと、保持されている値は破棄されます)

対策

[NonSerialized] private string[] m_list;

値を保持してほしくない private 変数に NonSerialized 属性を適用すると

f:id:baba_s:20190927174536p:plain

シリアライズされなくなるため、Unity の再生中以外は値が保持されなくなります