深度序列化的一种解决方案

说明:在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



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值