Каков наилучший способ сохранить terraindata в файл во время выполнения?
Моя игра позволяет пользователю изменять ландшафт во время выполнения, но теперь мне нужно сохранить этот ландшафт. Я пытался напрямую сохранить карту высот ландшафта в файл, но для написания этой карты высот 513x513 требуется почти две минуты.
Что было бы хорошим способом приблизиться к этому? Есть ли способ оптимизировать скорость записи, или я подхожу к этому неправильно?
public static void Save(string pathraw, TerrainData terrain)
{
//Get full directory to save to
System.IO.FileInfo path = new System.IO.FileInfo(Application.persistentDataPath + "/" + pathraw);
path.Directory.Create();
System.IO.File.Delete(path.FullName);
Debug.Log(path);
//Get the width and height of the heightmap, and the heights of the terrain
int w = terrain.heightmapWidth;
int h = terrain.heightmapHeight;
float[,] tData = terrain.GetHeights(0, 0, w, h);
//Write the heights of the terrain to a file
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
//Mathf.Round is to round up the floats to decrease file size, where something like 5.2362534 becomes 5.24
System.IO.File.AppendAllText(path.FullName, (Mathf.Round(tData[x, y] * 100) / 100) + ";");
}
}
}
Как примечание, Mathf.Round, похоже, не слишком сильно влияет на экономию времени, если вообще влияет.
1 ответ
Вы делаете много маленьких индивидуальных вызовов File IO. Файловый ввод-вывод всегда трудоемкий и дорогостоящий, так как содержит открытие файла, запись в него, сохранение файла и закрытие файла.
Вместо этого я бы сгенерировал полную строку, используя, например, StringBuilder
что также более эффективно, чем использование чего-то вроде
var someString for(...) { someString += "xyz" }
потому что последний всегда выделяет новый string
,
Затем используйте, например, FileStream и StringWriter.WriteAsync(string)
для написания асинхронных.
Также лучше использовать Path.Combine
вместо прямой конкатенации строки через /
, Path.Combine
автоматически использует правильные разъемы в соответствии с используемой ОС.
И вместо FileInfo.Directory.Create
скорее использовать Directory.CreateDirectory
который не выдает исключение, если каталог уже существует.
Что-то вроде
using System.IO;
...
public static void Save(string pathraw, TerrainData terrain)
{
//Get full directory to save to
var filePath = Path.Combine(Application.persistentDataPath, pathraw);
var path = new FileInfo(filePath);
Directory.CreateDirectory(path.DirectoryName);
// makes no sense to delete
// ... rather simply overwrite the file if exists
//File.Delete(path.FullName);
Debug.Log(path);
//Get the width and height of the heightmap, and the heights of the terrain
var w = terrain.heightmapWidth;
var h = terrain.heightmapHeight;
var tData = terrain.GetHeights(0, 0, w, h);
// put the string together
// StringBuilder is more efficient then using
// someString += "xyz" because latter always allocates a new string
var stringBuilder = new StringBuilder();
for (var y = 0; y < h; y++)
{
for (var x = 0; x < w; x++)
{
// also add the linebreak if needed
stringBuilder.Append(Mathf.Round(tData[x, y] * 100) / 100).Append(';').Append('\n');
}
}
using (var file = File.Open(filePath, FileMode.OpenOrCreate, FileAccess.Write))
{
using (var streamWriter = new StreamWriter(file, Encoding.UTF8))
{
streamWriter.WriteAsync(stringBuilder.ToString());
}
}
}
Возможно, вы захотите указать, как именно цифры должны быть напечатаны с определенной точностью, например, например,
(Mathf.Round(tData[x, y] * 100) / 100).ToString("0.00000000");