什么是序列化
-
unity的序列化在unity的開發中起着舉重足輕的地位,許多核心的功能都是基於序列化和反序列化來實現的。序列化簡單來講就是就是將我們所要保存的數據進行二進制存儲,然后當我們需要的時候,在讀取二進制文件,反序列化回來。下面是一些常用的序列化的例子:
- 存儲腳本化的數據。在我們的c#代碼中,可以將我們所要存儲的數據進行序列化,進行存儲
- prefab與初始化。在unity開發過程中我們會制作很多的預制體(prefab),這些prefab都是序列化,以二進制的形式保存的。當你要對預制體進行初始化的時候,unity根據之前序列化的信息新建出一個物體,然后將物體上的信息反序列化回去,實現類似於clone的效果。
- 在unity編輯器的拓展功能,AssetBundle等等,都可以看到序列化的身影。
-
可序列化對象
- 公有的,或者有[SerializeField]屬性,非靜態,非常量,非只讀
- 自定義非抽象類,並且有Serializable屬性
- 繼承自unity.object的類
- 數組和List
注意:
- 字典不能通過添加Serializable屬性進行序列化
- 如果一個類基類不能被序列化,那它即便添加了序列化特性也無法被序列化
- 序列化不能保存另一個要反序列化的對象指針,因為反序列化是new一個新的對象,指針指向的內存將不會是原對象
-
可采用unity編輯器的序列化類ScriptableObject,當我們繼承這個基類,我們就可以調用unity給我們的接口進行序列化了。具體的序列化接口可以到unity的官方接口文檔上查看使用方法,這里不具體介紹。
-
下面來講講具體開發過程中個人遇到的坑
- 首先給出一段開發過程中的報錯信息
Serialization depth limit 7 exceeded at 'XNodeScripts::ConfigNode.nodeList'. There may be an object composition cycle in one or more of your serialized classes. Serialization hierarchy: 8: XNodeScripts::ConfigNode.nodeList 7: XNodeScripts::ConfigNode.nodeList 6: XNodeScripts::ConfigNode.nodeList 5: XNodeScripts::ConfigNode.nodeList 4: XNodeScripts::ConfigNode.nodeList 3: XNodeScripts::ConfigNode.nodeList 2: XNodeScripts::ConfigNode.nodeList 1: XNodeScripts::ConfigNode.nodeList 0: XNodeScripts::SelectMsgNode.rootNode
這里的報錯信息提示我們我在創建序列化數據nodeList的時候,可能是在這個nodeList里面有循環的序列化。這邊我們發現unity還提示我們序列化的深度為7。我在網上查到,原來我們的子系統是構建在序列化系統之上的,所以如果放任其死循環的序列化的話,當一個非常大的序列化流將導致整個系統崩潰。為了避免這樣的情況,unity采用了一個深度為7的闕值,及循環序列化為空的子集最多只能為7個。我們在進行序列化的創建的時候,一個非常常見的錯誤就是通過序列化構造一個類似樹狀結構的數據結構,在我們聲明的序列化類里面,如果還有包含本身的對象,類似於
[Serializable] public class DepthClass : ScriptableObject { public List<DepthClass> depthObjects; }
上訴的這種序列化結構,就會產生我之前所示的系統報錯。有效的避免空值的序列化,對於樹狀結構包括字典的存儲,我采用的是List
key的存儲方式,對下一個子節點,都用字符串作為一個key值來存儲,然后將所有的節點都存儲在一個List中。這樣我們通過key來尋找節點,其中我們可以采用堆存儲或者更加高效的查詢方式,來避免效率的底下。目前還沒有想到更好的解決方法。。。23333