Tüm oyun nesnelerini tek bir listede saklamak kabul edilebilir bir tasarım mı?


24

Yaptığım her oyun için, tüm oyun nesnelerimi (mermiler, arabalar, oyuncular) tek bir listeye yerleştirip sonunda çizip güncelleyeceğim. Her varlık için güncelleme kodu kendi sınıfında saklanır.

Merak ediyordum, işler yolunda gitmenin doğru yolu mu, yoksa daha mı hızlı bir şekilde?


4
Bunun .Net olduğunu varsayarak "dizi listesi" dediğini farkettim, bunun yerine bir Listeye <T> yükseltmelisiniz. Bir <T> Listesinin tip güçlü olması dışında neredeyse aynıdır ve bu nedenle bir elemana her erişişinizde cast yazmanıza gerek kalmaz. İşte doktor: msdn.microsoft.com/en-us/library/6sh2ey19.aspx
John McDonald

Yanıtlar:


26

Tek bir doğru yol yok. Açıkladığınız şey işe yarıyor ve büyük olasılıkla yeterince hızlı çünkü sorduğunuz göründüğü için önemli değil çünkü tasarım konusunda endişelisiniz ve performans sorunlarına yol açtığı için değil. Yani öyle bir emin, doğru yolu.

Örneğin, her nesne türü için homojen bir liste tutarak veya heterojen listenizdeki her şeyin bir arada gruplandırılmasını sağlayarak, önbellekteki kod için uyumlu tutarlılığı artıracak veya paralel güncelleştirmelere izin verecek şekilde kesinlikle farklı bir şekilde yapabilirsiniz. Bunu yaparak kayda değer bir performans artışı görüp görmediğinizi, oyunlarınızın kapsamını ve ölçeğini bilmeden söylemek zor.

Ayrıca, Tek Sorumluluk İlkesini daha iyi takip edebilmek için işletmelerin şu anda kendilerini hem güncellemelerine hem de kendilerini geliştirmelerine benziyor - seslerinizin sorumluluklarını daha da artırabilirsiniz. Bu, bireysel arayüzlerinizin birbirlerinden ayrıştırılarak bakımını ve esnekliğini artırabilir. Fakat yine de, gerektirecek fazladan işten bir fayda görüp görmeyeceğinize bakılmaksızın, oyunlarınız hakkında ne bildiğimi bilmek benim için zor (ki bu temelde hiçbir şey değil).

Bu konuda çok fazla strese girmemenizi tavsiye ederim ve performans veya bakımla ilgili sorunları fark ederseniz, iyileştirme için potansiyel bir alan olabileceğini zihninizin arkasında tutun.


16

Diğerlerinin dediği gibi, yeterince hızlıysa, yeterince hızlıdır. Daha iyi bir yol olup olmadığını merak ediyorsanız, kendinize sormanız gereken soru

Kendimi tüm nesnelerde yineleyerek ancak sadece bir alt kümesinde çalıştığını mı buluyorum?

Bunu yaparsanız, bu sadece ilgili referansları tutan birden fazla listenin olmasını isteyebileceğinizin bir göstergesidir. Örneğin, onları işlemek için her oyun nesnesinin üzerinden geçiyorsanız ancak yalnızca bir nesne alt kümesinin oluşturulması gerekiyorsa, yalnızca işlenmesi gereken nesneler için ayrı bir Renderable listesi tutmaya değer olabilir.

Bu, döngü içine aldığınız nesnelerin sayısını azaltır ve gereksiz denetimleri atlar; çünkü listedeki tüm nesnelerin işleneceğini zaten biliyorsunuzdur.

Çok fazla performans kazanıp kazanmadığınızı görmek için profilinizi oluşturmalısınız.


Buna katılıyorum, genellikle her karede güncellenmesi gereken nesneler için listemdeki alt kümelerim var ve bunları yapan nesneler için de aynısını düşünüyordum. Ancak bu özellikler anında değişebiliyorsa, tüm listelerin yönetilmesi karmaşık olabilir. Ne zaman ve nesne yenilmez hale getirilemez hale getirilemez hale getirilemez hale getirilebilirse veya güncellenemez hale getirilemez hale getirilebilir duruma getirilebiliyorsa ve şimdi bunları bir seferde birden çok listeye ekliyor veya kaldırıyorsunuz.
Nic Foster

8

Neredeyse tamamen sizin oyun gereksinimlerinize bağlıdır . Basit bir oyun için mükemmel çalışabilir, ancak daha karmaşık bir sistemde başarısız olabilir. Oyununuz için çalışıyorsa, endişelenmeyin ya da ihtiyaç doğana kadar üzerinde çalışmayı deneyin.

Basit bir yaklaşımın neden bazı durumlarda başarısız olabileceğine gelince, olasılıklar sonsuz olduğundan ve tamamen oyunların kendilerine bağlı olduğundan, tek bir gönderide bunu özetlemek zordur. Diğer cevaplar, örneğin sorumluluk paylaşan nesneleri birlikte ayrı bir listede gruplamak isteyebileceğinizden bahsetti.

Oyun tasarımınıza bağlı olarak yapabileceğiniz en yaygın değişikliklerden biri budur. Tarif do şans alacağım birkaç diğer (daha karmaşık) örnek , ancak hala birçok olası neden ve çözüm olduğunu unutmayın.

Başlangıç ​​olarak, oyun nesnelerinizi güncellemenin ve bunları oluşturmanın farklı gereksinimlere sahip olabileceğini belirteceğim. bazı oyunlarda ve bu nedenle ayrı ayrı ele alınması gerektiğine dikkat çekeceğim. Oyun nesnelerini güncelleme ve oluşturma ile ilgili bazı olası sorunlar:

Oyun Nesnelerini Güncelleme

İşte, okumanızı tavsiye edeceğim Game Engine Architecture'dan bir alıntı :

Nesneler arası bağımlılıkların varlığında, yukarıda açıklanan fazlı güncelleme tekniği biraz ayarlanmalıdır. Bunun nedeni nesneler arası bağımlılıkların güncelleme sırasını düzenleyen çelişkili kurallara yol açabilmesidir.

Başka bir deyişle, bazı oyunlarda nesnelerin birbirine bağlı olması ve belirli bir güncelleme sırası gerektirmesi mümkündür. Bu senaryoda, nesneleri ve bağımlılıklarını depolamak için bir listeden daha karmaşık bir yapı oluşturmanız gerekebilir.

görüntü tanımını buraya girin

Oyun Nesnelerinin İşlenmesi

Bazı oyunlarda sahneler ve nesneler ebeveyn-çocuk düğümleri hiyerarşisi yaratır ve doğru sırayla ve ebeveynlerine göre dönüşümlerle oluşturulmalıdır. Bu durumlarda, tek bir liste yerine sahne grafiği (ağaç yapısı) gibi daha karmaşık bir yapıya ihtiyacınız olabilir :

görüntü tanımını buraya girin

Diğer nedenler, örneğin, nesnelerinizi, görüntü oluşturma işleminin etkinliğini artıracak şekilde düzenlemek için uzamsal bir bölümleme veri yapısı kullanmak olabilir.


4

Cevabı "evet, bu iyi." Performansın müzakere edilemez olduğu büyük demir simülasyonları üzerinde çalışırken, her şeyi tek bir büyük şişman (BIG ve FAT) global dizide gördüğüme şaşırdım. Daha sonra bir OO sistemi üzerinde çalıştım ve ikisini karşılaştırdım. Süper çirkin olmasına rağmen, tek iş parçacıklı düz eski C versiyonu "tek dizi Hepsini kural" hata ayıklama derlenmiş birkaç kat daha hızlı "güzel" parçacıklı OO sistemden daha oldu -o2 derlenen C ++. Bir kısmının, büyük yağ dizisi sürümünde (hem alanda hem de zamanda) mükemmel önbellek konumuyla ilgili olduğunu düşünüyorum.

Performans istiyorsanız, bir büyük şişman küresel C dizisi (deneyimden) üst sınır olacak.


2

Temelde yapmak istediğin, istediğin kadar makyaj listeleri yapmak. Arazi nesnelerini tek seferde yeniden çizerseniz, sadece bunların bir listesi kullanışlı olabilir. Ancak bunun için on binlerce kişiye ve 10,000'in üzerinde arazi olmayan şeylere ihtiyacınız olacak . Aksi halde, her şeyin listesini incelemek ve arazi nesnelerini çıkarmak için "if" kullanmak yeterince hızlıdır.

Sahip olduğum diğer listelerden bağımsız olarak her zaman bir ana listeye ihtiyacım vardı. Genelde münferit öğelere rastgele erişebilmem için bir Harita veya anahtarlı bir tablo veya bir tür sözlük hazırlarım. Ancak basit bir liste daha basittir; Oyun nesnelerine rastgele erişemiyorsanız Haritaya ihtiyacınız yoktur. Ve sıralı arama, doğru olanı bulmak için birkaç bin girişe uzun sürmez. Yani, başka ne yaparsan yap, ana listeye bağlı kalmayı planlıyorum. Büyükse bir Harita kullanmayı düşünün. (Anahtarlar yerine dizinleri kullanabiliyor olsanız da, dizilere yapışabilir ve bir Haritadan çok daha iyi bir şeye sahip olabilirsiniz. Her zaman indeks sayıları arasında büyük boşluklar bıraktım, bu yüzden vazgeçmek zorunda kaldım.)

Diziler hakkında bir kelime: inanılmaz derecede hızlılar. Ancak yaklaşık 100.000 element kaldıklarında Çöp Toplayıcılarına uyumaya başladılar. Boşluğu bir kez ayırabilir ve daha sonra dokunmayacaksanız, tamam. Ancak diziyi sürekli olarak genişletiyorsanız, sürekli olarak büyük bellek parçalarını ayırıyor ve serbest bırakıyorsunuz ve oyununuzun bir anda saniyeler boyunca takılmasına ve hatta bir bellek hatasıyla başarısız olmasına izin veriyorsunuz.

Yani daha hızlı ve daha verimli? Emin. Rasgele erişim için bir harita. Eğer varsa ve ne zaman gerçekten büyük listeleri için bir Bağlantılı Liste bir dizi yenecek yoktur rasgele erişim ihtiyacı. Nesneleri, ihtiyaç duyduğunuz gibi çeşitli şekillerde düzenlemek için çoklu haritalar / listeler. Güncellemeyi çok iş parçacıklı hale getirebilirsiniz.

Ama hepsi çok iş. Bu tek dizi hızlı ve basittir. Diğer listeleri ve şeyleri yalnızca gerektiğinde ekleyebilir ve muhtemelen bunlara ihtiyacınız olmayacak. Oyununuz büyürse neyin yanlış gidebileceğinin farkında olun. Ne zaman başa çıkacağınıza dair bir fikir vermek için gerçek sürümde 10 kat daha fazla nesne içeren bir test sürümüne sahip olmak isteyebilirsiniz. (Sadece oyun eğer bunu edilir büyüyorlar.) (Ve ona düşünce çok vermeksizin çoklu iş parçacığı çalışmayın. Her şey ile başlamak çok kolaydır ve size kadar ya da benzeri gibi küçük yapabilir ve herhangi bir zaman geri çekilin. Çok iş parçacıklı olmak için içeri girmek için büyük bir bedel ödersiniz, kalma süreleri yüksektir ve geri dönmek zordur.)

Başka bir deyişle, yaptığınız şeyi yapmaya devam edin. En büyük limitiniz muhtemelen kendi zamanınızdır ve bunu en iyi şekilde kullanıyorsunuzdur.


Bağlantılı Listenin, sık sık ortadan şeyler ekleyip çıkarmadığınız sürece herhangi bir boyutta bir diziden daha iyi performans göstereceğini düşünmüyorum.
Zan Lynx,

@ZanLynx: Ve o zaman bile. Ben düşünüyorum yeni çalışma zamanı optimizasyonlar yardım Diziler çok - onlar bu gerekli değil. Ancak, büyük dizilerde olduğu gibi, büyük bellek parçalarını art arda ve hızlı bir şekilde tahsis edip serbest bırakırsanız, çöp toplayıcıyı düğümlere bağlayabilirsiniz. (Toplam bellek miktarı burada da bir faktördür. Sorun blok büyüklüğünün, serbest ve tahsisat sıklığının ve mevcut belleğin bir fonksiyonudur.) Program askıda kalırken ya da kilitlenirken, aniden ortaya çıkıyor. Genellikle çok fazla boş hafıza vardır; GC sadece kullanamaz. Bağlantılı listeler bu sorunu önler.
RalphChapin

Öte yandan, bağlantılı listeler, düğümlerin bellekte bitişik olmadıkları için yinelemeyi kaçırmayı önleme olasılığını önemli ölçüde arttırmaktadır. Neredeyse hiç kesilmiş ve kuru değil. Yeniden tahsis etme sorunlarını, bunu yapmadan (mümkünse önden ayırma, çünkü çoğu zaman olduğu gibi) hafifletebilirsiniz ve önbellek tutarlılığı sorunlarını, düğümleri ayırarak havuza ayırarak (yine de referansın takip maliyetine sahip olmanıza rağmen) bağlantılı bir listede hafifletebilirsiniz. , nispeten önemsiz).
Josh

Evet, ama bir dizide bile, işaretçileri nesnelere kaydetmiyor musunuz? (Parçacık sistemi için kısa ömürlü bir dizi veya başka bir şey değilse.) Öyleyse, yine de çok fazla önbellek özeti olacaktır.
Todd Lehman,

1

Basit oyunlar için biraz benzer bir yöntem kullandım. Her şeyi bir dizide saklamak, aşağıdaki koşullar geçerli olduğunda çok kullanışlıdır:

  1. Az sayıda veya bilinen maksimum oyun nesnesi var
  2. Performansa göre sadeliği tercih edersiniz (yani: ağaçlar gibi daha optimize edilmiş veri yapıları sorun değil)

Her durumda, bu çözümü gameObject_ptryapı içeren sabit boyutlu bir dizi olarak uyguladım . Bu yapı oyun nesnesinin kendisine bir işaretçi ve nesnenin "canlı" olup olmadığını temsil eden bir boolean değeri içeriyordu.

Booleanın amacı oluşturma ve silme işlemlerini hızlandırmaktır. Oyun nesnelerini simülasyondan çıkardıklarında yok etmek yerine, sadece "canlı" bayrağını ayarlarım false.

Nesneler üzerinde hesaplamalar yapılırken, kod dizide dolaşır, "canlı" olarak işaretlenmiş nesneler üzerinde hesaplamalar gerçekleştirir ve yalnızca "canlı" olarak işaretlenmiş nesneler oluşturulur.

Diğer bir basit optimizasyon diziyi nesne türüne göre organize etmektir. Örneğin, tüm madde işaretleri, dizinin son n hücrelerinde yaşayabilir . Ardından, dizinin değerine bir int veya dizenin bir göstergesine kaydederek, belirli tiplere daha düşük bir maliyetle referans verilebilir.

Dizi kabul edilemez derecede büyük olacağından, bu çözümün büyük miktarda veri içeren oyunlar için iyi olmadığını söylemeye gerek yoktur. Ayrıca, kullanılmayan nesnelerin hafızayı almasını yasaklayan hafıza kısıtlamalı oyunlar için uygun değildir. Sonuç olarak, bir noktada sahip olacağınız oyun nesnesi sayısında bilinen bir üst sınırınız varsa, bunları statik bir dizide saklamanın yanlış bir tarafı yoktur.

Bu benim ilk gönderim! Umarım sorunuza cevap verir!


ilk gönderi! yuppi!
Tili

0

Olağandışı olmasına rağmen kabul edilebilir. "Daha hızlı" ve "daha verimli" gibi terimler görecelidir; tipik olarak, programlamayı daha organize bir hale getirmek için oyun nesnelerini ayrı kategoriler halinde düzenlersiniz (yani bir mermi listesi, düşmanlar için bir liste vb.), ancak bilgisayarın tüm nesneler arasında daha hızlı dönmesi gibi olmaz .


2
Doğru çoğu XNA oyunlar açısından yeterli, ama Nesnelerinizi organize edebilirsiniz aslında daha hızlı içlerinden iterating olun: Aynı türden nesneler daha muhtemel CPU talimat ve veri önbelleği vurmak için vardır. Önbellek özlüyor, özellikle konsollarda pahalıdır. Örneğin Xbox 360'ta, bir L2 önbellek özeti size ~ 600 devire mal olacak. Masada çok fazla performans bırakıyor. Bu, yönetilen kodun yerel kod üzerinde bir avantaja sahip olduğu bir yerdir: zaman içinde birbirine yakın tahsis edilen nesneler de belleğe yakın olacak ve durum çöp toplama (sıkıştırma) ile düzelecektir.
Kronik Oyun Programcısı

0

Tek dizi ve nesneler diyorsunuz.

Bu yüzden (bakacak kodun olmadan) hepsinin 'uberGameObject' ile ilgili olduğunu tahmin ediyorum.

Yani belki iki sorun.

1) Bir 'tanrı' nesnesine sahip olabilirsiniz - tonlarca bir şey yapar, ne yaptığına veya ne olduğuna göre ayrılmaz.

2) Bu listeyi ve türünü dökümde yinelemek? veya her birinin kutusunu açmak. Bunun büyük bir performans cezası var.

farklı türleri (mermiler, kötü adamlar, vb.) kendi kuvvetlice yazılan listelerine bölmeyi düşünün.

List<BadGuyObj> BadGuys = new List<BadGuyObj>();
List<BulletsObj> Bullets = new List<BulletObj>();

 //Game 
OnUpdate(gameticks gt)
{  foreach (BulletObj thisbullet in Bullets) {thisbullet.Update();}  // much quicker.

Güncelleme döngüsünde (örn. update()) Çağrılacak tüm yöntemlere sahip bir tür ortak temel sınıf veya arabirim varsa, tür dökümünü gerçekleştirmeye gerek yoktur .
Bummzack

0

Genel tek liste ve her nesne tipi için ayrı listeler arasında bir uzlaşma önerebilirim.

Birden fazla listedeki bir nesneye başvuruyu koru. Bu listeler temel davranışlarla tanımlanır. Örneğin, MarineSoldiernesne başvurulan edilecektir PhysicalObjList, GraphicalObjList, UserControlObjList, HumanObjList. Bu yüzden, fiziği PhysicalObjListişlediğinizde, zehirden zarar veren süreç - HumanObjListeğer Asker ölürse - onu uzaklaştırın HumanObjListama içeride tutun PhysicalObjList. Bu mekanizmanın çalışmasını sağlamak için oluşturulduğunda / silindiğinde otomatik ekleme / çıkarma nesnesi düzenlemelisiniz.

Yaklaşımın Avantajları:

  • Nesnelerin yazılı organizasyonu ( AllObjectsListgüncellemede yayın yok )
  • listeler nesne sınıflarına değil mantığa dayalıdır.
  • birleştirilmiş yeteneklere sahip nesnelerle sorun yok ( MegaAirHumanUnderwaterObjecttürü için yeni liste oluşturmak yerine ilgili listelerde eklenir)

Not: Kendini güncellemeye gelince, birden fazla nesne etkileşime girdiğinde ve her biri diğerlerine bağlı olduğunda sorunla karşılaşacaksın. Bunu çözmek için nesneyi kendi kendini güncellemenin içinde değil dıştan etkilememiz gerekir.

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.