Çok sayıda küp ile çalışma. Performansı arttırmak?


12

Düzenleme: Soruyu özetlemek için, kötü performanstan muzdarip bir voksel tabanlı dünya (Minecraft tarzı (teşekkürler komünist ördek)) var. Kaynakta olumlu değilim ama ondan nasıl kurtulacağına dair olası herhangi bir tavsiye istiyorum.

Bir dünyanın büyük miktarda küplerden oluştuğu bir proje üzerinde çalışıyorum (size bir sayı vereceğim, ancak kullanıcı tanımlı dünyalar). Benim testim yaklaşık (48 x 32 x 48) blok.

Temelde bu bloklar kendi başlarına hiçbir şey yapmazlar. Sadece orada oturuyorlar.

Oyuncu etkileşimi söz konusu olduğunda kullanılmaya başlarlar.

Kullanıcı fare hangi fare ile etkileşim (fare üzerinde, tıklama, vb) ve oyuncu hareket ederken çarpışma tespit için kontrol etmek gerekir.

Şimdi her blokta büyük bir gecikme yaşadım.

Tüm bloklarda döngü yaparak ve hangi blokların karakterin belirli bir aralığı içinde olduğunu bularak ve sonra sadece çarpışma tespiti için bu bloklar arasında döngü yaparak bu gecikmeyi azaltmayı başardım.

Ancak, hala iç karartıcı bir 2fps gidiyorum.

Bu gecikmeyi nasıl azaltabileceğim konusunda başka fikri olan var mı?

Btw, XNA (C #) kullanıyorum ve evet, 3d.


Şunu mu demek istediniz: Minecraft benzeri? Yani, voksel mi?
Komünist Ördek

4
Oktrees'e baktın mı? en.wikipedia.org/wiki/Octree
bummzack

1
Oyununu profillemeyi denedin mi? En fazla zamanın harcandığı bazı kilit alanları gösterebilir. Düşündüğünüz gibi olmayabilir.
yavaşladı

2
Her küpün 6 ​​yüzünü de çizmektense, yalnızca hiçbir şeyle temas etmeyen yüzleri çizebilirsiniz
David Ashmore

1
@David: Evet, ya da önce küp başına tek bir çizim çağrısı yapmayı bırakabilir ve daha sonra bireysel çokgenler hakkında endişelenebilir.
Olhovsky

Yanıtlar:


20

Görünüşe göre Ağaçlar hakkında bilgi edinmek istiyorsun!

Ve ciddi oluyorum, şu anda tüm küplerinizin bir dizisi üzerinde döngü yapıyorsanız, gerçekten çeşitli uzamsal veri yapılarına bakmalısınız. Bu durumda, küp dünyanızı yeniden hayal etmenin en iyi yolu bir ağaçtır.

Nedeninin nedenlerine girmeden önce sorunumuzu düşünelim. Mümkün olduğunca az maliyetle, oyuncunun çarpışabileceği yakındaki küplerin bir listesini alabileceğimiz bir çözüm arıyoruz. Bu liste olabildiğince küçük, ancak kesin olmalıdır.

Şimdi bu bölgeyi belirlemek için, oyuncumuzun koordinat alanını küp haritasının koordinat alanına eşlememiz gerekiyor; yani, oynatıcının kayan nokta konumunu çok boyutlu küp dizisinin ayrı bir dizinine eşlememiz gerekir (örnek gösterimi world[31][31][31], yani 64 * 64 * 64 çok boyutlu bir dizinin tam ortası olabilir ).

Sadece aynı yakındaki indekslemeyi kullanarak çevreleyen blokları hesaplayabiliriz, belki de sadece yakındaki küpleri örnekleyebiliriz, ancak bu yine de sürekli yeniden hesaplama gerektirir ve yerleşimde ayrık olmayan herhangi bir nesneye izin vermez (yani küple eşleşmeyebilir) ) map.

İdeal durum, küp haritamızın belirli bölümleri için küplerimizi içeren, bir dizi eşit şekilde bölünmüş bir kova kümesidir, bu nedenle çevreleyen alanı yeniden hesaplamak yerine, bu bölgelere girip çıkıyoruz . Önemsiz herhangi bir hesaplama için, verilerimizi böyle tutmak tüm küpleri ve sadece yakındaki bu bireysel kümeleri yinelemeyi ortadan kaldırabilir.

Soru şudur: Bunu nasıl uygularız?

64 * 64 * 64 dünyası için 8 * 8 * 8 bölgeye ayrıldığını hayal edin . Bu, dünyanızda eksen başına 8 bölge (X, Y, Z) olacağı anlamına gelir. Bu bölgelerin her biri, bu yeni basitleştirilmiş dizin tarafından kolayca alınabilen 8 küp içerecektir.

Yakınınızdaki bir dizi küp üzerinde bir işlem yapmanız gerekiyorsa, dünyanızdaki her küpü tekrarlamak yerine, bu bölgeler üzerinde yineleyerek orijinal 64 * 64 * 64 (262144) 'den maksimum yineleme sayısını sadece 520 (8 * 8 * 8 + 8).

Şimdi bu bölgeler dünyasından uzaklaşın ve bölgeleri daha büyük süper bölgelere yerleştirin ; burada her süper bölge 2 x 2 x 2 normal bölge içerir . Dünyanız şu anda 512 (8 * 8 * 8) içerdiği gibi bölgeler , biz 8 * 8 * 8 kırabilir bölgeleri 64 (4 * 4 * 4) içine süper bölgeleri 8 bölerek bölgeleri 2 tarafından bölgeleri başına süper bölge . Yukarıdan aynı mantığı uygulamak, süper bölgeyi bulmak için maksimum yinelemeleri 512'den 8'e düşürür ; ve daha sonra 64 maksimum işlem bulmak için bölgeyi(toplam maksimum 72)! Bunun sizi zaten çok fazla yinelemeden nasıl kurtardığını görebilirsiniz (262144: 72).

Eminim şimdi ağaçların ne kadar yararlı olduğunu görebilirsiniz. Her bölge , ağaç üzerindeki bir daldır ve her süper bölge bir önceki dal olarak bulunur. İhtiyacınız olanı bulmak için sadece ağacı geziyorsunuz; toplam maliyeti en aza indirmek için daha küçük veri kümeleri kullanmak.

Aşağıdaki şema konsepti görselleştirmenize yardımcı olacaktır. ( Wikipedia'dan görsel : Octrees ): octree

Yasal Uyarı:

Voksel dünyanızın sabit boyutlu çok boyutlu bir dizide düzenlendiği yukarıdaki gibi ideal bir kurulumda, sadece oyuncu konumunu sorgulayabilir, daha sonra çevreleyen blokları O (1) maliyetle endeksleyebilirsiniz! (Bkz. Olhovskys açıklaması) Ama dünyanızın bir voksel oyununda nadiren sabit büyüklükte olduğunu düşünmeye başladığınızda bu daha zor hale gelir; ve tüm süper bölgeleri HDD'den belleğe yükleyebilmek için veri yapınıza ihtiyacınız olabilir . Sabit boyutlu çok boyutlu bir dizinin aksine, ağaçlar kombine algoritmalar üzerinde çok fazla zaman harcamadan kolayca buna izin verir.


Anladım diyorum ama maalesef bilmiyorum. Blokların çarpışma kutuları hareket etmiyor, sadece oyuncunun çarpışma kutusu. Ve şimdi (tüm bloklar arasında döngü olmadan) oyuncunun 5 blok yarıçapı içinde olan tüm blokları döndüren bir yöntem var. Sorun için özür dilerim, ama herhangi bir açıklama? Bu arada, daha basit hale getirmek için dünyanın 64 x 64 x 64 olduğunu varsayabilir misiniz?
Joel

Ve hala 5 kare / saniye civarında alıyorum :(
Joel

Cevabımı yeniden yazdım, yardımcı olup olmadığını bana bildirin.
yavaşladı

Bu yüzden bu oktree tekniğini kullanarak oyuncunun olabileceği blokları daraltırım. Bunu aldığımdan eminim. Ama sadece küçük bir blok seçimine daralttıktan sonra çarpışma tespitimi kullanmamı mı öneriyorsun, yoksa başka bir yöntemin var mı?
Joel

2
Oyuncu küplerin boyutuna göre çok büyük değilse, o zaman oyuncunun etrafındaki çarpışmayı kontrol etmek muhtemelen en hızlı yoldur. Oyuncu bir küpten daha fazla yer kaplamıyorsa, bir çarpışma bulmak için en fazla 27 çevreleyen küpü kontrol etmesi gerekir. Küpleri indeksleyebileceği bir dizide saklayacağı varsayılarak, olası her küp konumu için bir yuva tahsis edildiği varsayıldığında, bu küp konumlarına doğrudan indeksleyebileceğiniz için bu bir ağaç gerektirmez.
Olhovsky

13

Kutuların büyük miktarlarda üzerinden o iterating içinde Daniels yanıta katılabilir olan en olası nedeni ve mekansal bölümleme kullanarak bir sürü kadar oyunu hızlandırmak olabilir - ama sorun olabilir ayrıca başka bir yerde olmak ve size vakit olabilir .

Oyununuzun hızını önemli ölçüde artırmak için kodunuzu değiştirmeniz gerekir. Darboğazın nerede olduğunu belirleyin, bu en büyük iyileştirmeleri yapmanıza izin verecektir.

Kodunuzu profillendirmenin birçok yolu vardır, kendi performans analiz sınıfınızı ( Kronometre sınıfından (MSDN) yararlanabilir ) geçebilir veya CPU / GPU'nun ne kadar meşgul olduğuna dair genel bir fikir edinmek için PIX'i kullanabilirsiniz. .

Kodunuza PIX olay işaretlerinde renkli bölgeler olarak görünecek PIX olay işaretleyicileri de koyabilirsiniz . Bu işlevler için resmi bir C # arabirimi yoktur, ancak bu iş parçacığı nasıl bir C # arabirimini kendiniz yapabileceğinizi gösterir.


2
+1, tamamen katılıyorum. Algoritmalara bakmamalısınız, kodunuzu profillemeye bakmalısınız. Benim tahminim, blokları tekrarladığınız (bunun o kadar değil), her blokla yaptığınız şeyle ilgisi yoktur. Nihayetinde, kaba kuvvetten daha iyi bir yönteme sahip olmanız gerekir, ancak 48x32x48 küplerde basit bir yinelemeyi kaldıramazsanız, her bir küple ne yaptığınızı yeniden düşünmeniz gerekir, nasıl döngü yaptığınızı değil.
Tim Holt

4
@Tim: Oyuncusu 48x32x48 yer kaplayacak kadar büyük olmadığı sürece, o kadar çok küpün yakınında hiçbir yerde tekrar etmemelidir. Çerçeve başına 73000 küp boyunca yineleniyorsa, o zaman herhangi bir profil oluşturmadan size söyleyebilirim, on binlerce kat daha fazla yineleme yapmaktan kaçınmayı öğrenmekten başka bir sebep yoksa düzeltmesi için buna değer olduğunu söyleyebilirim. gerekli. Mikro veya erken optimizasyon dediğim bu değil.
Olhovsky

Oyuncum 1 küpün boyutundan küçük, ancak bir aşamada daha büyük olabilir (ancak çok fazla değil)
Joel

Randomman159: O zaman sadece çarpışmayı bulmak için çevresindeki 27 küpü test etmelisin. Cevabımı gör.
Olhovsky

6

Oynatıcınız küplerin boyutuna göre büyükse, muhtemelen başkalarının önerdiği gibi bir oktree veya başka bir uzamsal bölümleme yapısı istersiniz.

Ancak, oynatıcınız küplerin boyutuna göre küçükse, muhtemelen küplerle çarpışmayı tespit etmenin en hızlı yolu, oynatıcının etrafındaki alanı basit bir doğrusal arama yapmaktır.

Oynatıcınız 1 küpten küçük olduğu için, en fazla komşu 27 küple çarpışmayı test etmeniz yeterlidir.

Bu, küpleri dizine ekleyebileceğiniz bir dizide sakladığınızı ve her küp için dizide bir yuva bulunduğunu varsayar.

Diğerlerinin işaret ettiği gibi, aslında sizi neyin yavaşlattığını görmek için kodunuzu profillemeniz gerekir.

Eğer tahmin etmek zorunda kalsaydım, muhtemelen her küp için bir beraberlik çağrısı yaptığınızı söyleyebilirim. Bunu düzeltmek için geometri örneklemesine bakmalısınız.


Ya da oynatıcıyı çevreleyen bir 'sınırlayıcı kutuya sahip olabilirsiniz ve daha sonra oyuncunun hangi nesnelerle çarpışması gerektiğini belirlemek için çarpıştığı nesneleri kontrol edebilirsiniz. İyi bir fizik motoru sizin için tüm optimizasyonları yapabilecektir; aynı zamanda sadece 'bloklardan' çarpışmaya izin verir.
yavaşlama

Şahsen, benim için çarpışmayı verimli bir şekilde test etmek için iki düzine kod satırı yazabildiğimde, benim için 73000 küpü test etmek için bir fizik motorunun geniş fazına güvenmek istemem. Ayrıca, muhtemelen şu anda girecek bir fizik motoru yok.
Olhovsky

1

İşleri hızlandırmak için bir öneri daha: Bloklarınız yaklaşık olarak sabittir - bu, bir oyuncunun çoğuyla çarpışmasının bir yolu olmadığı anlamına gelir. Görünür olup olmadıklarını gösteren bloklara bir boole ekleyin. (Bu, komşularına bakarak yeniden hesaplanabilir.) Maruz kalmayan bir bloğun çarpışmalara karşı kontrol edilmesi gerekmez.

Minecraft'ın buna benzer bir şey yaptığı açıktır - bana dünyaya bir bakış veren yüklü olmayan bir parçaya vurdum - Sağlam zemin boyunca görebiliyordum, ortaya çıkan tek şey açık alanlardı (uzak taraf bunlar açıkta kalan bir yüzeydi ve bu yüzden render edildi.)


-1

Voksel motorumda bu sorunu yaşadım.

Çözüm: (Oktrees'ten çok daha basit) Tüm bloklar arasında döngü yapmak yerine blokların bloklar dizisindeki konumunu belirlemek için bir denklem kullanın.

BlockIndex = (x * WorldWidth * WorldHeight) + (z * WorldHeight) + y;

Sonra bir bloğun var olup olmadığını görmek istiyorsanız:

Blocks[BlockIndex].Type > -1;

Ancak bloğun var olup olmadığını siz belirlersiniz.


Buradaki problem daha karmaşıktır, çünkü farenizi 3D dünyayla test etmek için sadece 2 koordinatınız vardır. Görünüm yukarıdan aşağıya doğru ise, bir fare konumu için 3 sağ dizinden 2'sini bulabilir ve daha sonra üstten başlayarak kameraya daha yakın bir yerden başlayarak yükseklik için döngü oluşturabilir ve bir bloğun ilk oluşumunu bulabilirsiniz.
Markus von Broady
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.