代理类可以包含对原始类的引用

时间:2014-09-24 15:00:22

标签: c# serialization unity3d protocol-buffers protobuf-net

我试图为名为Transform的第三方类创建代理。我需要序列化它的一些公共成员,包括对另一个这样的类实例的一些引用,并公开Transforms的内部列表。所以我为它写了代理,但我不确定是否可以在[ProtoMember]类型的代理字段中定义Transform属性。或者他们应该TransformSurrogate?现在我的代码是:

[ProtoContract(AsReferenceDefault = true)]
public class TransformSurrogate {
    [ProtoMember(1)]
    Vector3 localPosition { get; set; }
    [ProtoMember(2)]
    Vector3 localScale { get; set; }
    [ProtoMember(3)]
    Quaternion localRotation { get; set; }
    [ProtoMember(4, AsReference = true)]
    Transform parent { get; set; }
    [ProtoMember(5, AsReference = true)]
    List<Transform> children { get; set; }

    public static explicit operator TransformSurrogate(Transform transform) {
        if (transform == null) return null;
        var surrogate = new TransformSurrogate();
        surrogate.localPosition = transform.localPosition;
        surrogate.localRotation = transform.localRotation;
        surrogate.localScale = transform.localScale;
        surrogate.parent = transform.parent;
        surrogate.children = new List<Transform>();
        for (int i = 0; i < transform.childCount; ++i) {
            surrogate.children.Add(transform.GetChild(i));
        }
        return surrogate;
    }

    public static explicit operator Transform(TransformSurrogate surrogate) {
        if (surrogate == null) return null;
        var transform = new GameObject().transform;
        transform.localPosition = surrogate.localPosition;
        transform.localRotation = surrogate.localRotation;
        transform.localScale = surrogate.localScale;
        transform.parent = (Transform) surrogate.parent;
        foreach (var child in surrogate.children) {
            child.parent = transform;
        }
        return transform;
    }
}

不要过多关注Vector3Quaternion类 - 它们很容易被序列化。所以我为RuntimeTypeModel类型定义了我自己的Transform,如下所示:

Model.Add(typeof(Transform), false).SetSurrogate(typeof(TransformSurrogate));

然而,我在反序列化过程中遇到错误,告诉我Protobuf无法在类之间进行转换。我认为这是因为在代理班内混合原创班级,但我不确定。

1 个答案:

答案 0 :(得分:1)

对于某些数据类型是的,它会起作用,我现在正在做同样的事情(编写Unity3D变换的代理)。早些时候,我在MeshSurrogate上测试过。以下是代码的启动方式......

[ProtoContract(AsReferenceDefault = true)]
    [ProtoSurrogate(typeof(Mesh))]
    sealed class MeshSurrogate {
        [ProtoMember(1)]
        Matrix4x4[] bindposes;

        [ProtoMember(2)]
        BoneWeight[] boneWeights;

/* Lots more follows */

(ProtoSurrogateAttribute是自定义的...它可以满足您的需求) 它可以很好地序列化和反序列化所有数据类型的原始非代理类型的字段。

变换变得更有趣。这是一个潜在的问题 - 据报道AsReference不能与代理人合作 - https://code.google.com/p/protobuf-net/issues/detail?id=352太糟糕了,因为这是我们的Unity序列化策略的核心。

如果我开始工作,我会更新。

<强>更新 事实证明这很复杂。

我已经解决的问题是,基本上任何继承UnityEngine.Object的东西都会获得一个包含3个且只有3个成员的代理 - 一个instanceIdBeforeSerialization(通过UnityEngine.Object.GetInstanceID()获得),一个&#34 ;壳&#34;和#34;数据&#34;。如果在此序列化期间首次遇到此代理项,则仅填充shell和数据,否则它们为null。 Shell负责实例化对象,以及用于在[OnDeserialized]方法中填充它们的数据。当shell创建一个对象时,它将它存储在一个表中,该表具有从它的旧实例id映射到它的新实例id,因此当任何代理被转换为原始类型时,它只需要搜索因为它是表格中的新对象。因为shell早先放在流上,所以在需要在对象图中引用它们之前,所有对象都至少被初始化。

不幸的是,我无法让Data类直接存储对Unity对象的引用,但这是一个库,所以最大的便利是用户而不是作者。

现在已经解决了问题,看起来这种方法可以很好地运作。我会看看我的雇主是否允许它去资产店。

<强>更新 这将在资产商店结束时进行。首先要添加很多代理,这可能需要一段时间。