尝试使用带有预分配对象的protobuf-net

时间:2014-08-12 21:37:03

标签: c# ios unity3d protobuf-net

TLDR;我试图序列化/反序列化对象图预分配的对象图的所有成员值

[ProtoContract]
class Node {
    [ProtoMember(1)] int data;
    [ProtoMember(2)] string moreData;
    [ProtoMember(3)] Node otherNodeInSameGraph;
    LargeData thisComesFromAnotherSourceAndIsPreallocated;
}

[ProtoContract]
class Graph {
    [ProtoMember(1)] List<Node> allNodesInGraph;
}

我的目标是能够保存图表并加载图表,其中所有节点都已分配(为了处理Node.thisComesFromAnotherSourceAndIsPreallocated)。


完整版(对不起文字墙):

我试图使用protobuf-net在Unity游戏中实现保存游戏。我可以很好地序列化独立类,但是当涉及从Unity内置的MonoBehaviour派生的类时,我遇到了一些问题。

基本上,Unity要求 it 创建任何这些对象。通常这很好,因为它们是大型预构建对象图(包含图形和其他内容)的一部分

public class MonoBehaviour : Behaviour {
    public MonoBehaviour() { 
        throw new DontDoThisException (
            "You should never allocate monobehaviours yourself, but let Unity do it"
        ); 
    }
}

在我的游戏中,我有一些东西可以简化为一群怪物,相互攻击。让我们将此模型化为

[ProtoContract]
class Monster : MonoBehaviour {
    [ProtoMember(2)] public int health;
    [ProtoMember(3)] public Monster whoAmITargetting;
}

我有一个SaveManager,它知道我游戏中的所有怪物。

public SaveManager : MonoBehaviour {
    public static List<Monster> allMonsters;
}

当我尝试在Protobuf-net中使用它时,就会出现问题。 Protobuf-net想为我实例化这些对象(通常很好)。问题是我已经拥有了世界上所有的怪物,并希望将数据转化为这些。这变得更加复杂,因为它们互相指向。

我尝试过这样的事情:

[ProtoContract]
[ProtoInclude (100, typeof (Monster))]
class SavableBehaviour : MonoBehaviour {
    [ProtoMember(1)] public long guid;    // I can guarantee that these are unique.
}

[ProtoContract]
class Monster : SavableBehaviour {
    // as above
}

现在,我的目标是将Monster.whoAmITargetting序列化为GUID。当我反序列化时,我会在全局列表中查找GUI并分配对象。

[ProtoContract]
public class SerializableSurrogate
{
    [ProtoMember(1)] int saveID { get; set; }

    public static implicit operator SerializableSurrogate (SavableComponent comp)
    {
        return 
            comp != null 
            ? new SerializableSurrogate { saveID = comp.saveID } 
            : null;
    }

    public static implicit operator SavableComponent (SerializableSurrogate surr)
    {
        return SaveManager.allMonsters.Find (i=>i.guid == surr.saveID);
    }
}

这是有效的(我认为)。我可以看到代理被调用,就像我期望的那样。但是,有一个问题。当我尝试保存SaveManager.allMonsters时,代理也会被激活,这意味着我只保存GUID,而不保存任何其他变量。

我缺少一些新颖的方法吗?

1 个答案:

答案 0 :(得分:0)

我对团结并不十分熟悉,但我怀疑这里有一些选择;要尝试的第一件事是Serializer.Merge而不是Serializer.Deserialize。如果您使用的是TypeModel API,则为Deserialize,但会传入预先存在的实例。

另一种选择是使用自定义工厂;从内存中,此选项目前只能通过触摸RuntimeTypeModel实例(我应该通过属性使其可用)来实现,但这可以作为创建实例的静态方法(可选择接受序列化上下文):做任何你喜欢的事情,包括(大概)要求团结。


通过下面的评论来澄清事情,我的建议改为古老的:序列化数据;不要序列化实现。 MonoBehaviour类及其所有规则是实现。最好是序列化与之无关的东西 - 一个简单的DTO模型(可能是非常基本的,只是你的guids等) - 并且只是在序列化之前/之后映射到DTO模型/从DTO模型映射。这增加了一些代码,但它实际上解决了每个“但我的模型可以做什么限制”的问题,并且还将您与版本控制/重构问题隔离开来(如果您更改为完全不同的引擎,则无关紧要; DTO模型不受模型更改的影响,因此您需要做的就是使映射代码保持最新;您的DTO基本保持不变。)