深度序列化的一種解決方案

說明:在unity3d中自己定義的scriptobject中使用了一個可以序列化的類,希望可以在界面上顯示,但這個類的一個樹型結構,其中包含了同類型的列表。這種情況下,利用unity自己的序列化方式雖然能夠用,但官方強烈不讓這樣用,還直接報紅:Serialization depth limit exceeded at '‘. There may be an object composition cycle in one or more of your serialized classes.,爲些想了不少辦法,但最終還是需要利用unity自身的序列化方式,不然後不能在界面上顯示,也就沒有達到可視化的效果。

一、問題來源

1.源碼

using System.Collections.Generic;

[System.Serializable]
public class Person {
    public string name;
    public List<Person> childs;
}
[CreateAssetMenu(menuName = "創建FamilyObj")]
public class FamilyObj : ScriptableObject {
    public Person personRoot;

2.問題說明

以上分別是對象模型和一個ScriptObject的腳本,此時,一創建出一個FamilyObj,編輯器就會報紅。主要是因爲這種序列化的方案不運行空的列表,一但開始序列化,理論會無限創建對象。而unity對這種方案支持的效果不會超過7級,如下圖所示:




二、解決方案

1.方案來源

由於這個問題網上十分普遍,但是並沒有找到能夠滿足需求的方案,大多是告訴你怎麼不讓報錯。但並沒有說怎麼還能顯示在界面上的同時不報錯,

這是官方說明怎麼序列化非常規的類型如字典:https://docs.unity3d.com/ScriptReference/ISerializationCallbackReceiver.html

這是官方說明怎麼自己定義序列化:https://docs.unity3d.com/Manual/script-Serialization.html

以上的兩個貌似對我也沒有什麼啓發,也沒有實現要在界面上顯示多層級的樹型結構。

2.利用繼承和自定義層級的方案

這裏說一個我程序內的解決方案:

using System.Collections.Generic;

public abstract class Person {
    public string name;
    public abstract List<Person> childs { get; set; }

    [System.Serializable]
    public class P1 : Person
    {
        public List<P2> _childs;

        public override List<Person> childs
        {
            get
            {
                return _childs.ConvertAll<Person>(x=>x);
            }

            set
            {
                _childs = value.ConvertAll(x => x as P2);
            }
        }
    }

    [System.Serializable]
    public class P2 : Person
    {
        public List<P3> _childs;
        public override List<Person> childs
        {
            get
            {
                return _childs.ConvertAll<Person>(x => x);
            }

            set
            {
                _childs = value.ConvertAll(x => x as P3);
            }
        }
    }
    [System.Serializable]
    public class P3 : Person
    {
        public override List<Person> childs
        {
            get
            {
                Debug.Log("不孕不育");
                return null;
            }

            set
            {
                Debug.Log("不孕不育");
            }
        }
    }
}
[CreateAssetMenu(menuName = "創建FamilyObj")]
public class FamilyObj : ScriptableObject {
    public Person.P1 personRoot;
}

以上爲修改後的類,因爲並沒有引用自己同類型的對象,而且最後一級也已經指定了空,所以程序也並不會報錯。由於其他子類都繼承於一個抽象類,其他業務邏輯並不會發生改變,主要是創建的時候,需要指定是具體的那個類。此時最好寫個對象創建的功能,但並不在本文的討論範圍內。

三、相關文章

雖然,目前已經不會報錯了,但由於程序的擴展性問題,這種方案其實還是個問題。一般情況下子層級是有限的,好辦到是好辦,多加一級就要寫不少重複的代碼。希望可以遇到更好的解決方案。

下面是一些相關的網站:

Custom serialization:docs.unity3d.com/Manual/script-Serialization-Custom.html

Script serialization errors:https://docs.unity3d.com/Manual/script-Serialization-Errors.html



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章