20 milyon kiremitli harita oyunu hafızanın bitmesine neden olur, nasıl önlerim?


11

Çok büyük haritalar yüklenirken, yükleme yöntemi yeni bir harita döşemesi örneğinin oluşturulduğu bellek istisnasını atar. Tüm haritanın en azından sunucu uygulamasında (ve mümkünse istemcide) işlenmesini istiyorum. Bu sorunu nasıl çözmeliyim?

UPD: Buradaki soru, hala kullanabileceği boş hafıza olduğunda oyunun çökmesini nasıl durduracağı. Haritayı parçalar halinde bölmeye gelince, bu iyi bir yaklaşım, ancak benim durumumda istediğim şey değil.

UPD2: İlk başta çini sınıfının her yeni örneğine bir doku atadım ve bu da çok fazla bellek aldı (yükleme süresi). Şimdi yaklaşık dört kat daha az yer kaplıyor. Herkese teşekkürler, şimdi onları henüz parçalara ayırmayı düşünmeden devasa haritalar çalıştırabiliyorum.

UPD3: Döşeme özellikleri dizilerinin daha hızlı çalışıp çalışmadığını veya daha az bellek tüketip tüketmediklerini görmek için kodu yeniden yazdıktan sonra (nesne özellikleri olarak ilgili döşemelerde belirtilen özelliklerden daha fazla), sadece bunu denemek için çok zaman aldığımı değil, herhangi bir performans artışı getirmedi ve oyunu hata ayıklamak çok zorlaştırdı.


8
Sadece oyuncuların etrafındaki alanları yükleyin.
MichaelHouse

4
Karo başına 4 baytta 20 milyon karo sadece 80MB'dir - bu yüzden karolarınız çok küçük değildir. Size sihirli olarak daha fazla bellek veremeyiz, bu nedenle verilerinizi nasıl küçültebileceğimizi bulmamız gerekir. Şimdi, bize bu karolarda ne olduğunu gösterin.
Kylotan

Yükleme yöntemi ne işe yarar? Posta kodu, İçerik kanalını kullanıyor musunuz? Belleğe veya sadece meta verilere doku yüklüyor mu? Hangi meta veriler? XNA içerik türleri genellikle yönetilmeyen nesnelerin çok küçük olması için yönetilmeyen DirectX öğelerine başvurur, ancak yönetilmeyen tarafta da bir DirectX profil oluşturucu çalıştırmadığınız sürece görmediğiniz şeyler olabilir. stackoverflow.com/questions/3050188/…
Oskar Duveborn

2
Sistemin belleği yetersiz bir istisna atarsa, düşündüğünüze rağmen kullanılacak yeterli boş bellek yoktur. Ek belleği etkinleştirmek için verebileceğimiz gizli bir kod satırı olmayacak. Her döşemenin içeriği ve ayırma şekliniz önemlidir.
Kylotan

3
Boş hafızaya sahip olduğunuzu düşündüren nedir? 64 bit işletim sisteminde 32 bit bir işlem mi kullanıyorsunuz?
Dalin Seivewright

Yanıtlar:


58

1.5 Gb'ye ulaştığında uygulama kilitleniyor.

Bu, döşemelerinizi doğru bir şekilde temsil etmediğinizi kuvvetle önerir, çünkü bu, her döşemenin ~ 80 bayt boyutunda olduğu anlamına gelir.

Anlamanız gereken şey , bir döşemenin oyun konsepti ile kullanıcının gördüğü görsel döşeme arasında bir ayrım olması gerektiğidir . Bu iki kavram aynı şey değildir .

Örneğin Terraria'yı ele alalım. En küçük Terraria dünyası, 5 milyon fayans olan 4200x1200 fayans alır. Şimdi, o dünyayı temsil etmek için ne kadar bellek gerekiyor ?

Her karonun bir ön plan katmanı, bir arka plan katmanı (arka plan duvarları), tellerin gittiği bir "tel katmanı" ve mobilya öğelerinin gittiği bir "mobilya katmanı" vardır. Her kutucuk ne kadar bellek kaplar? Yine, sadece kavramsal olarak konuşuyoruz, görsel olarak değil.

Bir ön plan karosu, imzasız bir kısa sürede kolayca saklanabilir. 65536'dan fazla ön plan döşeme türü yoktur, bu nedenle bundan daha fazla bellek kullanmanın bir anlamı yoktur. Arka plan döşemeleri, 256'dan az farklı arka plan döşemesi olduğundan, işaretsiz bir baytta kolayca olabilir. Tel katman tamamen ikiliktir: bir kiremitin içinde bir tel vardır veya yoktur. Yani her karo için bir bit. Ve mobilya katmanı, olası kaç farklı mobilya parçasına bağlı olarak, yine işaretsiz bir bayt olabilir.

Döşeme başına toplam bellek boyutu: 2 bayt + 1 bayt + 1 bit + 1 bayt: 4 bayt + 1 bit. Bu nedenle, küçük bir Terraria haritası için toplam boyut 20790000 bayt veya ~ 20MB'dir. (not: bu hesaplamalar Terraria 1.1'e dayanmaktadır. Oyun o zamandan beri çok genişlemiştir, ancak modern Terraria bile fayans yeri başına 8 bayt veya ~ 40MB'ye sığabilir. Yine de tolere edilebilir).

Sen gerektiğini asla C # sınıfların diziler olarak depolanan bu temsilin. Tamsayı dizileri veya benzeri bir şey olmalıdırlar. AC # struct da çalışır.

Şimdi, bir haritanın bir parçasını çizmenin zamanı geldiğinde (vurguyu not edin), Terraria'nın bu kavramsal karoları gerçek karolara dönüştürmesi gerekiyor . Her döşemenin aslında bir ön plan görüntüsü, arka plan görüntüsü, isteğe bağlı bir mobilya görüntüsü seçmesi ve bir tel görüntüsüne sahip olması gerekir. XNA, çeşitli sprite sayfaları ve benzeri ile buraya gelir.

Yapmanız gereken, kavramsal haritanızın görünür kısmını gerçek XNA sprite sayfası döşemelerine dönüştürmektir. Sen dönüştürmek için çalışıyor olmamalıdır seferde şeyin tamamını . Sakladığınız her bir döşemenin "Ben X döşemeliyim" diyen bir dizin olması gerekir; burada X bir tamsayıdır. Görüntülemek için kullandığınız hareketli grafiği almak için bu tamsayı dizinini kullanırsınız. Ve bunu tek tek dörtlü çizmekten daha hızlı yapmak için XNA'nın sprite sayfalarını kullanırsınız.

Şimdi, döşemelerin görünür bölgesinin çeşitli parçalara bölünmesi gerekiyor, böylece kamera her hareket ettiğinde sürekli sprite sayfaları oluşturmuyorsunuz. Yani dünyanın sprite sayfaları olarak 64x64 parçalarına sahip olabilirsiniz. Dünyanın 64x64 parçaları hangisinin oynatıcının mevcut kamera konumundan görünür olursa olsun çizdiğiniz parçalardır. Diğer parçaların sprite sayfaları bile yoktur; bir yığın ekrandan düşerse, bu sayfayı dışarı atarsınız (not: gerçekten silmiyorsunuz; etrafında tutuyor ve daha sonra görünebilecek yeni bir yığın için saygı duyuyorsunuz).

Tüm haritanın en azından sunucu uygulamasında (ve mümkünse istemcide) işlenmesini istiyorum.

Kişisel sunucu biliyor ya karoların görsel sunum umurumda gerekmez. Tek dikkat etmesi gereken kavramsal temsildir. Kullanıcı buraya bir kutucuk eklediğinden kutucuk dizinini değiştirir.


4
+1 Mükemmel cevap. Benimkinden daha fazla oy gerekiyor.
MichaelHouse

22

Araziyi bölgelere veya parçalara ayırın. Sonra sadece oyuncuların görebildiği parçaları yükleyin ve olmayanları kaldırın. Bunu, bir ucunda topakları yüklediğiniz ve oyuncu ilerledikçe diğer ucundan çıkardığınız bir konveyör bandı gibi düşünebilirsiniz. Daima oyuncunun önünde kalmak.

Ayrıca örnekleme gibi hileler de kullanabilirsiniz. Bir türdeki tüm karoların bellekte sadece bir örneği varsa ve gerekli olan tüm konumlarda birden çok kez çizilirse.

Bunun gibi daha fazla fikir burada bulabilirsiniz:

Bunu nasıl yaptılar: Terraria'da milyonlarca fayans

Büyük bir kiremit haritasında ve zindanlarda 'bölgeleme'


Sorun böyle bir yaklaşım istemci uygulaması için iyi, ancak sunucu için iyi değil. Dünyadaki bazı karolar söndüğünde ve bir zincirleme reaksiyon başladığında (ve bu çok fazla olur) tüm harita bunun için hafızada olmalıdır ve hafızadan atıldığında bir sorundur.
user1306322

6
Her karo ne içerir? Döşeme başına ne kadar bellek kullanılır? Her biri 10 baytta bile sadece 190 megabayttasınız. Sunucu, dokuyu veya gerekli olmayan diğer bilgileri yüklememelidir. 20 milyon karo için bir simülasyona ihtiyacınız varsa, sadece zincir reaksiyonlarınız için gerekli bilgilere sahip bir simülasyon seti oluşturmak için bu karolardan mümkün olduğunca uzaklaşmanız gerekir.
MichaelHouse

5

Byte56'nın cevabı iyi ve bunu bir yorum olarak yazmaya başladım, ancak çok uzun sürdü ve bir cevapta daha yararlı olabilecek bazı ipuçları içeriyor.

Yaklaşım kesinlikle bir istemci için olduğu kadar sunucu için de iyidir. Aslında , sunucunun bir istemciden çok daha fazla alana bakması isteneceği göz önüne alındığında, muhtemelen daha uygundur. Sunucunun neyin yüklenmesi gerektiğinden farklı bir fikri vardır (bağlı tüm istemcilerin önem verdiği tüm bölgeleri önemser), ancak aynı şekilde çalışan bir bölge kümesini yönetebilir.

Böyle devasa bir haritaya (ve büyük olasılıkla yapamayacağınız) hafızaya sığsanız bile, istemezsiniz . Hemen kullanmak zorunda olmadığınız verilerin yüklenmesinde kesinlikle sıfır nokta vardır, verimsiz ve yavaştır. Belleğe sığabilseniz bile, büyük olasılıkla hepsini makul bir süre içinde işleyemezdiniz.

Belirli bölgelerin kaldırılmasına yönelik itirazınızın, dünya güncellemenizin tüm karoları yinelemesi ve işlemesi nedeniyle mi şüpheliyim? Bu kesinlikle geçerlidir, ancak yalnızca belirli parçaların yüklenmesini engellemez. Bunun yerine, bir bölge yüklendiğinde, güncelleştirmek için gözden kaçan güncellemeleri uygulayın. Bu, işlem açısından da çok daha verimlidir (küçük bir bellek alanına büyük bir çaba patlaması). Bölge sınırlarını aşabilecek herhangi bir işleme etkisi varsa (örn. Bir lav akışı veya başka bir bölgeye taşınacak bir su akışı), o zaman bu iki bölgeyi birbirine bağlayın, böylece biri yüklendiğinde diğeri de öyle. İdeal olarak bu durumlar en aza indirilir, aksi takdirde kendinizi 'tüm bölgeler her zaman yüklenmelidir' durumunda hızlı bir şekilde bulacaksınız.


3

Bir XNA oyununda kullandığım etkili bir yol:

  • her karo / nesne için bir görüntü değil sprite sayfaları kullanın ve sadece bu haritaya ihtiyacınız olanları yükleyin;
  • haritayı daha küçük bölümlere ayırın, haritanızın bu boyutun 4 katı ise 500 x 200 parçadan oluşan yığın haritaları veya haldle yapabileceğiniz bir şey söyleyin;
  • bu yığını belleğe yükleyin ve bir haritanın yalnızca görünür kısımlarını (örneğin, görünür karolar ve oyuncunun hareket ettiği her yön için 1 karo) bir ekran dışı tamponuna boyayın;
  • görünümü temizleyin ve pikselleri tampondan kopyalayın (buna çift tampon denir ve hareketi düzeltir);
  • karakterinizi hareket ettirdiğinizde, haritanın tomurcuklarını izleyin ve yaklaştığında bir sonraki parçayı yükleyin ve geçerli olana ekleyin;
  • oyuncu bu yeni haritada tamamen bulunduğunda, son haritayı kaldırabilirsiniz.

Eğer çok büyük değilse tek bir büyük haritayı da kullanabilirsiniz ve sunulan mantığı kullanabilirsiniz.

Tabii ki dokularınızın boyutu dengelenmeli ve çözünürlük bu konuda büyük bir bedel ödüyor. Daha düşük bir çözünürlükte çalışmayı deneyin ve gerekirse bir yapılandırma ayarı olarak ayarlanmışsa, yüklemek için yüksek çözünürlüklü bir paket hazırlayın.

Her seferinde bu haritanın yalnızca ilgili bölümlerini döngüye sokmanız ve yalnızca gerekli olanları oluşturmanız gerekir. Bu şekilde performansı çok geliştireceksiniz.

Bir sunucu büyük bir haritayı daha hızlı işleyebilir, çünkü oyunu (en yavaş işlemlerden biri) yapmak zorunda değildir, bu yüzden mantığı daha büyük bir parça veya tüm haritanın kullanımı olabilir, ancak sadece mantığı işleyebilir Oyuncunun etrafındaki oyuncu görünüm alanının 2 katı gibi.

Burada bir tavsiye, hiçbir şekilde oyun geliştirmede uzman olmam ve oyunumun mezuniyetimin final projesi için yapıldığı (en iyi puanı aldım), bu yüzden her noktada o kadar doğru olmayabilirim, ama ben bilgi ve becerileri toplamak için http://www.gamasutra.com ve XNA yaratıcıları sitesi creators.xna.com (şu anda http://create.msdn.com ) gibi web ve siteleri araştırdı ve benim için iyi çalıştı .


2

ya

  • daha fazla bellek satın al
  • veri yapılarınızı daha kompakt bir şekilde temsil edin
  • tüm verileri bir kerede hafızada tutmayın.

Ben win7 64bit 8 Gb ram var ve 1.5Gb ulaştığında uygulama çöküyor. Yani bir şey eksik olmadıkça daha fazla koç satın almanın bir anlamı yok.
user1306322

1
@ user1306322: 20 milyon kutunuz varsa ve yaklaşık ~ 1.5GB bellek kaplıyorsa, bu kutucuklarınızın kutucuk başına yaklaşık 80 bayt olduğu anlamına gelir . Bu şeylerde tam olarak ne saklıyorsunuz?
Nicol Bolas

Dediğim gibi @NicolBolas, birkaç ints, bayt ve bir texture2d. Ayrıca hepsi 1.5GB almıyor, uygulama bu bellek maliyetine ulaştığında çöküyor.
user1306322

2
@ user1306322: Bu hala olması gerekenden çok daha fazlası. A ne kadar büyük texture2d? Her döşemenin benzersiz bir döşemesi var mı yoksa dokuları paylaşıyorlar mı? Ayrıca, bunu nerede söyledin? Sorunuzda kesinlikle bilgi vermediniz, bu da bilginin nerede olması gerektiği. Özel durumlarınızı daha iyi açıklayınız, lütfen.
Nicol Bolas

@ user1306322: Açıkçası, cevabımın neden reddedildiğini tam olarak anlamıyorum. Bellek yetersiz durumlara yönelik üç çare saydım - elbette, bunların hepsinin mutlaka geçerli olduğu anlamına gelmez . Ama yine de doğru olduklarını düşünüyorum. Yine de, sanal makinelerin durumunu dahil etmek için "satın al" yerine "hafızanızı uzatın" yazmalıydım. Cevabım ayrıntı yerine özlü olduğu için indirilmiyor muyum? Bence bunlar daha fazla açıklama yapılmadan anlaşılabilecek kadar basittir ?!
Thomas

1

burada soru, hala kullanabileceği boş hafıza olduğunda oyunun çökmesini nasıl durduracağıdır. Haritayı parçalar halinde bölmeye gelince, bu iyi bir yaklaşım, ancak benim durumumda istediğim şey değil.

Güncellemenize yanıt olarak, dizileri Int32.MaxValueboyut olarak ayıramazsınız . Onu parçalara bölmelisin. Hatta dizi benzeri bir cepheyi ortaya çıkaran bir sarmalayıcı sınıfta bile kapsülleyebilirsiniz:

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.