Minecraft-esque voksel dünyasını nasıl optimize edebilirim?


76

Minecraft'ın muhteşem büyük dünyalarını, dört çekirdekli ve etli grafik kartıyla bile gezinmek için oldukça yavaş buldum.

Minecraft'ın yavaşlığının neden olduğunu farz ediyorum:

  • Java, mekansal bölümleme ve bellek yönetimi olarak yerel C ++ 'da daha hızlıdır.
  • Zayıf dünya bölümlemesi.

Her iki varsayımda da yanılıyor olabilirim. Ancak, bu beni büyük voksel dünyalarını yönetmenin en iyi yolunu düşündürdü. Bir blok, dünyanın herhangi bir yerindeki varlığını sürdürebilen bir gerçek 3D dünyası, olduğu gibi, büyük bir 3D dizi temelde [x][y][z]dünyada her bloğun bir türü olan, (yani BlockType.Empty = 0, BlockType.Dirt = 1vs.)

Bu tür bir dünyanın iyi performans göstermesini sağlamak için şunu yapmanız gerektiğini düşünüyorum:

  • Tüm küpleri ayırmak için bir çeşit ağaç ( oct / kd / bsp ) kullanın; Bir oct / kd daha iyi bir seçenektir, çünkü sadece küp seviyesine göre üçgen seviyesine göre bölümleme yapabilirsiniz.
  • Hangi blokların görülebildiğini hesaplamak için bazı algoritmalar kullanın, çünkü kullanıcıya daha yakın olan bloklar arkasındaki blokları gizleyebilir ve bunları oluşturmayı anlamsızlaştırabilir.
  • Blok nesnesini kendileri hafif tutun, böylece ağaçlara eklemeniz ve çıkarmanız hızlıdır.

Sanırım buna doğru bir cevap yok , ama insanların konu hakkındaki görüşlerini görmek isterim. Büyük voksel tabanlı bir dünyada performansı nasıl geliştirirsiniz?



2
Peki aslında ne soruyorsun? Büyük dünyaları yönetmek için iyi yaklaşımlar mı, özel yaklaşımınız hakkında geribildirim mi, yoksa büyük dünyaları yönetme konusundaki görüşleri mi istiyorsunuz?
doppelgreener

1
Şimdiye kadar iyi şeyler, bu tür şeylere en yaygın yaklaşımın ne olduğu ile ilgili . Benim önerim, benim önerdiğim tek şey mantıklı bir şekilde olmasını beklediğim gibi. Sadece konuyla ilgili daha fazla bilgi almak istedim ve birkaç aramadan sonra çok fazla bir şey gelmedi. Benim soru vb alanları chunking olarak ... sadece oluşturma performansını ilgili değil ama verilerin böyle bir hacimle yönetme hakkında değil sanırım
SomeXnaChump

2
Bu yüzden açık olun ve yazınıza bir soru ekleyin, böylece hangi soruyu cevapladığımızı biliriz. ;)
doppelgreener

3
Ne demek "gezinmek için çok yavaş"? Oyun yeni araziler oluştururken kesinlikle biraz yavaşlama var, ancak ondan sonra Minecraft araziyi oldukça iyi idare etmeye meyilli.
pazartesi

Yanıtlar:


106

Voksel Motor Kayaları

Voxel Motor Çimentosu

Java vs C ++ ile ilgili olarak, her ikisinde de bir voksel motoru yazdım (yukarıda gösterilen C ++ sürümü). Ayrıca 2004'ten beri (moda olmadıkları zamanlarda) voksel motorları yazıyorum. :) Çok az tereddütle C ++ performansının çok daha üstün olduğunu söyleyebilirim (ancak kodlaması daha zordur). Hesaplama hızı ve hafıza yönetimi hakkında daha az şey. Eller aşağıdayken, bir voksel dünyasındaki veriler kadar veri tahsis / tahsis ederken, C (++) yenilecek dildir. ancak, amacını düşünmelisin. Performans sizin en yüksek önceliğiniz ise, C ++ ile gidin. Kanama performansı olmayan bir oyun yazmak istiyorsanız, Java kesinlikle kabul edilebilir (Minecraft tarafından kanıtlandığı gibi). Çok sayıda önemsiz / son durum vardır, ancak genel olarak Java'nın (iyi yazılmış) C ++ 'dan yaklaşık 1.75-2.0 kat daha yavaş çalışmasını bekleyebilirsiniz. Motorumun zayıf optimize edilmiş eski versiyonunu burada çalışırken görebilirsiniz (EDIT: burada daha yeni sürüm ). Yığın oluşumu yavaş görünse de, hacimsel olarak 3B voronoi şemaları oluşturduğunu, yüzeysel normalleri, ışıklandırmayı, AO'yu ve CPU üzerindeki gölgeleri hesaplayarak kaba kuvvet yöntemleriyle oluşturduğunu unutmayın. Çeşitli teknikleri denedim ve çeşitli önbellekleme ve deneme teknikleri kullanarak yaklaşık 100 kat daha hızlı yığın üretme elde edebiliyorum.

Sorunuzun geri kalanına cevap vermek için performansı artırmak için yapabileceğiniz birçok şey var.

  1. Caching. Nerede olursanız olun, verileri bir kez hesaplamanız gerekir. Örneğin, aydınlatmayı sahneye fırlatırım. Dinamik aydınlatmayı kullanabilir (ekran alanında, bir post-proses olarak), ancak aydınlatmayı pişirmek, üçgenler için normalleri geçmek zorunda olmadığım anlamına gelir, yani ...
  2. Video kartına mümkün olduğunca az veri iletin. İnsanların unutmaya meyilli olduğu şeylerden biri GPU'ya ne kadar çok veri aktarırsanız, o kadar fazla zaman harcar. Tek bir renk ve bir tepe pozisyonunda geçiyorum. Gündüz / gece döngüleri yapmak istersem, sadece renk derecelendirmesini yapabilirim veya güneş yavaş yavaş değiştikçe sahneyi yeniden hesaplayabilirim.

  3. Verilerin GPU'ya aktarılması çok pahalı olduğu için, bazı yönlerden daha hızlı olan yazılımda bir motor yazmak mümkündür. Yazılımın avantajı, bir GPU'da mümkün olmayan her türlü veri işleme / bellek erişimini yapabilmesidir.

  4. Toplu boyutu ile oynayın. Bir GPU kullanıyorsanız, performans, aktardığınız her bir köşe dizisinin ne kadar büyük olduğuna bağlı olarak büyük ölçüde değişebilir. Buna göre, topakların boyutuyla oynayın (topak kullanıyorsanız). 64x64x64 parçalarının çok iyi çalıştığını buldum. Ne olursa olsun, topaklarınızı kübik tutun (dikdörtgen prizmalar yok). Bu kodlama ve çeşitli işlemleri (dönüşümler gibi) kolaylaştıracak ve bazı durumlarda daha performans gösterecektir. Her boyutun uzunluğu için yalnızca bir değer saklarsanız, hesaplama sırasında takas edilen daha az iki kayıt olduğunu unutmayın.

  5. Ekran listelerini düşünün (OpenGL için). "Eski" yol olsalar bile, daha hızlı olabilirler. Bir ekran listesini değişkene dönüştürmelisiniz ... eğer ekran listesi oluşturma işlemlerini gerçek zamanlı olarak çağırırsanız, çok yavaş olacaktır. Bir ekran listesi nasıl daha hızlı? Yalnızca durumu, köşe başına öznitelikleri vs güncelleştirir. Bu, altı yüze kadar geçebileceğim anlamına gelir, sonra bir renk (vokselin her bir köşesi için bir renge karşı). GL_QUADS ve kübik voksel kullanıyorsanız, bu voksel başına 20 bayta (160 bit) kadar tasarruf sağlayabilir! (Alfa içermeyen 15 bayt, genellikle 4 baytlık bir şeyleri saklamak istemenize rağmen)

  6. "Parçalar" oluşturmak için kaba kuvvet yöntemini ya da ortak bir teknik olan veri sayfalarını kullanıyorum. Okuyuculardan farklı olarak, verileri okumak / işlemek çok daha kolay / daha hızlıdır, ancak çok daha az hafıza dostu olsa da (ancak bugünlerde 200-300 dolara 64 gigabaytlık bellek alabilirsiniz) ... ortalama bir kullanıcı bu değildir. Açıkçası, tüm dünya için büyük bir dizi tahsis edemezsiniz (1024x1024x1024 voksel seti, voksel başına 32 bitlik bir int kullanıldığı varsayılarak, 4 gigabayt bellekdir). Böylece, izleyiciye olan yakınlığına bağlı olarak birçok küçük diziyi ayırır / devre dışı bırakırsınız. Ayrıca verileri tahsis edebilir, gerekli ekran listesini alabilir, ardından hafızadan tasarruf etmek için veriyi bırakabilirsiniz. İdeal combo'nun octrees ve dizilerin hibrid bir yaklaşımını kullanmak olabileceğini düşünüyorum - dünyanın prosedürel jenerasyonu, aydınlatma vb.

  7. Uzaklara yakın render ... kırpılmış bir piksel zaman kazandırır. Derinlik tamponu testini geçmezse gpu bir piksel atacaktır.

  8. Render sadece görünümdeki parçaları / sayfaları gösterir (kendi kendini açıklayıcı). Gpu, görüntü portunun dışındaki poligonları nasıl yakalayacağını bilse bile, bu verileri geçmek zaman alıyor. Bunun için en verimli yapının ne olacağını bilmiyorum ("utanç verici", hiçbir zaman bir BSP ağacı yazmadım), ancak yığın bazında basit bir raycast bile performansı artırabilir ve açıkça izleyen frustuma karşı test yapabilir. Zamandan tasarruf.

  9. Açıkça bilgi, ancak yeni başlayanlar için: yüzeyde olmayan her bir poligonu çıkarın - yani bir voksel altı yüzden oluşuyorsa, hiç işlenmemiş yüzleri çıkarın (başka bir vokselle temas ediyor).

  10. Programlamada yaptığınız her şeyin genel kuralı olarak: CACHE LOCALITY! İşleri yerel olarak saklayabiliyorsanız (az miktarda bile olsa, çok büyük bir fark yaratacaktır. Bu, verilerinizi uyumlu (aynı bellek bölgesinde) tutmak ve bellek alanlarını çok sık işlem yapmak zorunda bırakmamak anlamına gelir. , ideal olarak, iş parçacığı başına bir öbek üzerinde çalışın ve bu belleği iş parçacığına özgü tutun, bu yalnızca CPU önbelleği için geçerli değildir. Bunun gibi önbellek hiyerarşisini düşünün (en yavaşdan en hızlı): ağ (bulut / veritabanı / vb) -> sabit sürücü (zaten yoksa, bir SSD alın), ram (zaten yoksa üçlü kanal veya daha fazla RAM alın), CPU Önbellek (ler) i, kaydedicilerinizi kaydedin. Sonuncusu, ve gerekenden daha fazla değiştirmeyin.

  11. Threading. Yap. Voxel dünyaları her parçayı diğerlerinden bağımsız olarak hesaplayabildiğinden (çoğunlukla) hesaplanabildiğinden, diş açma için çok uygundur ... Yazarken, yordam dünyasında tam anlamıyla 4x'e yakın bir gelişme (4 çekirdekli, 8 iplik Çekirdekli i7) gördüm. diş açma için rutinler.

  12. Char / byte veri türlerini kullanmayın. Veya şort. Ortalama bir tüketici modern bir AMD veya Intel işlemciye sahip olacak (muhtemelen sizin gibi). Bu işlemcilerin 8 bit kaydı yok. Bayt'ları 32 bitlik bir yuvaya yerleştirip sonra tekrar bellekte (belki) dönüştürerek hesaplarlar. Derleyiciniz her türlü vudu yapabilir, ancak 32 veya 64 bitlik bir sayı kullanmak size en öngörülebilir (ve en hızlı) sonuçları verecektir. Aynı şekilde, bir "bool" değeri 1 bit almaz; derleyici bir bool için genellikle tam bir 32 bit kullanır. Verilerinizde belirli sıkıştırma türleri yapmanız cazip gelebilir. Örneğin, aynı vokal / renkte olsaydı 8 vokseli tek bir numara olarak saklayabilirsiniz (2 ^ 8 = 256 kombinasyon). Bununla birlikte, bunun sonuçları hakkında düşünmeniz gerekir - bu, çok fazla hafıza biriktirebilir, ancak küçük bir dekompresyon süresinde bile performansı engelleyebilir, çünkü bu küçük miktardaki fazladan zaman bile dünyanızın boyutuna göre küp ölçeklenir. Bir raycast hesapladığını düşünün; Rapikastin her basamağı için, dekompresyon algoritmasını çalıştırmanız gerekecektir (bir ışın basamağında 8 voksel için hesaplamayı genelleştirmenin akıllı bir yolunu bulamazsanız).

  13. Jose Chavez'in bahsettiği gibi, flyweight tasarım deseni faydalı olabilir. 2B oyunda bir döşemeyi temsil etmek için bir bitmap kullandığınız gibi, dünyanızı birkaç 3B döşeme (veya blok) türünden oluşturabilirsiniz. Bunun dezavantajı, dokuların tekrarıdır, ancak birbirine uyan değişken dokuları kullanarak bunu iyileştirebilirsiniz. Genel bir kural olarak, nerede olursanız olun, örneklemeden yararlanmak istersiniz.

  14. Geometri çıkarırken gölgelendiricideki köşe ve piksel işlemeden kaçının. Bir voksel motorunda, kaçınılmaz olarak birçok üçgene sahip olacaksınız, böylece basit bir piksel gölgelendirici bile oluşturma sürenizi büyük ölçüde azaltabilir. Tamponun oluşturulması daha iyidir, o zaman bir işlem sonrası piksel gölgelendiriciyi yaparsınız. Bunu yapamıyorsanız, köşe gölgelendiricinizde hesaplamalar yapmayı deneyin. Diğer hesaplamalar mümkünse tepe noktası verilerine yapılmalıdır. Tüm geometrileri (gölge haritalama veya çevre haritalama gibi) yeniden oluşturmanız gerekirse ek geçişler çok pahalı hale gelir. Bazen daha zengin ayrıntılar lehine dinamik bir sahneden vazgeçmek daha iyidir. Oyununuzda değiştirilebilir sahneler varsa (örneğin, yıkılabilir arazi), sahneyi işler yıkıldıkça yeniden hesaplayabilirsiniz. Yeniden derleme pahalı değildir ve bir saniyeden daha az sürmelidir.

  15. Döngülerini çöz ve dizileri düz tut! Bunu yapma:

    for (i = 0; i < chunkLength; i++) {
     for (j = 0; j < chunkLength; j++) {
      for (k = 0; k < chunkLength; k++) {
       MyData[i][j][k] = newVal;
      }
     }
    }
    //Instead, do this:
    for (i = 0; i < chunkLengthCubed; i++) {
     //figure out x, y, z index of chunk using modulus and div operators on i
     //myData should have chunkLengthCubed number of indices, obviously
     myData[i] = newVal;
    }
    

    EDIT: Daha kapsamlı testlerle bunun yanlış olabileceğini keşfettim. Senaryonuz için en uygun olanı kullanın. Genel olarak, diziler düz olmalıdır, ancak çok endeksli döngüler kullanılması duruma bağlı olarak genellikle daha hızlı olabilir

DÜZENLEME 2: Çok endeksli döngüler kullanıldığında, en iyisi int, z, y, x sıralarında değil, int çevrimidir. Derleyiciniz bunu optimize edebilir, ancak yapsaydı şaşırırdım. Bu, hafıza erişiminde ve yerellikte verimliliği en üst düzeye çıkarır.

for (k < 0; k < volumePitch; k++) {
    for (j = 0; j < volumePitch; j++) {
        for (i = 0; i < volumePitch; i++) {
            myIndex = k*volumePitch*volumePitch + j*volumePitch + i;
        }
    }
}
  1. Bazen varsayımlar, genellemeler ve fedakarlıklar yapmanız gerekir. Yapabileceğiniz en iyi şey, dünyanızın çoğunun tamamen statik olduğunu varsaymak ve sadece her birkaç bin kareyi değiştirdiğini varsaymak. Dünyanın hareketli bölümleri için bunlar ayrı bir geçişte yapılabilir. Ayrıca, dünyanızın çoğunun tamamen opak olduğunu varsayın. Saydam nesneler ayrı bir geçişte oluşturulabilir. Dokularýn sadece her x biriminde deðiþtirildiðini veya nesnelerin yalnýzca x artýmlarna konabileceðini varsayýn. Sabit bir dünya boyutu varsayalım ... sonsuz bir dünya kadar çekici, öngörülemeyen sistem gereksinimlerine yol açabilir. Örneğin, yukarıdaki kayalarda voronoi desen oluşumunu basitleştirmek için, her voronoi merkez noktasının hafif bir kayma (başka bir deyişle, geometrik karmalaştırma) ile tek tip bir ızgarada uzandığını varsaydım. Sarılmayan (kenarları olmayan) bir dünya düşünün. Bu, kullanıcı deneyimine en düşük maliyetle, bir sarma koordinat sistemi tarafından ortaya konan birçok karmaşıklığı basitleştirebilir.

Sen benim uygulamaları hakkında daha fazla bilgi bulabilirsiniz siteme


9
+1. Makaleleri okumak için bir teşvik olarak üstte resimler içeren hoş bir dokunuş. Şimdi makaleyi okuduğuma göre, ihtiyaç olmadıklarını söyleyebilirim ve buna değdi. ;)
George Duckett,

Teşekkürler - bir resim dedikleri gibi bin kelimeye bedeldir. :) Metin duvarımı daha az korkutucu kılmanın dışında, okuyuculara, tarif edilen teknikleri kullanarak kaç vokselin makul bir oranda oluşturabileceği konusunda bir fikir vermek istedim.
Gavan Woolery,

14
SE'nin en sevdiğim özel cevaplara izin vermesini diliyorum.
joltmode

2
@PatrickMoriarty # 15 oldukça yaygın bir numaradır. Derleyicinizin bu optimizasyonu yapmadığını varsayalım (döngüsünü açabilir, ancak muhtemelen çok boyutlu bir diziyi sıkıştırmaz). Önbellekleme için tüm verilerinizi aynı bitişik hafıza alanında tutmak istiyorsunuz. Çok boyutlu bir dizi (potansiyel olarak) bir dizi işaretçi olduğu için birçok alana tahsis edilebilir. Döngüyü açmak için, derlenmiş kodun neye benzediğini düşünün. En az kayıt ve önbellek takas için en az değişken / talimat üretmek istersiniz. Sizce hangisi daha çok derlenir?
Gavan Woolery,

2
Buradaki noktaların bazıları, özellikle önbellekleme, iş parçacığı ve GPU aktarımını en aza indirgemek açısından iyi olsa da, bazıları korkunç derecede yanlış. 5: HER ZAMAN ekran listeleri yerine VBO'lar / VAO'lar kullanın. 6: Daha fazla RAM sadece daha fazla bant genişliğine ihtiyaç duyar. 12'ye kadar: EXACT bunun tersi modern bellek için doğrudur; bunun için her byte kurtarılmıştır ve daha fazla veriyi önbelleğe alma şansını arttırmaktadır. 14: Minecraft'ta piksellerden (tüm bu uzak küpler) daha fazla köşeler var, bu nedenle hesaplamayı, FROM'dan değil, tercihen ertelenmiş gölgelemeyle piksel gölgelendiricisine doğru kaydırın.

7

Minecraft'ın daha verimli yapabileceği pek çok şey var. Örneğin, Minecraft, yaklaşık 16x16 fayansın tüm dikey sütunlarını yükler ve bunları işler. Birçok karoyu gereksiz yere göndermenin ve oluşturmanın çok yetersiz olduğunu hissediyorum. Fakat dil seçiminin önemli bir şey olduğunu düşünmüyorum.

Java oldukça hızlı olabilir ancak bu veri odaklı bir şey için, C ++ dizilere erişmek ve bayt içinde çalışmak için önemli ölçüde daha az ek yüke sahip büyük bir avantaja sahiptir. Kapak tarafında, Java'daki tüm platformlarda diş açmak daha kolaydır. OpenMP veya OpenCL kullanmayı düşünmüyorsanız, bu rahatlığı C ++ 'da bulamazsınız.

İdeal sistemim biraz daha karmaşık bir hiyerarşi olurdu.

Döşeme , malzeme türü ve aydınlatma gibi bilgileri saklamak için muhtemelen 4 bayt civarında olan tek bir birimdir.

Segment 32x32x32 karo bloğu olacaktır.

  1. Tüm taraf katı bloklarsa, bayraklar altı tarafın her biri için ayarlanacaktır. Bu, işleyicinin o bölümün arkasındaki bölümleri kapatmasına olanak tanır. Minecraft şu anda oklüzyon testi yapıyor gibi görünmüyor. Ancak , düşük maliyetli kartlarda büyük miktarlarda çokgen yapmaktan daha pahalı ama daha iyi olan donanım tıkanıklığı temizleme özelliğine sahip olduğunun belirtilmesi gerekiyordu.
  2. Segmentler sadece aktivite sırasında hafızaya yüklenir (oyuncular, NPC'ler, su fiziği, ağaç büyümesi, vb.). Aksi halde doğrudan diskten istemcilere sıkıştırılmış olarak gönderilirler.

Sektörler 16x16x8'lük bir segment bloğu olacaktır.

  1. Sektörler, her dikey sütun için en yüksek kesimi izler, böylelikle daha yüksek kesimler hızlıca boş olarak belirlenebilir.
  2. Aynı zamanda, dibi kapatılmış olan parçayı izler, böylece yüzeyden yapılması gereken her bir segment hızlı bir şekilde yakalanabilir.
  3. Sektörler, her bir segmentin bir daha güncellenmesi gerektiğinde (su fiziği, ağaç büyümesi vb.) İzleyecektir. Bu yolla her sektöre yükleme, dünyayı hayatta tutmak için yeterli ve sadece görevlerini tamamlayacak kadar uzun bölümlere yükleme yapmak yeterli olacaktır.
  4. Tüm varlık pozisyonları Sektöre göre izlenir. Bu, harita merkezinden çok uzağa seyahat ederken Minecraft'taki kayan nokta hatalarını önler.

Dünya sonsuz bir sektör haritası olacaktır.

  1. Dünya, sektörleri ve sonraki güncellemelerini yönetmekten sorumlu olacaktır.
  2. Dünya, oyuncuları potansiyel yolları boyunca ön plana çıkardı. Minecraft, müşterinin istediği segmentleri gecikmeli olarak reaktif olarak gönderir.

Genelde bu fikri beğenirim, fakat nasıl içsel olarak Dünya sektörlerini haritalandırırsınız ?
Clashsoft

Bir dizi Segmentlerdeki Döşemeler ve Sektördeki Segmentler için en iyi çözüm olurken, Dünyadaki Sektörler sonsuz bir harita boyutuna izin verecek farklı bir şeye ihtiyaç duyacaktır. Benim önerim, karma için XY koordinatlarını kullanarak bir karma tablosu (sözde <<22, Sektör>) kullanmak olacaktır. O zaman Dünya basitçe verilen koordinatlarda bir sektör arayabilir.
Josh Brown

6

Minecraft 2 çekirdekte bile oldukça hızlı. Java, biraz sunucu gecikmesi olmasına rağmen, burada sınırlayıcı bir faktör olarak görünmüyor. Yerel oyunlar daha iyi görünüyor, bu yüzden bazı verimsizlikler varsayacağım.

Sorunuzla ilgili olarak, Notch (Minecraft yazarı) teknoloji hakkında bir süredir blog yazdı. Özellikle, dünya “topaklarda” saklanır (bazen, henüz dünyanın henüz doldurmadığı bir şey eksik olduğunda, bazen bunları görürsünüz), bu nedenle ilk optimizasyon bir topağın görüp görülemeyeceğine karar vermektir. .

Bir öbek içinde, tahmin ettiğiniz gibi, uygulamanın, bir bloğun diğer bloklar tarafından gizlenip gizlenmediğine bağlı olarak bir bloğun görülebilip görülmeyeceğine karar vermesi gerekir.

Ayrıca, gizlendiği (yani başka bir bloğun yüzü kapladığı) ya da kameranın hangi yöne baktığını (kamera kuzeye bakarsa), göremeyeceği düşünülen FACES bloğu olduğunu unutmayın. HERHANGİ BİR bloğun Kuzey yüzünü göremiyorum!)

Yaygın teknikler ayrıca, ayrı blok nesnelerinin tutulmasını değil, her biri için tek bir prototip bloğun yanı sıra, bu bloğun nasıl özel olabileceğini tanımlamak için bazı minimum veri setleriyle birlikte, blok tiplerinin "yığın" ını da içerir. Örneğin, özel bir granit blok (bilmediğim) yoktur, ancak suyun her birinin yüzü boyunca ne kadar derin olduğunu ve bunun akış yönünü hesaplayabildiğini söyleyen veri vardır.

Oluşturma hızını, veri boyutunu veya neyi optimize etmek istediğinizde sorunuz açık değil. Buradaki açıklama yardımcı olacaktır.


4
"kümeler" genellikle topaklar olarak adlandırılır.
Marco

İyi yakalama (+1); Cevap güncellendi. (Başlangıçta hafıza için çalışıyordu ve doğru sözcüğü unuttum.)
Olie,

Bahsettiğiniz verimsizlikler aynı zamanda “uç nokta” olarak da bilinen “ağ” olarak da bilinir.
Edwin Buck

4

İşte size, aşırı tecrübeli bir Minecraft modder'i olarak verebileceğim (en azından kısmen size rehberlik edecek olan) genel bilgi ve tavsiyelerden oluşan birkaç kelime.

Minecraft'ın yavaş olmasının nedeni, bazı sorgulanabilir, düşük seviyeli tasarım kararları ile ilgili bir LOT'a sahip olmasının - örneğin, bir bloğu konumlandırmakla her seferinde başvuruda bulunulduğunda, oyun sınırların dışına çıkmadığından emin olmak için eğer ifadeler yaklaşık 7 ile doğrulanıyor . Ayrıca, önbellek aramalarını atlamak ve erm, aptalca doğrulama sorunlarını (iow, her blok referansı da içerir), bir 'öbeği' (oyunun çalıştığı blokların 16x16x256 blok birimi) kapmak için hiçbir yol yoktur. diğer şeylerin yanı sıra bir yığın arama.) Benim modumda, masif zindan neslini okunaksızca labirekten göze çarpan bir hıza yükselten doğrudan blok dizisini alıp değiştirmenin bir yolunu yarattım.

EDIT: Değişkenleri farklı bir kapsamda ilan etmenin performans kazancıyla sonuçlandığı iddiası kaldırıldı, aslında durum böyle görünmüyor. Bu sonucu, deneyimlediğim başka bir şeyle karıştırdığımda (özellikle, çiftlerle birleştirilen patlamalar ile ilgili koddaki çiftleri atmalarını, çiftlerle birleştirerek ... anlaşılabilir bir şekilde, bunun çok büyük bir etkisi oldu!)

Ayrıca, çok fazla zaman harcadığım alan olmasa da Minecraft'taki performans boğulmalarının çoğu, oluşturma ile ilgili bir sorundur (oyun zamanının yaklaşık% 75'i sistemime adanmıştır). Açıkçası, ilgi çok oyunculuda daha fazla oyuncuyu destekliyorsa (sunucu hiçbir şey yapmıyorsa) pek fazla umursamıyorsunuz, ancak herkesin makinelerinin bile oynayabileceği ölçüde önemli.

Hangi dili seçerseniz seçin, uygulama / düşük seviye detaylarıyla çok yakından ilgilenmeye çalışın, çünkü bunun gibi bir projede küçük bir detay bile fark yaratabilir (C ++ için benim için bir örnek "derleyici statik olarak satır içi işlevi görebilir mi?" işaretçiler? "Evet yapabilir! Üzerinde çalıştığım projelerden birinde inanılmaz bir fark yarattı, çünkü daha az kod ve satır içi avantajım vardı.)

Bu cevabı gerçekten beğenmedim, çünkü üst düzey tasarımı zorlaştırıyor, ancak performans kaygı verici ise acı verici bir gerçek. Umarım bu yararlı buldunuz!

Ayrıca, Gavin'in cevabı yinelemek istemediğim bazı ayrıntıları (ve daha fazlası! Konuyla ilgili benden açıkça daha bilgili) ve çoğunlukla onunla aynı fikirdeyim. İşlemciler ve daha kısa değişken boyutlarla ilgili yorumunu denemek zorunda kalacağım, bunu hiç duymadım - kendime doğru olduğunu ispatlamak isterim!


2

Önemli olan, öncelikle verileri nasıl yükleyeceğinizi düşünmektir. Harita verilerinizi gerektiğinde belleğe aktarırsanız, kesinlikle yapabilecekleriniz için doğal bir sınır vardır, bu zaten bir oluşturma performansı yükseltmesidir.

Bu verilerle ne yaptığınız size kalmış. GFX performansı için, daha sonra kullanabilirsiniz Clipping vb çok küçük nesneler görünür olması için gizli nesneleri klibi için

Eğer sadece Grafik Performansı tekniklerini arıyorsanız, internette bulabileceğiniz dağları bulabileceğinize eminim.


1

Bakılacak bir şey Flyweight tasarım deseni. Buradaki cevapların çoğunun bu tasarım modeline bir şekilde göndermediğine inanıyorum.

Tam yöntemi bilmiyorum, ancak Minecraft her blok tipi için belleği en aza indirmek için kullanıyor, bu oyununuzda kullanmak için olası bir yol. Buradaki fikir, tüm bloklar hakkında bilgi tutan prototip bir nesne gibi tek bir nesneye sahip olmaktır. Tek fark her bloğun konumu olacaktır.

Ancak konum bile en aza indirilebilir: bir toprak bloğunun tek tip olduğunu biliyorsanız, neden o toprakların boyutlarını tek bir konum verisi setiyle dev bir blok olarak saklamıyorsunuz?

Açıkçası, bilmenin tek yolu, kendi uygulamanızı başlatmaya başlamak ve performans için bazı hafıza testleri yapmaktır. Nasıl geçtiğini bize bildirin!

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.