【问题标题】:Problem with serialization of object in unity统一对象序列化的问题
【发布时间】:2020-07-21 14:03:27
【问题描述】:

这是我要序列化的类:

System.Serializable]
public class Save
{
    public int turn, player;
    public int build, attack, action;
    public double resource, resourceProd;
    public List<ObjectData> objectsData = new List<ObjectData>();

    public Save(GameObject[] saveBuilding, GameObject[] saveUnit, int turn, int player, int build, int attack, int action, double resource, double resourceProd)
    {
        foreach(GameObject building in saveBuilding)
        {
            ObjectData data = new ObjectData();
            data.position = building.transform.position;
            data.building = building.GetComponent<Building>();
            data.path = data.building.type[0] + data.building.level + data.building.branch;
            objectsData.Add(data);
        }
        foreach(GameObject unit in saveUnit)
        {
            ObjectData data = new ObjectData();
            data.position = unit.transform.position;
            data.unit = unit.GetComponent<Unit>();
            data.path = data.unit.type[0] + data.unit.level + data.unit.branch;
            objectsData.Add(data);
        }
        this.turn = turn;
        this.player = player;
        this.build = build;
        this.attack = attack;
        this.action = action;
        this.resource = resource;
        this.resourceProd = resourceProd;
        Debug.Log(objectsData.Count);
    }
}

public class ObjectData
{
    public Vector3 position;
    public Building building;
    public Unit unit;
    public string path;
}

当我用下面的方法创建它的一个实例时,objectsData 中有两个元素根据Debug.Log(objectsData.Count);

    public static void SaveTurn()
    {
        turnSave = new Save(GameObject.FindGameObjectsWithTag("building"), GameObject.FindGameObjectsWithTag("unit"), turnCourt, Players.player, BuildDisplay.maxBuildAction, Attacking.maxAttackAction, Building.actions, Currency.resource, Building.totalprod);
        solution.Add(JsonUtility.ToJson(turnSave));
        Debug.Log(JsonUtility.ToJson(turnSave));
    }

当我尝试加载时,保存 Debug.Log(save.objectsData); 返回 null。 为什么会这样?

    public static void LoadSave(string saves)
    {
        Save save = JsonUtility.FromJson<Save>(saves);
        Currency.resource = save.resource;
        Building.totalprod = save.resourceProd;
        BuildDisplay.maxBuildAction = save.build;
        Attacking.maxAttackAction = save.attack;
        Building.actions = save.action;

        Debug.Log(save.objectsData);
        LoadObjects(save.objectsData);

        RemoveObjects(GameObject.FindGameObjectsWithTag("building"));
        RemoveObjects(GameObject.FindGameObjectsWithTag("unit"));
        Turn.refresh();
    }
    public static void LoadObjects(List<ObjectData> gameObjects)
    {
        Debug.Log(gameObjects.Count);
        foreach(ObjectData gameObject in gameObjects)
        {
            GameObject Prefab = Resources.Load(gameObject.path) as GameObject;
            
            if(gameObject.building != null)
            {
                Building building = Prefab.GetComponent<Building>();
                building = gameObject.building;
            }
            if(gameObject.unit != null)
            {
                Unit unit = Prefab.GetComponent<Unit>();
                unit = gameObject.unit;
            }
            Instantiate(Prefab);
        }
    }

在使所有涉及的类可序列化后,Debug.Log(save.objectsData); 不再返回 null,Debug.Log(gameObjects.Count); 返回 2,但在 Building building = Prefab.GetComponent&lt;Building&gt;(); 处出现 null 异常,即使 Prefab 上有 Building 脚本。

【问题讨论】:

  • 您的 ObjectData 不是 [Serializable] ...然后请注意,序列化引用是不可能的/没有意义。 UnitBuildingComponents,它们的数据可以/不会被序列化
  • Unit 和 Building 不是各自类的实例,为什么我不能使它们 [Serializable]?
  • 因为这两个是MonoBehaviour,它继承自Object,它始终仅作为参考处理和序列化。相同,例如如果你有一个GameObject 字段。但是,您唯一可以做的就是再次使用 JSON 来完全序列化该组件的所有字段,并在反序列化时覆盖 JSON 中的字段值

标签: c# unity3d


【解决方案1】:

首先,您要序列化的所有类都需要具有属性[Serializable]ObjetData 也是如此

[Serializable]
public class ObjectData
{
    ...
}

然后这里的另一个“问题”是UnitBuilding 都是MonoBehaviour 派生自UnityEngine.Object。从UnityEngine.Object 派生的所有类型始终仅(反)序列化为实例引用,从不实际包含相应的字段值。


有两种类似的方法。

选项 A

与其直接序列化UnitBuilding,不如序列化一些基本反映组件字段值但在可序列化容器中的专用数据类。

例如假设您的课程看起来像

public class Unit : MonoBehaviour
{
    [SerializeField] private string _name;
    [SerializeField] private int _id;

    ...
}

然后你可以创建一个类似的容器

[Serializable]
public class UnitData
{
    public string Name;
    public int ID;
}

然后为了保持封装并保留您的字段private,我会为您的类型本身添加相应的方法,例如

public class Unit : MonoBehaviour
{
    [SerializeField] private string _name;
    [SerializeField] private int _id;

    public UnitContainer GetData()
    {
        return new UnitContainer()
        {
            Name = _name,
            ID = _id;
        }
    }

    public void SetData(UnitData data)
    {
        _name = data.Name;
        _id = data.ID;

        // ... Probably react to changed data
    }
}

这样您还可以存储通常不序列化的值,如属性或private 字段。

Building 也是如此。

那么您可以将这些存储在您的数据中,例如

[Serializable]
public class ObjectData
{
    public Vector3 position;
    public BuildingData building;
    public UnitData unit;
    public string path;
}

然后在您的 Save 类中,您将确保相应地创建这些数据类

foreach(var building in saveBuilding)
{
    var data = new ObjectData()
    {
        position = building.transform.position,
        building = building.GetComponent<Building>().GetData(),
        path = data.building.type[0] + data.building.level + data.building.branch
    };

    objectsData.Add(data);
}

foreach(var unit in saveUnit)
{
    var data = new ObjectData()
    {
        position = unit.transform.position,
        unit = unit.GetComponent<Unit>().GetData(),
        path = data.unit.type[0] + data.unit.level + data.unit.branch
    };

    objectsData.Add(data);
}

并进行相应的加载

foreach(var data in objectDatas)
{
    var prefab = (GameObject) Resources.Load(data.path);
        
    if(data.building != null)
    {
        var building = Instantiate (prefab).GetComponent<Building>();
        building.SetData(data.building);
    }
    else if(data.unit != null)
    {
        var unit = Instantiate(prefab).GetComponent<Unit>();
        unit.SetData(data.unit);
    }
}

选项 B

实际上非常相似,但不需要额外的数据容器类,您可以直接使用 JSON 再次(反)序列化您的组件,例如

public class Unit : MonoBehaviour
{
    ...

    public string GetData()
    {
        return JsonUtility.ToJson(this);
    }

    public void SetData(string json)
    {
        JsonUtility.FromJsonOverwrite(json, this);

        // ... React to changed data
    }
}

这会自动包含所有可序列化的字段(public 或标记为 [SerializeField] 并具有可序列化类型)。

然后宁愿像这样存储这些json字符串

[Serializable]
public class ObjectData
{
    public Vector3 position;
    public string building;
    public string unit;
    public string path;
}

保存和加载的方法与上面的选项 A 相同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-08
    • 1970-01-01
    • 2010-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多