2011-06-15 73 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層」。如果是牆,則保存開始和結束位置以及紋理類型。如果在開闊場地中間有一個多面磚裝飾物,並且它的位置與故事無關,則將其隨機放置在場地中(並將隨機數發生器的種子保存在地圖文件中,以便下次它將會出現在同一個地方)。