箱庭ハーブblog

7年目プログラマの趣味の小部屋

完全攻略、Unityシリアライゼーション

UNITY、Unity、unity
SERIALIZABLE、Serializable、serializable
SCRIPTABLEOBJECT、ScriptableObject、scriptableobject

2017/03/13
ScriptableObjectに関して、勘違いがあったため補足。

2017/04/22
全体的に記事を修正中(やりかけ)

 
使い方を間違えなければ、非常に強力です。
Unityのシリアライズは非常に便利なのですが、使い方を間違えると不具合地獄に落ちます。

ーー

この門をくぐる者は一切の高望みを捨てよ

ーー

まさにそんな言葉が頭によぎるレベルですね。

基本的な内容は、下記の2つの公式サイトを読むのが早いと思いますが、
詳細に関してはプログラマでも文章だけではよくわからない。


Unityのシリアライゼーション
http://japan.unity3d.com/blog/?p=1630

スクリプトシリアライゼーション
https://docs.unity3d.com/ja/current/Manual/script-Serialization.html


実際に試した方が早いということで、冒頭のスクリーンショットのようになりました。

実際にやったこと



S1.cs
using System;
using System.Collections.Generic;
using UnityEngine;

namespace Assets.UnlimitedFairyTalesHowTo.UnityAndCSharp.SerializeSample.Parts
{
    // S1 is not UnityEngine.Object!
    [Serializable]
    public class S1
    {
        // PARAMETER / STATUS

        int _num1_private; // private member don't show on editor and don't serialize.

        [SerializeField]
        int _num2_private_serializable;

        public int _num3_public;

        [NonSerialized]
        public int _num4_public_NonSerialized;

        public int Num5_property { get; set; }

        public List _numList; // Array and List are serializable.

        // PROPERTY & FIELD / METHOD

        public void CountUp()
        {
            _num1_private++;
            _num2_private_serializable++;
            _num3_public++;
            _num4_public_NonSerialized++;
            Num5_property++;
        }

        public string GetText()
        {
            return
                "num1(private) = " + _num1_private + "\n" +
                "num2(SerializeField) = " + _num2_private_serializable + "\n" +
                "num3(public) = " + _num3_public + "\n" +
                "num4(NonSerialized) = " + _num4_public_NonSerialized + "\n" +
                "num5(Auto Property) = " + Num5_property;
        }
    }
}


MyBehaviour.cs
using UnityEngine;

namespace Assets.UnlimitedFairyTalesHowTo.UnityAndCSharp.SerializeSample.Parts
{
    public class MyBehaviour : MonoBehaviour
    {
        // PARAMETER / STATUS
#pragma warning disable 649 // SerializedField's fileds or public fields are initialized by Unity.

        public S1 _s1_public;

        S1 _s1_private; // MonoBehaviour's private fields is not initialized!

#pragma warning restore 649
        // PROPERTY & FIELD / METHOD

        // Use this for initialization
        void Start()
        {

        }

        // Update is called once per frame
        void Update()
        {

        }

        public void AllCountUp()
        {
            _s1_public.CountUp();
            if (_s1_private != null) {
                _s1_private.CountUp();
            }
        }

        public string AllGetText()
        {
            var a = "MyBehaviour's" + "\n"
                + "s1_public" + "\n"
                + _s1_public.GetText() + "\n"
                + "s1_private" + "\n";
            if (_s1_private == null) {
                a += "null";
            }
            else {
                a += _s1_private.GetText();
            }
            return a;
        }
    }
}

MyScriptableObject.cs
using UnityEngine;

namespace Assets.UnlimitedFairyTalesHowTo.UnityAndCSharp.SerializeSample.Parts
{
    [CreateAssetMenu(menuName = "UnlimitedFairyTales/HowTo/SerializeSamples/MyScriptableObject")]
    public class MyScriptableObject : ScriptableObject
    {
        // PARAMETER / STATUS
#pragma warning disable 649 // SerializedField's fileds or public fields are initialized by Unity.

        public S1 _s1_public; // ScriptableObject's field seems to be saving value on editor, but It is not actually saved.

        S1 _s1_private;

#pragma warning restore 649

        // PROPERTY & FIELD / METHOD

        public void AllCountUp()
        {
            _s1_public.CountUp();
            _s1_private.CountUp();
        }

        public string AllGetText()
        {
            return "MyScriptableObject's" + "\n"
                + "s1_public" + "\n"
                + _s1_public.GetText() + "\n"
                + "s1_private" + "\n"
                + _s1_private.GetText();
        }
    }
}


AssetUserBehaviour.cs
using UnityEngine;

namespace Assets.UnlimitedFairyTalesHowTo.UnityAndCSharp.SerializeSample.Parts
{
    public class AssetUserBehaviour : MonoBehaviour
    {
        public GameObject _myPrefab;
        public MyScriptableObject _myScriptableObject;

        // PARAMETER / STATUS

        public S1 _s1;

        GameObject _instantiated;
        MyBehaviour _instantiatedBehaviour;

        // PROPERTY & FIELD / METHOD

        // Use this for initialization
        void Start()
        {
            _instantiated = Instantiate(_myPrefab);
            _instantiatedBehaviour = _instantiated.GetComponent();
        }

        // Update is called once per frame
        void Update()
        {
            if (Input.GetKeyDown(KeyCode.Space)) {
                _instantiatedBehaviour.AllCountUp();
                _myScriptableObject.AllCountUp();
                _s1.CountUp();
            }
            var canvasText = this.GetComponentInChildren();
            canvasText.text =
                "instantiated" + "\n"
                + _instantiatedBehaviour.AllGetText() + "\n"
                + "\n"
                + "myScriptableObject" + "\n"
                + _myScriptableObject.AllGetText() + "\n"
                + "\n"
                + "S1" + "\n"
                + _s1.GetText();
        }
    }
}



【操作】
1、実行し、5回キーを叩く
2、一度止め、再度実行し、5回キーを叩く
3、一度止め、MyScriptableObjectのインスペクタからResetを実行し、5回キーを叩く
4、一度止め、再度実行し、5回キーを叩く

結果からわかること

1、MonoBehaiviour上の非公開メンバは、newで初期化されない
(myMonoBehaviourのs1_privateがnullのまま)

2、ScriptableObjectのAssetを使用する際は、初期化済み。Asset初回使用=Deserializeなため
(myScriptableObjectのs1_privateが初期化済み)

3、ScriptableObjectの状態は、privateでも保存される
(myScriptableObjectのprivateメンバが20回叩いたことになっている)

2017/03/13
privateメンバは20になり一見記憶されているように見えるが、
実際のところシリアライズ対象でないため、ファイルとして保存されない
エディタを起動しなおすと、リセットされている

3-1、NonSerializedは、保存されない

3-2、privateは、そのままではReset対象外である

3-3、自動プロパティは、privateと同じようにエディタ上は見えないが保存される

2017/03/13
privateメンバと同様一見保存されているように見えるが、
シリアライズ対象でないため、ファイルとして保存されない
エディタを起動しなおすと、リセットされている

4、MonoBehaviourのメンバであるS1は、保存されない
(Instantiateされたものを使用しているので、元Prefabと直接関係ない)

個々を見ると謎めいた動きですが、
全ての結果を並べると、動作自体は非常に単純な仕様であることが分かります。

・ScriptableObjectは参照。共有される。保存される
・Editor上に表示されていないメンバは、そのままではResetされない
・Instantiateなどで参照が維持されるのは、Unityオブジェクトのみ
 (今回の調査対象外ですが、このような前提が存在しています)

なお、ScriptableObjectを複製したい場合は、
Prefabと同じようにInstantiateすると良いです。

また、ScriptableObjectAssetに値を保存したくない場合
(パラメータではなくステータス)は、privateにするか、
NonSerialized属性を付加すればよいです。
コメント
PAGETOPへ

コメント送信

お名前
タイトル
文字色
メールアドレス
URL
コメント
パスワード

パスワードを入れておかないと、コメントの再編集ができません!

フリーエリア

takemori
Twitter : @takemori_kondo

1. Unityと戯れてます
2. Cake3は劣化じゃないRails

iOS
coming soon...

Windows
Html Editor - Nazuna
Managed DirectX サンプル集

beginning since
2006.08.17
renewaled on
2011.06.03

最新コメント

[2013/06/14 ミューネ]
[2012/08/30 ノートPC]