我运行保存文件的脚本后,Unity就崩溃了

编程语言 2026-07-11

我想创建一个保存文件,用来保存场景中所有对象的位置和速度,但当OnSave()被调用时,Unity编辑器会完全冻结,似乎什么也没做。

这是我的代码:

using System.Collections.Generic;
using UnityEngine;
using Newtonsoft.Json;
using System.IO;
using System;

public class DataService : MonoBehaviour
{   
    string path = Directory.GetCurrentDirectory() + "/SaveFileGame.Json";
    public SaveFile Saver;

    public List<GameObject> GB_list = new List<GameObject>();
    public List<Vector3> VEL_list = new List<Vector3>();
    public List<Vector3> TRANS_list = new List<Vector3>();

    public void OnSave()
    {   
        GB_list.Clear();
        VEL_list.Clear();
        TRANS_list.Clear();

        var componentRB = GameObject.FindObjectsByType<Rigidbody>(FindObjectsSortMode.None);//Adds all rigidbody components

        for (int i = 0; i < componentRB.Length; i++)
        {
            if (! componentRB[i].gameObject.CompareTag("Player"))
            {
                // removes BIMOS Player objects
                VEL_list.Add(componentRB[i].linearVelocity);//Adds velocity to list
                GB_list.Add(componentRB[i].gameObject);//Adds GameObject to list
                TRANS_list.Add(componentRB[i].gameObject.GetComponent<Transform>().position);  // Adds Transform To list
            }
        }

        Saver.SAVED_GB = GB_list;
        Saver.SAVED_VEL = VEL_list;
        Saver.SAVED_TRANS = TRANS_list;   // applies all lists to SaveFile script to be easily re applied to Json

        if (File.Exists(path))
        {
            File.Delete(path);
            string json = JsonConvert.SerializeObject(Saver, Formatting.Indented, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore});
            File.WriteAllText(path, json);     //overwrites file if it already exists  
        }
        else
        {  
            File.Create(path);
            string json = JsonConvert.SerializeObject(Saver, Formatting.Indented, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore}); //creates new file if it doesn't
            File.WriteAllText(path,json);
        }
    }
}

我用下面的代码来防止自引用循环,这个循环是不是会一遍又一遍地发生,导致崩溃,但被忽略以防止错误?

JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore});

编辑:这是保存文件脚本:

using UnityEngine;
using BIMOS;
using System.Collections.Generic;

[System.Serializable]
public class SaveFile 
{   
    public List<GameObject> SAVED_GB = new List<GameObject>();
    public List<Vector3> SAVED_VEL = new List<Vector3>();
    public List<Vector3> SAVED_TRANS = new List<Vector3>();
}

解决方案

据我所知,这里有若干问题:

  • 首先,直接把一个 GameObject 序列化为JSON是没有意义的。

JSON中的一个 GameObject 到底应该长成什么样?它基本上只是一些晦涩的实例引用ID,在导出中几乎没有用处。唯一也许有点有效的数据在一个 GameObject 上可能仅限于名称、标签和层级等。我很确定这根本不是你想要的。

你可以做的是,例如检查附着的组件,进行深度序列化,稍后在加载时再逐一创建它们。 不确定这是不是你在这里真正想要和需要的。

对我而言,这听起来像是某种“序列化某类型的所有对象”的事情。因此,这里的解决办法就是只存储该类型,在加载时再据此创建它们。

  • 然后——也已知 Vector3 与Newtonsoft JSON相处并不理想,因为它包含一些属性,这些属性又返回 Vector 自身(例如 normalized),而Newtonsoft试图把它们全部序列化,基本上导致无限序列化循环。它们是结构体,且从来不是同一个引用!

ReferenceLoopHandling.Ignore 用于在你实际遇到相同对象实例引用时进行忽略。这在一些图结构中很有用,因为循环引用是可能的。它和你的数据结构无关。

还有其他一些属性也会返回 float,你仍然会序列化它们(magnitude 等)

对此你通常会引入你自己的自定义镜像类型,它只包含简单的x,y,z,没有任何额外的花哨,然后在序列化时在它们之间来回转换。

  • 然后——取决于这是否打算在UnityEditor外运行,还是在构建版本中运行——在构建版本中你不能在大多数平台上简单地写入 Directory.GetCurrentDirectory()。可以参考例如 Application.persistentDataPath

除此之外

  • 使用 Rigidbody 时你可以直接使用它的 position

  • 通过 Transform 时就没有理由再使用 GetComponent 来获取它

但这只是相对上面的内容而言的次要注释


所以暂时跳过 GameObject,我大概会给出这样一个例子:

public class SerializeVector3
{
    public float x, y, z;

    public static implicit operator Vector3(SerializeVector3 vec) => new Vector3(vec.x, vec.y, vec.z);

    public static implicit operator SerializeVector3(Vector3 vec) => new SerializeVector3 { x = vec.x, y = vec.y, z = vec.z };
}

public class SaveFile 
{   
    // Skip until you actually know what exactly you want to save here 
    // public List<GameObject> SAVED_GB = new ();
    public List<SerializeVector3> SAVED_VEL = new ();
    public List<SerializeVector3> SAVED_TRANS = new ();
}

然后是

    public void OnSave()
    {   
        //Saver.SAVED_GB.Clear();
        Saver.SAVED_VEL.Clear();
        Saver.SAVED_TRANS.Clear();

        var componentRBs = GameObject.FindObjectsByType<Rigidbody>(FindObjectsSortMode.None);

        foreach (var rb in componentRBs)
        {
            if (rb.gameObject.CompareTag("Player")) continue;

            Saver.SAVED_VEL.Add(rb.linearVelocity);
            //Saver.SAVED_GB.Add(rb.gameObject);
            Saver.SAVED_TRANS.Add(rb.position); // implicitly converts to SerializeVector3 

        }   

        // as said the loop handling is not really relevant to your data structure
        // since you are anyway dealing with structs and no references
        string json = JsonConvert.SerializeObject(Saver, Formatting.Indented, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore});
        File.WriteAllText(path, json);  // already overwrites file if it already exists       
    }
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章