Рекомендуемый процесс сохранения пользовательских классов C # (содержащих списки / массивы различного размера), кроме binaryformatter.serialize? - PullRequest
0 голосов
/ 29 июня 2018

Я бы хотел, чтобы пользователи могли создавать «карты» в виде сетки в моем приложении, сохранять их на диск и затем делиться ими с другими. Среди основных картографических данных я бы хотел, чтобы пользователь вводил свое имя, чтобы «заявить» о своей работе.

Чтобы это было немного более безопасным, я думал о сохранении класса карты (с именем художника, размерами карты, другими полезными опциями и плоским / 2D массивом / списком [удалить при необходимости]) в двоичный файл. Итак, XML (удобочитаемый для человека), вероятно, не то, что я собираюсь использовать ... но я еще не полностью настроен против них, просто осторожен, чтобы не «воровать».

Я использовал метод BinaryFormatter.Serialize, однако по мере того, как размер моих tiledata увеличивается с 200 * 200 плиток до 300 * 300 плиток, время, необходимое для десериализации, увеличивается с 6,5 до 33 секунд и продолжает увеличиваться экспоненциально.

В идеале я бы хотел иметь возможность хранить картографические данные 2000 * 2000, если это возможно.

Итак, помимо изучения методов сжатия (которые я буду делать в любом случае для уменьшения размера файлов), мне было интересно, есть ли другие сериализаторы, которые рекомендуется использовать. Я видел, что есть некоторые сторонние доступные, но нужно выяснить, совместимы ли они с 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;
    }
}

Ответы [ 2 ]

0 голосов
/ 01 июля 2018

Хорошо, я воспользовался советом из предыдущего поста, который я имел на форумах Unity, а также рассмотрел некоторые из пунктов, которые были здесь упомянуты, и создал несколько тестовых сценариев - вот мои выводы.

Прежде всего, исходя из того, что @Scott Hannen упомянул в комментарии (это также упоминалось в предыдущем посте на forum.Unity.com), я попытался создать собственный де-сериализатор, я удалил информацию, которую нужно поместить. в файл (и считывание обратно), и время сократилось до 0,13 секунды, чтобы получить достаточно информации для воссоздания местности для карты размером 1920 * 1080. Данные, с которыми я работал, были просто строкой, 2-мя целочисленными значениями и плоским массивом bools.

Итак, я вернулся, чтобы снова посмотреть на BinaryFormatter. Сразу же самым большим отличием, которое мне пришлось учесть, чтобы сопоставить сравнения друг с другом, было изменение списка пользовательского класса TestDataBaseEntry в массив bools (это то, что я использовал с бинарным писателем). Одно это уменьшило время чтения файла с 33,3 секунды для карты размером от 300 * 300 до 11,1 для карты размером 1920 * 1080 !!! Массовое улучшение - я предполагаю, что часть процесса BinaryFormatter - это много фоновых проверок и ошибок?

Так что это все еще не так быстро, как мой раздетый пользовательский сериализатор. Тем не менее, это еще не настоящее сравнение, поскольку я все еще де / сериализую реальный класс с BinaryFormatter, тогда как я де / сериализую только компоненты этого класса по отдельности, используя BinaryWriter / BinaryReader.

Так что у меня еще есть кое-какое тестирование, но для меня пока что решение состоит в том, чтобы не усложнять информацию, которую я пытаюсь записать в файл, и удалить как можно больше пользовательских типов (?). Вполне возможно, что я читаю неправильные ответы из этого ... но я взломаюсь и посмотрю, что я могу сделать.

Большое спасибо всем, кто ответил и предоставил мне другие возможности для изучения. Я все еще буду разбираться с ними дальше, чтобы расширить свои знания ... плюс один из них (например, protobuf-net) может быть лучшим решением для реализации;)

Еще раз спасибо. Laters.

ОБНОВЛЕНИЕ - На следующее утро ...

Итак, я запустил еще один тест, на этот раз полностью исключив пользовательский класс и просто сериализовав и десериализовав соответствующую часть прямо в файл и из файла. Теперь, когда BinaryFormatter не приходится иметь дело с ним в сериализуемом настраиваемом классе, я теперь получаю время чтения 0,15 секунды - то же самое, как если бы я использовал свой собственный настраиваемый сериализатор.

Итак, я узнал кое-что очень ценное со всей вашей помощью - сериализовать базовый тип, а не пользовательские классы!

Опять большое спасибо;)

0 голосов
/ 29 июня 2018

Посмотрите на protobuf-сеть. Это не так читается, довольно быстро и занимает меньше места на диске. https://github.com/mgravell/protobuf-net

...