Render mantığını GameObject sınıfının dışına taşımak için taktikler


10

Oyun yaparken genellikle tüm varlıkların miras aldığı şu oyun nesnesini yaratırsınız:

public class GameObject{
    abstract void Update(...);
    abstract void Draw(...);
}

Böylece, güncelleme döngüsünde tüm oyun nesneleri üzerinde yineleme yaparsınız ve onlara durum değiştirme şansı verirsiniz, daha sonra bir sonraki çizim döngüsünde tüm oyun nesneleri üzerinde tekrarlanır ve onlara kendilerini çizme şansı verirsiniz.

Bu, basit bir ön oluşturucu ile basit bir oyunda oldukça iyi çalışmasına rağmen, genellikle modellerini, çoklu dokuları ve oyun nesnesi arasında sıkı bir bağlantı oluşturan yağ çekme yönteminin en kötüsünü depolaması gereken birkaç dev oyun nesnesine yol açar. mevcut render stratejisi ve render ile ilgili sınıflar.

Eğer render stratejisini ileri seviyeden ertelemeye değiştirseydim, bir çok oyun nesnesini güncellemem gerekecekti. Ve yaptığım oyun nesneleri olabildiğince tekrar kullanılamaz. Elbette miras ve / veya kompozisyon, kod çoğaltma ile savaşmama ve uygulamayı değiştirmeyi biraz daha kolay hale getirmeme yardımcı olabilir, ancak yine de eksiktir.

Belki de daha iyi bir yol, Draw yöntemini GameObject sınıfından tamamen kaldırmak ve bir Renderer sınıfı oluşturmaktır. GameObject'in hangi görselle temsil edileceği ve modelde hangi dokuların boyanması gerektiği gibi görselleriyle ilgili bazı veriler içermesi gerekir, ancak bunun nasıl yapıldığı oluşturucuya bırakılır. Bununla birlikte, genellikle render sırasında çok sayıda sınır durumu vardır, bu nedenle bu, GameObject'ten Renderer'a sıkı bağlantıyı kaldıracak olsa da, Renderer hala şişmanlaştıracak tüm oyun nesnelerini bilmeli, sıkı bağlanmış. Bu, birkaç iyi uygulamayı ihlal edecektir. Belki Veri Odaklı-Tasarım hile yapabilir. Oyun nesneleri kesinlikle veri olacaktır, ancak oluşturucu bununla nasıl yönlendirilir? Emin değilim.

Bu yüzden kaybım var ve iyi bir çözüm düşünemiyorum. MVC ilkelerini kullanmaya çalıştım ve geçmişte bunu oyunlarda nasıl kullanacağım hakkında bazı fikirlerim vardı, ancak son zamanlarda düşündüğüm kadar uygulanabilir görünmüyordu. Hepinizin bu problemle nasıl başa çıktığını bilmek isterim.

Her neyse, özetleyelim, aşağıdaki tasarım hedeflerine nasıl ulaşılabileceğiyle ilgileniyorum.

  • Oyun nesnesinde oluşturma mantığı yok
  • Oyun nesneleri ve render motoru arasında gevşek bağlantı
  • Hiçbir şey bilmeyen renderer
  • Tercihen render motorları arasında çalışma zamanı geçişi

İdeal proje kurulumu ayrı bir 'oyun mantığı' olacaktır ve birbirlerine referans vermesi gerekmeyen mantık projesi oluşturur.

Bu düşünce treni, John Carmack'in Twitter'da, çalışma motorlarını çalışma zamanında değiştirebileceği ve hatta sistemine her iki oluşturucuyu (bir yazılım oluşturucu ve donanım hızlandırmalı bir oluşturucu) kullanmasını söyleyebileceği kadar esnek bir sistem olduğunu söylediğinde duyduğumda başladı. aynı zamanda farklılıkları denetleyebilir. Şimdiye kadar programladığım sistemler bu esnekliğe bile yakın değil

Yanıtlar:


7

Ayırma için hızlı bir ilk adım:

Oyun nesneleri, görsellerinin ne olduğunu ancak verilerin değil bir tanımlayıcıyı referans alıyor, dize gibi basit bir şey söyleyelim. Örnek: "human_male"

Renderer, "human_male" referanslarını yüklemek ve sürdürmekten ve kullanılacak bir tutamacı nesnelere geri vermekten sorumludur.

Korkunç sahte kod örneği:

GameObject( initialization parameters )
  me.render_handle = Renderer_Create( parameters.render_string )

- elsewhere
Renderer_Create( string )

  new data handle = Resources_Load( string );
  return new data handle

- some time later
GameObject( something happens to me parameters )
  me.state = something.what_happens
  Renderer_ApplyState( me.render_handle, me.state.effect_type )

- some time later
Renderer_Render()
  for each renderable thing
    for each rendering back end
        setup graphics for thing.effect
        render it

- finally
GameObject_Destroy()
  Renderer_Destroy( me.render_handle )

Bu karışıklık için özür dilerim, koşullarınız, gerçek dünya nesneleri gibi şeylere bakarak saf OOP'tan ve sorumluluklara göre OOP'a yapılan bu basit değişiklikle karşılanır.

  • Oyun nesnesinde oluşturma mantığı yok (bitti, tüm nesnenin bildiği bir tanıtıcıdır, böylece efektleri kendisine uygulayabilir)
  • Oyun nesneleri ve render motoru arasındaki gevşek bağlantı (bitti, tüm temaslar soyut bir tutamaçtan, bu eyaletlerle ne yapılacağı değil, uygulanabilen durumlar)
  • Hiç kimse bilmiyor renderer (bitti, sadece kendini biliyor)
  • Tercihen render motorları arasında çalışma zamanı geçişi (bu Renderer_Render () aşamasında yapılır, ancak her iki arka ucu da yazmanız gerekir)

Sınıfların basit bir şekilde yeniden hesaplanmasının ötesine geçmek için arama yapabileceğiniz anahtar kelimeler, sadece eski beyin dişlilerinin dönmesini sağlamak için "varlık / bileşen sistemi" ve "bağımlılık enjeksiyonu" ve potansiyel olarak "MVC" GUI kalıpları olacaktır.


Bu, daha önce yaptığımdan çok farklı, biraz potansiyel var gibi görünüyor. Neyse ki mevcut herhangi bir motor tarafından kısıtlanmam, böylece sadece uğraşabiliyorum. Bağımlılık enjeksiyonu her zaman beynimi incitirse de, bahsettiğiniz terimlere de bakacağım: P.
Roy T.

2

Kendi motorum için yaptığım her şeyi modüller halinde gruplandırmak. Bu yüzden GameObjectsınıfım var ve bir kolu var:

  • ModuleSprite - çizim spriteları
  • ModuleWeapon - ateş eden silahlar
  • ModuleScriptingBase - komut dosyası oluşturma
  • ModuleParticles - parçacık efektleri
  • ModuleCollision - çarpışma algılama ve yanıt

Bir Playersınıfım ve bir Bulletsınıfım var. Her ikisi de 'den türetilir GameObjectve buna eklenir Scene. Ancak Playeraşağıdaki modüllere sahiptir:

  • ModuleSprite
  • ModuleWeapon
  • ModuleParticles
  • ModuleCollision

Ve Bulletşu modüller var:

  • ModuleSprite
  • ModuleCollision

Bu şekilde bir şeyler düzenlemenin yolu a Vehicle, a VehicleLandve a VehicleWaterve şimdi istediğiniz bir "Ölüm Pırlantası" ndan kaçınır VehicleAmphibious. Bunun yerine bir Vehicleve bir ModuleWaterve bir olabilir ModuleLand.

Ek bonus: bir dizi özellik kullanarak nesne oluşturabilirsiniz. Bilmeniz gereken tek şey temel türdür (Oyuncu, Düşman, Madde İşareti, vb.) Ve daha sonra bu tür için ihtiyacınız olan modüller için tutamaçlar oluşturun.

Sahnemde aşağıdakileri yaparım:

  • UpdateTüm GameObjecttutamaçları arayın .
  • ModuleCollisionSapı olanlar için çarpışma kontrolü ve çarpışma tepkisi yapın .
  • Fizikten sonra son konumlarını bildirmek UpdatePostiçin tüm GameObjecttutamaçları arayın .
  • Bayrağı ayarlanmış nesneleri yok edin.
  • m_ObjectsCreatedListeden listeye yeni nesneler ekleyin m_Objects.

Ve onu daha da organize edebilirim: nesne yerine modüller tarafından. Sonra bir listesini oluşturmak ModuleSprite, bir demet güncellemek ModuleScriptingBaseve bir listesi ile çarpışmalar yapmak ModuleCollision.


Maks. Kompozisyona benziyor! Çok hoş. Yine de burada işleme ile ilgili çok fazla ipucu görmüyorum. Sadece farklı modüller ekleyerek bunu nasıl halledersiniz?
Roy T.

Oh evet. Bu sistemin dezavantajı budur: eğer belirli bir gereksiniminiz varsa GameObject(örneğin Spriteların "yılanını" yaratmanın bir yolu ) ya ModuleSpritebu belirli işlevsellik için bir çocuk oluşturmanız ( ModuleSpriteSnake) ya da tamamen yeni bir modül ( ModuleSnake). Neyse ki onlar sadece işaretçiler, ama GameObjectkelimenin tam anlamıyla bir nesnenin yapabileceği her şeyi nerede kod gördüm .
knight666
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.