Tilemap verilerini depolamanın iyi bir yolu nedir?


13

Bazı üniversitelerle bir 2D platform geliştiriyorum. Döşeme haritasını saklamak için .txt dosyalarını kullanan XNA Platformer Başlangıç ​​Seti'ni temel aldık. Bu basit olsa da, seviye tasarımı ile bize yeterince kontrol ve esneklik sağlamaz. Bazı örnekler: birden çok içerik katmanı için birden fazla dosya gerekir, her nesne ızgaraya sabitlenir, nesnelerin döndürülmesine, sınırlı sayıda karaktere vb. İzin vermez. ve harita dosyası.

Bu, oyun sırasında kullanılacak veri yapısını değil, yalnızca karo haritalarının dosya sistemi depolamasını ilgilendirir. Döşeme haritası bir 2D diziye yüklenir, bu nedenle bu soru diziyi hangi kaynağın dolduracağı ile ilgilidir.

DB için akıl yürütme: Benim bakış açımdan, döşeme veri depolamak için bir veritabanı kullanarak daha az veri artıklığı görüyorum. Aynı özelliklere sahip aynı x, y pozisyonundaki karolar seviyeden seviyeye tekrar kullanılabilir. Veritabanından belirli bir düzeyde kullanılan tüm karoları almak için bir yöntem yazmak için yeterince basit gibi görünüyor.

JSON / XML için gerekçe: Görsel olarak düzenlenebilir dosyalar, değişiklikler SVN üzerinden çok daha kolay takip edilebilir. Ancak tekrarlanan içerik var.

Ya diğerine kıyasla herhangi bir dezavantajı (yükleme süreleri, erişim süreleri, bellek vb.) Var mı? Ve endüstride yaygın olarak kullanılan nedir?

Şu anda dosya şöyle görünüyor:

....................
....................
....................
....................
....................
....................
....................
.........GGG........
.........###........
....................
....GGG.......GGG...
....###.......###...
....................
.1................X.
####################

1 - Oyuncu başlangıç ​​noktası, X - Seviye Çıkış,. - Boş alan, # - Platform, G - Mücevher


2
Hangi mevcut "biçimi" kullanıyorsunuz? Sadece "metin dosyaları" demek, ikili verileri kaydetmediğiniz anlamına gelir. "Yeterli kontrol ve esneklik yok" derseniz, özellikle karşılaştığınız sorunlar nelerdir? Neden XML ve SQLite arasındaki ilişki? Bu cevabı belirleyecektir. blog.stackoverflow.com/2011/08/gorilla-vs-shark
Tetrad

Daha okunabilir olduğu için JSON'u tercih ederim.
Den

3
İnsanlar neden bu şeyler için SQLite kullanmayı düşünüyor? Bu bir var ilişkisel veritabanı ; insanlar neden ilişkisel bir veritabanının iyi bir dosya formatı oluşturduğunu düşünüyor?
Nicol Bolas

1
@StephenTierney: Neden bu soru aniden XML ve SQLite olmaktan JSON'a ve herhangi bir veri tabanına kadar gitti? Sorum şu: Neden sadece "Tilemap verilerini depolamanın iyi bir yolu nedir?" Bu X ve Y soruları keyfi ve anlamsızdır.
Nicol Bolas

1
@Kylotan: Ama çok iyi çalışmıyor. Veritabanını yalnızca SQL komutları ile düzenleyebileceğiniz için düzenlemek inanılmaz zor. Tablolar bir tilemapa bakmanın ve neler olduğunu anlamanın etkili bir yolu olmadığından okumak zor. Ve arama yapabilirken, arama prosedürü inanılmaz derecede karmaşıktır. Bir şeylerin işe yaraması önemlidir, ancak oyunu gerçekten daha zor geliştirme süreci yapacaksa, kısa yolu izlemeye değmez.
Nicol Bolas

Yanıtlar:


14

Güncellenmiş sorunuzu okurken, yükleme süreleri ve endüstrinin kullandığı şeylerle ilgili ikincil endişelerle, diskteki "gereksiz veriler" ile ilgili en fazla endişe duyduğunuz gibi görünüyor.

Öncelikle, gereksiz veriler için neden endişeleniyorsunuz? XML gibi şişirilmiş bir format için bile, sıkıştırma uygulamak ve seviyelerinizin boyutunu düşürmek oldukça önemsiz olacaktır. Düzey verilerinizden daha fazla doku veya sesle yer kaplama olasılığınız daha yüksektir.

İkincisi, bir ikili biçim, ayrıştırmak zorunda olduğunuz metin tabanlı bir biçime göre daha hızlı yüklenecektir. Şüphesiz eğer onu hafızaya bırakabilir ve veri yapılarınızı orada tutabilirsiniz. Ancak, bunun bazı dezavantajları vardır. Birincisi, ikili dosyaların hata ayıklaması imkansızdır (özellikle içerik oluşturma insanlar için). Bunları değiştiremez veya versiyonlandıramazsınız. Ancak daha küçük olacaklar ve daha hızlı yüklenecekler.

Bazı motorların yaptığı (ve ideal bir durum olarak düşündüğüm) iki yükleme yolu uygulamak. Geliştirme için bir tür metin tabanlı format kullanırsınız. Sağlam bir kitaplık kullandığınız sürece hangi biçimi kullandığınız gerçekten önemli değildir. Sürüm için ikili (daha küçük, daha hızlı yükleme, muhtemelen hata ayıklama öğelerinden arındırılmış) bir sürüm yüklemeye geçersiniz. Seviyeleri düzenleme araçlarınız her ikisini de tükürür. Tek bir dosya biçiminden çok daha fazla iş, ancak her iki dünyanın da en iyisini elde edebilirsiniz.

Tüm söylenenler, sanırım silahı biraz atlıyorsunuz.

Bu sorunların ilk adımı, her zaman karşılaştığınız sorunu ayrıntılı olarak açıklamaktır. Hangi verilere ihtiyacınız olduğunu biliyorsanız, hangi verileri depolamanız gerektiğini bilirsiniz. Oradan test edilebilir bir optimizasyon sorusu var.

Test etmek için bir kullanım durumunuz varsa, önemli olduğunu düşündüğünüz şeyi (yükleme süresi, bellek kullanımı, disk boyutu vb.) Ölçmek için birkaç farklı veri depolama / yükleme yöntemini test edebilirsiniz. Hiçbirini bilmeden, daha spesifik olmak zordur.


9
  • XML: Elle düzenlemek kolaydır, ancak çok fazla veri yüklemeye başladığınızda yavaşlar.
  • SQLite: Bu, aynı anda çok sayıda veri almak ya da sadece küçük parçalar almak istiyorsanız daha iyi olabilir. Ancak, bunu oyununuzda başka bir yerde kullanmıyorsanız, haritalarınız için aşırı yüklü olduğunu düşünüyorum (ve muhtemelen aşırı karmaşık).

Benim önerim özel bir ikili dosya biçimi kullanmaktır. SaveMapOyunumla, her alanı kullanarak ve kullanarak bir yöntemim var BinaryWriter. Bu ayrıca isterseniz sıkıştırmayı seçmenizi sağlar ve dosya boyutu üzerinde daha fazla kontrol sağlar. Yani 32767'den daha büyük olmayacağını biliyorsanız, bunun shortyerine kaydedin int. Büyük bir döngüde, bir şey shortyerine bir şeyi kaydetmek intçok daha küçük bir dosya boyutu anlamına gelebilir.

Ayrıca, bu rotaya giderseniz, dosyadaki ilk değişkeninizin bir sürüm numarası olmasını öneririm.

Örneğin, bir harita sınıfını düşünün (çok basitleştirilmiş):

class Map {
    private const short MapVersion = 1;
    public string Name { get; set; }

    public void SaveMap(string filename) {
        //set up binary writer
        bw.Write(MapVersion);
        bw.Write(Name);
        //close/dispose binary writer
    }
    public void LoadMap(string filename) {
        //set up binary reader
        short mapVersion = br.ReadInt16();
        Name = br.ReadString();
        //close/dispose binary reader
    }
}

Şimdi, size Haritaya yeni özellik eklemek istedim diyelim, bir söylemek Listait Platformnesneler. Bunu seçtim çünkü biraz daha ilgili.

Her şeyden önce, aşağıdakileri artırır MapVersionve eklersiniz List:

private const short MapVersion = 2;
public string Name { get; set; }
public List<Platform> Platforms { get; set; }

Ardından, kaydetme yöntemini güncelleyin:

public void SaveMap(string filename) {
    //set up binary writer
    bw.Write(MapVersion);
    bw.Write(Name);
    //Save the count for loading later
    bw.Write(Platforms.Count);
    foreach(Platform plat in Platforms) {
        //For simplicities sake, I assume Platform has it's own
        // method to write itself to a file.
        plat.Write(bw);
    }
    //close/dispose binary writer
}

Ardından, avantajı gerçekten gördüğünüz yer, yükleme yöntemini güncelleyin:

public void LoadMap(string filename) {
    //set up binary reader
    short mapVersion = br.ReadInt16();
    Name = br.ReadString();
    //Create our platforms list
    Platforms = new List<Platform>();
    if (mapVersion >= 2) {
        //Version is OK, let's load the Platforms
        int mapCount = br.ReadInt32();
        for (int i = 0; i < mapCount; i++) {
            //Again, I'm going to assume platform has a static Read
            //  method that returns a Platform object
            Platforms.Add(Platform.Read(br));
        }
    } else {
        //If it's an older version, give it a default value
        Platforms.Add(new Platform());
    }
    //close/dispose binary reader
}

Gördüğünüz gibi, LoadMapsyöntem sadece haritanın en son sürümünü değil, aynı zamanda eski sürümleri de yükleyebilir! Eski haritaları yüklediğinde, kullandığı varsayılan değerler üzerinde kontrol sahibi olursunuz.


9

Kısa hikaye

Bu soruna düzgün bir alternatif, seviyelerinizi her karo için bir piksel olan bir bitmapte saklamaktır. RGBA kullanarak bu kolayca dört farklı boyutun (katmanlar, kimlikler, döndürme, renk tonu, vb.) Tek bir görüntüde saklanmasına izin verir.

Uzun Hikaye

Bu soru, Notch'in Ludum Dare'in birkaç ay önce canlı akışını ne zaman yaptığını hatırlattı ve ne yaptığını bilmemeniz durumunda paylaşmak istiyorum. Gerçekten ilginç olduğunu düşündüm.

Temel olarak, seviyelerini saklamak için bitmap'leri kullandı. Bitmapteki her piksel, dünyadaki bir "döşemeye" karşılık geliyordu (kasvetli bir oyun olduğu için gerçekten bir kiremit değil, ancak yeterince yakın). Seviyelerinden birine bir örnek:

resim açıklamasını buraya girin

RGBA (kanal başına 8 bit) kullanıyorsanız, her kanalı farklı bir katman olarak ve her biri için 256 adede kadar döşeme kullanabilirsiniz. Bu yeterli olur mu? Veya örneğin, kanallardan biri, sizin gibi döşemenin rotasyonunu tutabilir. Ayrıca, bu formatla çalışmak için bir seviye editörü oluşturmak da oldukça önemsiz olmalıdır.

Ve en iyisi, herhangi bir özel içerik işlemcisine bile ihtiyacınız yok, çünkü diğer herhangi bir Texture2D gibi XNA'ya yükleyebilir ve pikselleri bir döngüde okuyabilirsiniz.

IIRC, bir düşmanı işaret etmek için haritada saf kırmızı bir nokta kullandı ve bu piksel için alfa kanalının değerine bağlı olarak düşmanın türünü belirleyecekti (örneğin, 255'in alfa değeri bir yarasa olabilirken 254 bir zombi falan).

Başka bir fikir, oyun nesnelerinizi bir ızgarada sabitlenmiş olanlara ve fayanslar arasında "ince" hareket edebilenlere bölmek olacaktır. Sabit döşemeleri bitmap'te ve dinamik nesneleri bir listede tutun.

Daha fazla bilgiyi bu 4 kanala çoğaltmanın bir yolu bile olabilir. Eğer birisi bu konuda bir fikre sahipse, bana bildirin. :)


3

Muhtemelen neredeyse her şeyden kurtulabilirsiniz. Ne yazık ki modern bilgisayarlar o kadar hızlıdır ki, muhtemelen nispeten küçük olan bu veri, okunması gereken büyük miktarda işlem gerektiren bir şişirilmiş ve kötü yapılandırılmış veri biçiminde saklansa bile, fark edilir bir zaman farkı yaratmaz.

"Doğru" şeyi yapmak istiyorsanız, Drackir gibi bir ikili biçim yaparsınız, ancak aptalca bir nedenden dolayı başka bir seçim yaparsanız, muhtemelen geri dönmeyecek ve sizi ısırmayacaktır (en azından performans açısından akıllıca değil).


1
Evet, kesinlikle boyuta bağlı. Yaklaşık 161 000 fayanslık bir harita var. Her birinin 6 katmanı vardır. Başlangıçta XML'de vardı, ancak 82MB'dı ve yüklenmesi sonsuza dek sürdü . Şimdi bir ikili biçimde kaydediyorum ve sıkıştırmadan önce yaklaşık 5mb ve yaklaşık 200ms yükler. :)
Richard Marskell - Drackir

2

Şu soruya bakın: Oyun verileri için 'İkili XML'? Ayrıca JSON veya YAML veya hatta XML'i tercih ederdim, ancak oldukça fazla yükü vardır. SQLite gelince, bunu asla seviyeleri saklamak için kullanmam. İlişkisel bir veritabanı, ilgili çok fazla veri depolamayı (örneğin, ilişkisel bağlantılara / yabancı anahtarlara sahip) ve çok sayıda farklı sorgu sormayı bekliyorsanız, tilemap'ları depolamanın bu tür işleri yapacağı görülüyorsa kullanışlıdır. şeyler ve gereksiz karmaşıklık katacaktı.


2

Bu sorunun temel sorunu, birbiriyle ilgisi olmayan iki kavramı birleştirmesidir:

  1. Dosya depolama paradigması
  2. Verilerin bellek içi gösterimi

Bazı keyfi biçimde düz metin olarak dosyalarınızı saklayabilir, JSON, bunun Lua komut dosyası, XML, keyfi bir ikili biçim vb Ve hiçbiri olacaktır gerektiren bu verilerin bellek içindeki temsili herhangi bir formda yapılabilir.

Dosya depolama paradigmasını bellek içi sunumunuza dönüştürmek, seviye yükleme kodunuzun görevidir. Örneğin, "Döşeme verilerini depolamak için bir veritabanı kullanarak daha az veri yedeklemesi görüyorum" diyorsunuz. Daha az yedeklilik istiyorsanız, bu seviye yükleme kodunuzun bakması gereken bir şeydir. Bu, bellek içi temsilinizin işlemesi gereken bir şeydir.

Öyle değil dosya formatı ihtiyaçları ile ilgili gereken bir şey. İşte nedeni: bu dosyalar bir yerden gelmek zorunda.

Ya bunları elle yazacaksınız ya da seviye verilerini yaratan ve düzenleyen bir tür araç kullanacaksınız. Bunları el ile yazıyorsanız, ihtiyacınız olan en önemli şey, okunması ve değiştirilmesi kolay bir formattır. Eğer en geçirecek çünkü Veri fazlalık, biçim bile düşünmeye ihtiyacı şey değildir senin dosyalarını düzenleme zaman. Veri yedeklemesi sağlamak için bulduğunuz mekanizmaları elle kullanmak zorunda mısınız? Seviye yükleyicinizin idare etmesi zamanınızı daha iyi kullanmaz mı?

Bunları oluşturan bir aracınız varsa, gerçek biçim yalnızca okunabilirlik adına önemlidir (en iyi ihtimalle). Bu dosyalara herhangi bir şey koyabilirsiniz . Dosyadaki gereksiz verileri atlamak istiyorsanız, bunu yapabilen bir biçim tasarlamanız ve aracınızın bu yeteneği doğru şekilde kullanmasını sağlamanız yeterlidir. Kişisel tilemap biçimi çünkü hiç sıkıştırma teknikleri İstediğinizi, RLE (çalışma uzunluğu şifreleme) sıkıştırma, çoğaltılması sıkıştırma içerebilir senin tilemap biçimi.

Sorun çözüldü.

İlişkisel Veritabanları (RDB), birçok veri alanı içeren büyük veri kümeleri üzerinde karmaşık arama yapma sorununu çözmek için mevcuttur. Bir karo haritasında yaptığınız tek arama "X, Y konumunda karo alın" dır. Herhangi bir dizi bunu halledebilir. Tilemap verilerini saklamak ve korumak için ilişkisel bir veritabanı kullanmak aşırı derecede aşırı olacaktır ve gerçekten hiçbir şeye değmez.

Bellek içi sunumunuzda bir miktar sıkıştırma oluştursanız bile, yine de performans ve bellek ayak izi açısından RDB'leri kolayca yenebilirsiniz. Evet, aslında bu sıkıştırmayı uygulamanız gerekecek. Ancak bellek içi veri boyutu, bir RDB'yi dikkate alacağınız bir endişe ise, muhtemelen belirli sıkıştırma türlerini uygulamak istersiniz. Aynı anda RDB'den daha hızlı ve bellekte daha düşük olursunuz.


1
  • XML: kullanımı gerçekten basit, ancak yapı derinleştiğinde ek yük rahatsız edici oluyor.
  • SQLite: daha büyük yapılar için daha uygun.

Gerçekten basit veriler için, XML yükleme ve ayrıştırma konusunda zaten bilginiz varsa, muhtemelen XML yoluna giderdim.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.