【问题标题】:How to serialize and save a GameObject in Unity如何在 Unity 中序列化和保存游戏对象
【发布时间】:2016-08-19 12:44:54
【问题描述】:

我有一个游戏,玩家拿起武器,然后将它作为 GameObject 变量放置给我的玩家,称为“MainHandWeapon”,我试图通过场景更改来保存该武器,所以我试图保存它。我的处理方式如下:

public class Player_Manager : Character, Can_Take_Damage {

    // The weapon the player has.
    public GameObject MainHandWeapon;

    public void Save()
    {
        // Create the Binary Formatter.
        BinaryFormatter bf = new BinaryFormatter();
        // Stream the file with a File Stream. (Note that File.Create() 'Creates' or 'Overwrites' a file.)
        FileStream file = File.Create(Application.persistentDataPath + "/PlayerData.dat");
        // Create a new Player_Data.
        Player_Data data = new Player_Data ();
        // Save the data.
        data.weapon = MainHandWeapon;
        data.baseDamage = BaseDamage;
        data.baseHealth = BaseHealth;
        data.currentHealth = CurrentHealth;
        data.baseMana = BaseMana;
        data.currentMana = CurrentMana;
        data.baseMoveSpeed = BaseMoveSpeed;
        // Serialize the file so the contents cannot be manipulated.
        bf.Serialize(file, data);
        // Close the file to prevent any corruptions
        file.Close();
    }
}

[Serializable]
class Player_Data
{
    [SerializeField]
    private GameObject _weapon;
    public GameObject weapon{
        get { return _weapon; }
        set { _weapon = value; }
    }

    public float baseDamage;
    public float baseHealth;
    public float currentHealth;
    public float baseMana;
    public float currentMana;
    public float baseMoveSpeed;
}

但我不断收到此设置错误:

SerializationException: Type UnityEngine.GameObject is not marked as Serializable.

我到底做错了什么?

【问题讨论】:

  • 不是答案,但here 是指向已回答的类似问题的链接。我希望这会有所帮助。
  • 乔伊,你必须“手工”完成。就是这么简单。 Unity 中根本没有序列化或“保存东西”——就这么简单。 (请注意,所谓的“序列化”类几乎完全没用;只是不是为了那个目的。)
  • 注意。您说“我正在尝试通过场景更改来保存该武器……”为此,您只需使用 DontDestroyOnLoad。一个角色/任何东西会在场景中移动是完全司空见惯的。完全没有必要保存或序列化它。

标签: c# unity3d


【解决方案1】:

Unity 不会让您这样做,因为 Gameobject 包含所有附加到它的脚本。例如网格渲染器、碰撞器等。

如果你想说序列化变换,你可以通过创建一个新的 Vector3 位置、Vector3 比例、Vector4 四元数来解决这个问题,然后将其序列化,然后在反序列化时将此数据输入一个新的变换(例如) .

但尝试序列化与网格渲染器关联的实际网格数据将证明是一项相当复杂的任务。最好只序列化一个 int 或代表网格 ID 的东西,然后在加载时将其转换为正确的引用。

【讨论】:

  • 那这是什么意思呢? “- 从 UnityEngine.Object 继承的所有类,例如 GameObject、Component、MonoBehaviour、Texture2D、AnimationClip。”因此,通过该声明,我应该能够保存一个 GameObject 对吗?还是我不明白这一点?同样通过这个声明,如果我不想保存游戏对象,我可以只保存脚本吗?如果不是,我觉得他们提出的内容非常具有误导性。
  • 你的报价从哪里得出@JoeyL?
  • 它们的意思是在编辑器中可序列化为它们的内部保存格式。
【解决方案2】:

理想情况下,您应该有一个对象来保存武器数据(攻击、耐用性等...)并序列化该对象,它可以使您的保存游戏更小,并且更 OOP,因为您可以从该类继承。

【讨论】:

    【解决方案3】:

    经过几个小时的实验,我得出结论,Unity不能BinaryFormatter 序列化GameObject。 Unity 在他们的 API 文档中声称这是可能的,但它不是

    如果你想在不删除 _weapon GameObject 的情况下删除 error,你应该替换 ...

    [SerializeField]
    private GameObject _weapon;
    

    [NonSerialized]
    private GameObject _weapon;
    

    这将使您的其余代码运行而不会引发异常,但您不能反序列化 _weapon GameObject。您可以反序列化其他字段。

    您可以将 GameObject 序列化为 xml。这可以毫无问题地序列化 GameObject。它以人类可读的格式保存数据。如果您关心安全或不希望玩家在自己的设备上修改分数,您可以encrypt,将其转换为binaryBase-64格式,然后保存 它到磁盘。

    using UnityEngine;
    using System.Collections;
    using System.Runtime.Serialization.Formatters.Binary;
    using System.IO;
    using System;
    using System.Runtime.Serialization;
    using System.Xml.Linq;
    using System.Text;
    
    public class Player_Manager : MonoBehaviour
    {
    
        // The weapon the player has.
        public GameObject MainHandWeapon;
    
        void Start()
        {
            Save();
        }
    
        public void Save()
        {
            float test = 50;
    
            Debug.Log(Application.persistentDataPath);
    
            // Stream the file with a File Stream. (Note that File.Create() 'Creates' or 'Overwrites' a file.)
            FileStream file = File.Create(Application.persistentDataPath + "/PlayerData.dat");
            // Create a new Player_Data.
            Player_Data data = new Player_Data();
            //Save the data.
            data.weapon = MainHandWeapon;
            data.baseDamage = test;
            data.baseHealth = test;
            data.currentHealth = test;
            data.baseMana = test;
            data.currentMana = test;
            data.baseMoveSpeed = test;
    
            //Serialize to xml
            DataContractSerializer bf = new DataContractSerializer(data.GetType());
            MemoryStream streamer = new MemoryStream();
    
            //Serialize the file
            bf.WriteObject(streamer, data);
            streamer.Seek(0, SeekOrigin.Begin);
    
            //Save to disk
            file.Write(streamer.GetBuffer(), 0, streamer.GetBuffer().Length);
    
            // Close the file to prevent any corruptions
            file.Close();
    
            string result = XElement.Parse(Encoding.ASCII.GetString(streamer.GetBuffer()).Replace("\0", "")).ToString();
            Debug.Log("Serialized Result: " + result);
    
        }
    }
    
    
    [DataContract]
    class Player_Data
    {
        [DataMember]
        private GameObject _weapon;
    
        public GameObject weapon
        {
            get { return _weapon; }
            set { _weapon = value; }
        }
    
        [DataMember]
        public float baseDamage;
        [DataMember]
        public float baseHealth;
        [DataMember]
        public float currentHealth;
        [DataMember]
        public float baseMana;
        [DataMember]
        public float currentMana;
        [DataMember]
        public float baseMoveSpeed;
    }
    

    【讨论】:

    • “经过数小时的实验,我得出结论 Unity 无法使用 BinaryFormatter 序列化 GameObject。Unity 在其 API 文档中声称这是可能的,但事实并非如此。” 我同意你 100%。
    • @JoeBlow 还有很多其他人也有同样的问题。要么是文档有错误,要么是 unity 花时间编写代码但未能测试。
    • 什么序列化List<T> 类或Object[] foo 使用您的解决方案?它也会起作用吗?我尝试了您的解决方案,它仅适用于一个特定的类,但不适用于两个示例等数组类型。
    • 我真的不知道。此答案仅适用于 GameObject;。你应该测试一下看看。
    • @Programmer 看到您也对 Unity 一贯的不一致感到愤怒,我感到高兴、宽慰和难过。这证实了我长期以来的想法,即他们粗心大意。
    猜你喜欢
    • 2017-10-24
    • 2017-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-17
    • 1970-01-01
    • 2012-05-18
    相关资源
    最近更新 更多