2

私はここ数日、xnaゲームの1億4400万のタイル表現を保存時に非常に小さいサイズに圧縮する方法に取り組んできました。なんとかそれをやってのけることができたので、私は今、それらをファイルからチャンクで取り戻す方法に困惑していることに気づきました。

私が持っているファイルに。

  1. 整数(7BitEncodedIntメソッドを使用してバイトに圧縮されます)
  2. バイト

圧縮された整数はタイルの数を表し、後続のバイトによってタイルのタイプが決まります。これはすべてうまく機能し、本当にうまく機能します。最も重要なことは、ファイルサイズを平均でわずか50MBに縮小することです。

問題は、現在ファイル全体を読み戻していることです。ファイルから私はこれを取得しています。

  1. 各タイルのインデックス値(タイルを取得するときの基本的な反復)
  2. バイト値としての各タイルのタイプ
  3. そのタイルのテクスチャを表すバイト値(これを説明するのは難しいですが、タイルごとに必要です)

このすべての最終結果は、私がファイルを保存することに成功し、約50MBしか使用しないことです。しかし、すべてをロードして戻すと、RAM上で約1.5ギガに拡張されます。もうタイル情報を犠牲にする余裕はありません。そのため、プレーヤーの場所に基づいてマップの一部のみをロードする方法が必要です。目標は、100〜200mbの範囲にあることです

私は、クワッドツリーを使用してファイルをメモリマッピングすることを検討してきました。これは、ファイルをチャンクでロードするために見つけることができるほとんどすべてのものです。これらのオプションはすべてかなり良いように見えますが、どれが最適かはわかりません。状況を考えると、さらに良いオプションがあるかもしれません。これらすべてのもう1つの問題は、これらのソリューションがすべて非常に複雑に見えることです(特に、これが初めて使用するため)。長いコーディングに専念することに反対しているわけではありませんが、それが私が行うことを実行することを知りたいです。事前にそれが必要です。

私の質問は、ファイルをプルするときにファイルをどのように処理する必要があるか、そしてプレーヤーの場所に基づいてファイルを処理する必要があるという事実を考えると、これを行うための最良の方法は何でしょうか?ここで方向性を探しています。コードはいつでも歓迎しますが、必須ではありません。

4

2 に答える 2

0

いくつかのオプションがありますが、それらのすべてが特定のプロジェクトに適しているとは限りません。

  • すべてのデータに単一のファイルを使用しないでください。マップを小さな「部屋」に分割し、それぞれを独自のファイルに保存します。プレーヤーが開始する「部屋」のみをロードし、隣接する「部屋」をプリエンプティブにロードして、古い部屋をアンロードします。
  • 保存する必要のあるタイルの数を減らします。手続き型生成を使用して、エリアのレイアウトを作成します。床が単一のタイルタイプでできている10x10の部屋がある場合は、100個の個別のタイルを保管せず、代わりに「このエリアにはこのタイルの床が10x10あります」という特定のマーカーを使用します。壁の場合は、開始位置と終了位置、およびテクスチャタイプを保存します。オープンフィールドの真ん中にマルチタイルのドゥーダッドがあり、その位置がストーリーに関連していない場合は、フィールドにランダムに配置します(そして、乱数ジェネレーターのシードをマップファイルに保存して、次回は同じ場所に表示されます)。
于 2011-06-19T08:35:37.693 に答える
0

Tile クラスに固定長の変数を持ち、次のようなものを実装したいとします。

これは、ファイルにシリアル化されたコレクションからインデックスに基づいて値を取得できるコレクション クラス (People) の例です。

Person は 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");
        }
    }     
}
于 2011-06-19T08:00:57.683 に答える