2011-06-15 74 views
2

我一直在为最后几天的工作方法,以保存时将我的xna游戏的144百万磁贴表示压缩为非常小的尺寸。设法解决这个问题后,我现在发现自己难以应付如何从文件中将它们从大块中分离出来。请求方向加载我的文件块(只需要建议)

在我有的文件中。

  1. 一个整数(它被压缩到使用7BitEncodedInt方法字节)
  2. 字节

压缩整数表示的片的数目和随后的字节确定什么类型的砖。这一切都很好,并且运作得非常好。最重要的是,它将文件大小平均缩小到只有50mb。

问题是我正在读回整个文件。 从文件我得到这个。

  1. 每一瓦片的索引值(只是一个基本迭代作为我抢瓦片)
  2. 类型每一瓦片为字节值
  3. 表示用于该图块的纹理的一个字节值(这是很难解释,但它的必要基于每个瓷砖)

所有这一切的最终结果是,我设法保存文件,只使用约50MB。但是通过重新装载整个东西,扩展到了将近1.5G的内存。我无法承担牺牲任何瓦信息。所以我需要一种只根据玩家位置加载地图部分的方法。目标是在100-200mb范围内

我一直在寻找内存映射文件,使用quadtrees,几乎所有我能找到的用于加载文件的块。虽然这些选项似乎都很不错,但我不确定哪个是最好的,或者如果考虑到情况,可能会有另一个更好的选项。所有这些问题的另一个问题是,这些解决方案似乎都很涉及(尤其是因为这是我第一次使用它们),虽然我并不反对把自己投入一些冗长的编码,但我想知道它会做我所做的需要它之前。

我的问题是,鉴于我在处理文件时需要如何处理文件,并且需要根据播放器位置来完成这一事实,那么最好的方法是什么?我只是在这里寻找一些方向。代码总是受欢迎但不是必需的。

+0

在我头顶,jst看看你是否可以优化你存储在文件中的数据和你想传输的数据。把它分成几块,只发送需要的东西。对于网络传输,发送已更改并主要保存在本地计算机本身。对不起,如果这不是你要找的答案。 – Zenwalker 2011-06-15 04:56:05

回答

0

你想拥有固定长度的变量在瓷砖类,并实现类似这样的:

这是一个集合类的(人),可以得到基于指数的集合,它是序列化的值的例子成文件。

人是人类集合的基础类。

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"); 
     } 
    }  
} 
0

有多种选择,不是所有的人可能适合你的特定项目:

  • 不要使用单个文件的所有数据。将地图分成较小的“房间”并将每个地图存储在自己的文件中。只加载玩家启动的“房间”,并抢先装载邻近的“房间”并卸载旧房间。
  • 减少您需要存储的瓷砖数量。使用程序生成来创建区域的布局。 如果您有一个10x10的房间,地板由单一瓷砖类型制成,则不要存储100个单独的瓷砖,而是使用特定的标记,指出“此区域具有此瓷砖的10x10层”。如果是墙,则保存开始和结束位置以及纹理类型。如果在开阔场地中间有一个多面砖装饰物,并且它的位置与故事无关,则将其随机放置在场地中(并将随机数发生器的种子保存在地图文件中,以便下次它将会出现在同一个地方)。