我运行保存文件的脚本后,Unity就崩溃了
我想创建一个保存文件,用来保存场景中所有对象的位置和速度,但当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导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。