Запрос направления для загрузки моего файла кусками (нужен только совет) - PullRequest
2 голосов
/ 15 июня 2011

Последние несколько дней я работал над методом сжатия 144 миллионов плиточных представлений для моей игры xna до очень маленького размера при сохранении.После того, как я справился с этим, я теперь озадачен тем, как получить их обратно из файла кусками.

В файле, который у меня есть.

  1. Целое число (оносжимается в байты с помощью метода 7BitEncodedInt)
  2. Байт

Сжатое целое число представляет количество плиток, а следующий байт определяет тип плиток.Это все хорошо и работает очень хорошо.Самое главное, что размер файла уменьшается в среднем до 50 МБ.

Проблема в том, что я сейчас читаю весь файл обратно.Из файла я получаю это.

  1. Значение индекса каждой плитки (просто базовая итерация, когда я беру плитки)
  2. Тип каждой плитки в виде значения байта
  3. Значение байта, представляющее текстуру для этой плитки (это трудно объяснить, но это необходимо для каждой плитки)

Конечным результатом всего этого является то, что яудается сохранить файл и использовать только около 50 МБ.Но при загрузке все это обратно расширяется до почти 1,5 гигабарана на ОЗУ.Я не могу больше жертвовать информацией о плитке.поэтому мне нужен способ загружать только части карты в зависимости от местоположения игрока.Цель состоит в том, чтобы приблизиться к диапазону 100-200 МБ

Я смотрел на отображение памяти в файле, используя квадри, почти все, что я мог найти для загрузки файлов кусками.Хотя все эти варианты кажутся довольно хорошими, я не уверен, что лучше, или, если принять во внимание ситуацию, может быть и другой, даже лучший.Другая проблема во всем этом заключается в том, что все эти решения кажутся очень сложными (особенно потому, что я использую их впервые), и хотя я не против посвятить себя длительному кодированию, я хотел бы знать, что оно будет делать то, что яМне нужно, чтобы это было перед рукой.

Мой вопрос: учитывая, как мне нужно обрабатывать файл, когда я его извлекаю, и тот факт, что это нужно делать в зависимости от местоположения игроков, что было бы лучшим способомсделай это ?Я просто ищу направление здесь.Код всегда приветствуется, но не обязателен.

Ответы [ 2 ]

0 голосов
/ 19 июня 2011

Существует несколько вариантов, но не все из них могут подходить для вашего конкретного проекта:

  • Не используйте один файл для всех данных. Разделите карту на более мелкие «комнаты» и сохраните каждую в отдельном файле. Загружайте только «комнату», в которой игрок запускает, и преимущественно загружайте соседние «комнаты» и выгружайте старые.
  • Уменьшите количество плиток, которые нужно хранить. Используйте процедурную генерацию для создания макета области. Если у вас есть комната 10x10 с полом, сделанным из одного типа плитки, то не храните 100 отдельных плиток, а вместо этого используйте специальный маркер, который говорит, что «эта область имеет 10x10 этаж с этой плиткой». Если это стена, то сохраните начальную и конечную позиции и тип текстуры. Если у вас в центре открытого поля находится элемент мозаики из нескольких плиток, и его положение не имеет отношения к истории, поместите его случайным образом в поле (и сохраните начальное число для генератора случайных чисел в файле карты, чтобы в следующий раз он был появляются там же).
0 голосов
/ 19 июня 2011

Вы хотите иметь переменные фиксированной длины в своем классе Tile и реализовать что-то вроде этого:

Это пример класса коллекции (People), который может получить значение на основе индекса из коллекции, сериализованной в файл.

Человек - это класс, который является основой коллекции People.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace FileStreamDatabaseTest
{
    class Program
    {
        static void Main(string[] args)
        {
            People.OpenCollection();
            People.Test_WillOverwriteData();
            People.CloseCollection();
            Console.ReadLine();
        }
    }

    public class Person
    {
        // define maxium variable sizes for serialisation
        protected static int pMaxLength_FirstName = 64;
        protected static int pMaxLength_Age = 10;
        public static int MaxObjectSize
        {
            get
            {
                // return the sum of all the maxlegnth variables to define the entire object size for serialisation
                return pMaxLength_FirstName + pMaxLength_Age;
            }
        }

        // define each object that will be serialised as follows 
        protected string pFirstName;
        public string Firstname
        {
            set
            {
                // ensure the new value is not over max variable size
                if (value.Length > pMaxLength_FirstName)
                    throw new Exception("the length of the value is to long.");

                pFirstName = value;
            }
            get
            {
                return pFirstName;
            }
        }
        protected int pAge;
        public int Age
        {
            get
            {
                return pAge;
            }
            set
            {
                pAge = value;
            }
        }

        public byte[] Serialise()
        {
            // Output string builder
            StringBuilder Output = new StringBuilder();

            // Append firstname value
            Output.Append(Firstname);

            // Add extra spaces to end of string until max length is reached
            if (Firstname.Length < pMaxLength_FirstName)
                for (int i = Firstname.Length; i < pMaxLength_FirstName; i++)
                    Output.Append(" ");

            // Append age value as string
            Output.Append(Age.ToString());

            // Add extra spaces to end of string until max length is reached
            int AgeLength = Age.ToString().Length;
            if (AgeLength < pMaxLength_Age)
                for (int i = AgeLength; i < pMaxLength_Age; i++)
                    Output.Append(" ");

            // Return the output string as bytes using ascii encoding
            return System.Text.Encoding.ASCII.GetBytes(Output.ToString());
        }

        public void Deserialise(byte[] SerialisedData)
        {
            string Values = System.Text.Encoding.ASCII.GetString(SerialisedData);

            pFirstName = Values.Substring(0, pMaxLength_FirstName).Trim();
            pAge = int.Parse(Values.Substring(pMaxLength_FirstName, pMaxLength_Age).Trim());
        }
    }

    public static class People
    {
        private static string tileDatasource = @"c:\test.dat";
        private static System.IO.FileStream FileStream;

        public static void OpenCollection()
        {
            FileStream = new System.IO.FileStream(tileDatasource, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.ReadWrite, System.IO.FileShare.None);
        }

        public static void CloseCollection()
        {
            FileStream.Close();
            FileStream.Dispose();
            FileStream = null;
        }

        public static void SaveCollection(Person[] People)
        {
            FileStream.SetLength(People.Length * Person.MaxObjectSize);
            FileStream.Position = 0;

            foreach (Person PersonToWrite in People)
            {
                // call serialise to get bytes
                byte[] OutputBytes = PersonToWrite.Serialise();

                // write the output buffer
                // note: this will always be the same size as each variable should 
                //       append spaces until its max size is reached
                FileStream.Write(OutputBytes, 0, OutputBytes.Length);
            }
        }

        public static Person GetValue(int Index)
        {
            // set the stream position to read the object by multiplying the requested index with the max object size
            FileStream.Position = Index * Person.MaxObjectSize;

            // read the data
            byte[] InputBytes = new byte[Person.MaxObjectSize];
            FileStream.Read(InputBytes, 0, Person.MaxObjectSize);

            // deserialise
            Person PersonToReturn = new Person();
            PersonToReturn.Deserialise(InputBytes);

            // retun the person
            return PersonToReturn;
        }

        public static void Test_WillOverwriteData()
        {
            long StartTime;
            long EndTime;
            TimeSpan TimeTaken;

            Console.WriteLine("-------------------------------------------------------------------");
            Console.WriteLine("*** Creating 2,000,000 test people... ");
            StartTime = DateTime.Now.Ticks;
            Person[] People = new Person[2000000];
            for (int i = 0; i < 2000000; i++)
            {
                People[i] = new Person();
                People[i].Firstname = "TestName." + i;
                People[i].Age = i;
            }
            EndTime = DateTime.Now.Ticks;
            TimeTaken = new TimeSpan(EndTime - StartTime);
            Console.WriteLine("-> Completed in " + TimeTaken.TotalSeconds + " seconds");

            Console.WriteLine("-------------------------------------------------------------------");
            Console.WriteLine("*** Serialising Collection to disk... ");
            StartTime = DateTime.Now.Ticks;
            SaveCollection(People);
            EndTime = DateTime.Now.Ticks;
            TimeTaken = new TimeSpan(EndTime - StartTime);
            Console.WriteLine("-> Completed in " + TimeTaken.TotalSeconds + " seconds");

            Console.WriteLine("-------------------------------------------------------------------");
            Console.WriteLine("*** Redundancy Test... ");
            StartTime = DateTime.Now.Ticks;
            bool Parsed = true;
            int FailedCount = 0;
            for (int i = 0; i < 2000000; i++)
            {
                if (GetValue(i).Age != i)
                {
                    Parsed = false;
                    FailedCount++;
                }
            }
            EndTime = DateTime.Now.Ticks;
            TimeTaken = new TimeSpan(EndTime - StartTime);
            Console.WriteLine("-> " + (Parsed ? "PARSED" : "FAILED (" + FailedCount + " failed index's"));
            Console.WriteLine("-> Completed in " + TimeTaken.TotalSeconds + " seconds");

            Console.WriteLine("-------------------------------------------------------------------");
            Console.WriteLine("*** Reading 10,000 index's at once... ");
            StartTime = DateTime.Now.Ticks;
            Person[] ChunkOfPeople = new Person[10000];
            for (int i = 0; i < 10000; i++)
                ChunkOfPeople[i] = GetValue(i);
            EndTime = DateTime.Now.Ticks;
            TimeTaken = new TimeSpan(EndTime - StartTime);
            Console.WriteLine("-> Completed in " + TimeTaken.TotalSeconds + " seconds");


            Console.WriteLine("-------------------------------------------------------------------");
            Console.WriteLine("*** Reading 100,000 index's at once... ");
            StartTime = DateTime.Now.Ticks;
            ChunkOfPeople = new Person[100000];
            for (int i = 0; i < 100000; i++)
                ChunkOfPeople[i] = GetValue(i);
            EndTime = DateTime.Now.Ticks;
            TimeTaken = new TimeSpan(EndTime - StartTime);
            Console.WriteLine("-> Completed in " + TimeTaken.TotalSeconds + " seconds");

            Console.WriteLine("-------------------------------------------------------------------");
            Console.WriteLine("*** Reading 1,000,000 index's at once... ");
            StartTime = DateTime.Now.Ticks;
            ChunkOfPeople = new Person[1000000];
            for (int i = 0; i < 1000000; i++)
                ChunkOfPeople[i] = GetValue(i);
            EndTime = DateTime.Now.Ticks;
            TimeTaken = new TimeSpan(EndTime - StartTime);
            Console.WriteLine("-> Completed in " + TimeTaken.TotalSeconds + " seconds");
        }
    }     
}
...