保存自定义 C# 类(包含各种大小的 lists/arrays)而不是 binaryformatter.serialize 的推荐过程?
Recommended process for saving Custom C# Classes (containing lists/arrays of various size) other than binaryformatter.serialize?
我希望用户能够在我的应用程序中创建网格样式 'maps',将它们保存到磁盘,然后与其他人共享。在基本地图数据中,我希望用户输入他们的名字以便'claim'他们的工作。
为了更安全一点,我正在考虑保存地图 class(包括艺术家姓名、地图尺寸、其他有用的选项和 flat/2D array/list [必要时删除]) 成二进制文件。所以,XML(人类可读)可能不是我要使用的……但我还没有完全反对他们,只是谨慎地停止 'stealing'。
我一直在使用 BinaryFormatter.Serialize 方法,但是随着我的 tiledata 的大小从 200*200 tiles 增长到 300*300 tiles,反序列化所需的时间从 6.5 秒增长到 33 秒,并继续以指数方式增长。
理想情况下,如果可能的话,我希望能够存储 2000*2000 的地图数据。
因此,除了学习压缩技术(我无论如何都会这样做以减小文件大小)之外,我想知道是否还有其他推荐我使用的序列化程序。我已经看到有一些第 3 方的可用,但需要弄清楚它们是否与 Unity 兼容(是的......我是一个自学成才的 noob gamedev)。
如有任何帮助,我们将不胜感激。
为清楚起见添加了当前测试代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
public class FileReaderTest : MonoBehaviour
{
public int xlimit = 300;
public int ylimit = 300;
// Use this for initialization
void Start()
{
Save();
}
// Update is called once per frame
void Update()
{
if (Input.anyKeyDown)
Load();
}
private void Save()
{
BinaryFormatter formatter = new BinaryFormatter();
FileStream file = File.Open(Application.dataPath + "/saved.data", FileMode.OpenOrCreate);
List<TestDataBaseEntry> mapTileData = new List<TestDataBaseEntry>();
List<TestDataBaseEntry> extraMapTileData = new List<TestDataBaseEntry>();
for (int x = 0; x < xlimit; x++)
{
for (int y = 0; y < ylimit; y++)
{
// Simulating random data
mapTileData.Add(new TestDataBaseEntry(TileState.Filled, x, y));
}
}
for (int x = 0; x < 5; x++)
{
for (int y = 0; y < 3; y++)
{
extraMapTileData.Add(new TestDataBaseEntry(TileState.Ignore, x, y));
}
}
TestMapFile newFile = new TestMapFile("Mike", xlimit, ylimit, mapTileData, extraMapTileData);
formatter.Serialize(file, newFile);
file.Close();
Debug.Log("saved");
}
private void Load()
{
float starttime = Time.realtimeSinceStartup;
if (File.Exists(Application.dataPath + "/saved.data"))
{
using (FileStream file = File.Open(Application.dataPath + "/saved.data", FileMode.Open))
{
BinaryFormatter formatter = new BinaryFormatter();
TestMapFile retrievedTestMapFile = (TestMapFile) formatter.Deserialize(file);
}
}
Debug.Log("Loaded");
Debug.Log("The process took " + (Time.realtimeSinceStartup - starttime));
}
}
[Serializable]
public class TestDataBaseEntry
{
public TileState tileState;
public int x;
public int y;
public TestDataBaseEntry(TileState newTileState, int newX, int newY)
{
tileState = newTileState;
x = newX;
y = newY;
}
}
[Serializable]
public class TestMapFile
{
public int xSize;
public int ySize;
public List<TestDataBaseEntry> mapTileData = new List<TestDataBaseEntry>();
public List<TestDataBaseEntry> mapExtraTileData = new List<TestDataBaseEntry>();
public string createdBy;
public TestMapFile(string artist, int newXSize, int newYSize, List<TestDataBaseEntry> newDB, List<TestDataBaseEntry> newExtraDB)
{
createdBy = artist;
xSize = newXSize;
ySize = newYSize;
mapTileData = newDB;
mapExtraTileData = newExtraDB;
}
}
查看 protobuf-net。它的可读性不是很好,但速度很快,而且占用的磁盘空间更少 space。
https://github.com/mgravell/protobuf-net
好的,我已经采纳了我之前在 Unity 论坛上的 post 的建议,并考虑了这里所说的一些要点,并创建了一些测试脚本 - 这里是我的发现。
首先,继 @Scott Hannen 在评论中提到的内容(在之前 forum.Unity.com 上的 post 中也提到过)之后,我尝试制作自己的 de/serializer ,我剥离了要放入文件(并从中读回)的信息,并将时间缩短到 0.13 秒以获得足够的信息来为 1920 * 1080 大小的地图重新创建地形。我使用的数据只是一个字符串、2 个整数和一个平面布尔数组。
所以我又回去查看 BinaryFormatter。为了使比较相互内联,我必须解决的最大区别是将自定义 class TestDataBaseEntry 的列表更改为布尔数组(这是我与二进制编写器一起使用的)。仅此一项就使读取文件的时间从 300*300 地图大小的 33.3 秒减少到 1920*1080 地图大小的 11.1 秒!!!一个巨大的改进 - 我猜 BinaryFormatter 的一些过程是大量的背景检查和错误检查?
所以它仍然不如我的剥离自定义序列化程序快。然而,这仍然不是一个真正的比较,因为我仍然 de/serializing 一个实际的 class 与 BinaryFormatter,而我 de/serializing 只是 class 的组件单独使用 BinaryWriter/BinaryReader.
所以我还有更多测试要做,但到目前为止,对我来说,解决方案似乎是简化我尝试写入文件的信息并尽可能多地删除自定义类型(?) .我很可能从中读到了错误的答案...但我会继续努力,看看我能做些什么。
非常感谢所有回复并提供其他途径供我研究的人。我仍将进一步研究这些以扩展我的知识...再加上其中之一(如 protobuf-net)可能仍然是一个更好的实施解决方案;)
再次感谢。后来.
更新 - 第二天早上...
所以我 运行 进行了另一次测试,这次完全剥离了自定义 class 并简单地将相关部分直接序列化和反序列化到文件中或从文件中反序列化。现在 BinaryFormatter 不必在可序列化的自定义中处理它 class 我现在的读取时间为 0.15 秒——这与我使用自己的自定义序列化程序时的效果相同。
所以,在你的帮助下我学到了一些非常有价值的东西 - 序列化基本类型而不是自定义 classes!
再次感谢 ;)
我希望用户能够在我的应用程序中创建网格样式 'maps',将它们保存到磁盘,然后与其他人共享。在基本地图数据中,我希望用户输入他们的名字以便'claim'他们的工作。
为了更安全一点,我正在考虑保存地图 class(包括艺术家姓名、地图尺寸、其他有用的选项和 flat/2D array/list [必要时删除]) 成二进制文件。所以,XML(人类可读)可能不是我要使用的……但我还没有完全反对他们,只是谨慎地停止 'stealing'。
我一直在使用 BinaryFormatter.Serialize 方法,但是随着我的 tiledata 的大小从 200*200 tiles 增长到 300*300 tiles,反序列化所需的时间从 6.5 秒增长到 33 秒,并继续以指数方式增长。
理想情况下,如果可能的话,我希望能够存储 2000*2000 的地图数据。
因此,除了学习压缩技术(我无论如何都会这样做以减小文件大小)之外,我想知道是否还有其他推荐我使用的序列化程序。我已经看到有一些第 3 方的可用,但需要弄清楚它们是否与 Unity 兼容(是的......我是一个自学成才的 noob gamedev)。
如有任何帮助,我们将不胜感激。
为清楚起见添加了当前测试代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
public class FileReaderTest : MonoBehaviour
{
public int xlimit = 300;
public int ylimit = 300;
// Use this for initialization
void Start()
{
Save();
}
// Update is called once per frame
void Update()
{
if (Input.anyKeyDown)
Load();
}
private void Save()
{
BinaryFormatter formatter = new BinaryFormatter();
FileStream file = File.Open(Application.dataPath + "/saved.data", FileMode.OpenOrCreate);
List<TestDataBaseEntry> mapTileData = new List<TestDataBaseEntry>();
List<TestDataBaseEntry> extraMapTileData = new List<TestDataBaseEntry>();
for (int x = 0; x < xlimit; x++)
{
for (int y = 0; y < ylimit; y++)
{
// Simulating random data
mapTileData.Add(new TestDataBaseEntry(TileState.Filled, x, y));
}
}
for (int x = 0; x < 5; x++)
{
for (int y = 0; y < 3; y++)
{
extraMapTileData.Add(new TestDataBaseEntry(TileState.Ignore, x, y));
}
}
TestMapFile newFile = new TestMapFile("Mike", xlimit, ylimit, mapTileData, extraMapTileData);
formatter.Serialize(file, newFile);
file.Close();
Debug.Log("saved");
}
private void Load()
{
float starttime = Time.realtimeSinceStartup;
if (File.Exists(Application.dataPath + "/saved.data"))
{
using (FileStream file = File.Open(Application.dataPath + "/saved.data", FileMode.Open))
{
BinaryFormatter formatter = new BinaryFormatter();
TestMapFile retrievedTestMapFile = (TestMapFile) formatter.Deserialize(file);
}
}
Debug.Log("Loaded");
Debug.Log("The process took " + (Time.realtimeSinceStartup - starttime));
}
}
[Serializable]
public class TestDataBaseEntry
{
public TileState tileState;
public int x;
public int y;
public TestDataBaseEntry(TileState newTileState, int newX, int newY)
{
tileState = newTileState;
x = newX;
y = newY;
}
}
[Serializable]
public class TestMapFile
{
public int xSize;
public int ySize;
public List<TestDataBaseEntry> mapTileData = new List<TestDataBaseEntry>();
public List<TestDataBaseEntry> mapExtraTileData = new List<TestDataBaseEntry>();
public string createdBy;
public TestMapFile(string artist, int newXSize, int newYSize, List<TestDataBaseEntry> newDB, List<TestDataBaseEntry> newExtraDB)
{
createdBy = artist;
xSize = newXSize;
ySize = newYSize;
mapTileData = newDB;
mapExtraTileData = newExtraDB;
}
}
查看 protobuf-net。它的可读性不是很好,但速度很快,而且占用的磁盘空间更少 space。 https://github.com/mgravell/protobuf-net
好的,我已经采纳了我之前在 Unity 论坛上的 post 的建议,并考虑了这里所说的一些要点,并创建了一些测试脚本 - 这里是我的发现。
首先,继 @Scott Hannen 在评论中提到的内容(在之前 forum.Unity.com 上的 post 中也提到过)之后,我尝试制作自己的 de/serializer ,我剥离了要放入文件(并从中读回)的信息,并将时间缩短到 0.13 秒以获得足够的信息来为 1920 * 1080 大小的地图重新创建地形。我使用的数据只是一个字符串、2 个整数和一个平面布尔数组。
所以我又回去查看 BinaryFormatter。为了使比较相互内联,我必须解决的最大区别是将自定义 class TestDataBaseEntry 的列表更改为布尔数组(这是我与二进制编写器一起使用的)。仅此一项就使读取文件的时间从 300*300 地图大小的 33.3 秒减少到 1920*1080 地图大小的 11.1 秒!!!一个巨大的改进 - 我猜 BinaryFormatter 的一些过程是大量的背景检查和错误检查?
所以它仍然不如我的剥离自定义序列化程序快。然而,这仍然不是一个真正的比较,因为我仍然 de/serializing 一个实际的 class 与 BinaryFormatter,而我 de/serializing 只是 class 的组件单独使用 BinaryWriter/BinaryReader.
所以我还有更多测试要做,但到目前为止,对我来说,解决方案似乎是简化我尝试写入文件的信息并尽可能多地删除自定义类型(?) .我很可能从中读到了错误的答案...但我会继续努力,看看我能做些什么。
非常感谢所有回复并提供其他途径供我研究的人。我仍将进一步研究这些以扩展我的知识...再加上其中之一(如 protobuf-net)可能仍然是一个更好的实施解决方案;)
再次感谢。后来.
更新 - 第二天早上...
所以我 运行 进行了另一次测试,这次完全剥离了自定义 class 并简单地将相关部分直接序列化和反序列化到文件中或从文件中反序列化。现在 BinaryFormatter 不必在可序列化的自定义中处理它 class 我现在的读取时间为 0.15 秒——这与我使用自己的自定义序列化程序时的效果相同。
所以,在你的帮助下我学到了一些非常有价值的东西 - 序列化基本类型而不是自定义 classes!
再次感谢 ;)