Каков наилучший способ сохранить terraindata в файл во время выполнения? - PullRequest
1 голос
/ 27 июня 2019

Моя игра позволяет пользователю изменять ландшафт во время выполнения, но теперь мне нужно сохранить указанный ландшафт. Я пытался напрямую сохранить карту высот ландшафта в файл, но для написания этой карты высот 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 Ответ

2 голосов
/ 27 июня 2019

Вы делаете много маленьких индивидуальных вызовов File IO. Файловый ввод-вывод всегда трудоемкий и дорогостоящий, так как содержит открытие файла, запись в него, сохранение файла и закрытие файла.


Вместо этого я предпочел бы сгенерировать полную строку, используя, например, StringBuilder, что также более эффективно, чем использование чего-то вроде

var someString за(...) { 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");
...