Oyun Bileşenleri, Oyun Yöneticileri ve Nesne Özellikleri


15

Bileşen tabanlı varlık tasarımına yönelmeye çalışıyorum.

İlk adımım, bir nesneye eklenebilecek çeşitli bileşenler oluşturmaktı. Her bileşen türü için, her bileşenin güncelleme işlevini çağıracak, gerektiği gibi klavye durumu vb.

Yaptığım bir sonraki şey, nesneyi kaldırmak ve her bir bileşeni bir kimliğe sahip olmaktı. Yani bir nesne aynı ID'lere sahip bileşenler tarafından tanımlanır.

Şimdi, tüm bileşenlerim için bir yöneticiye ihtiyacım olmadığını düşünüyorum, örneğin SizeComponentsadece bir Sizeözelliği olan bir var). Sonuç olarak, SizeComponentgüncelleme yöntemi yoktur ve yöneticinin güncelleme yöntemi hiçbir şey yapmaz.

İlk düşüncem, ObjectPropertybileşenlerin özellikleri olarak kullanmak yerine bileşenlerin sorgulayabileceği bir sınıfa sahip olmaktı. Yani bir nesnenin bir dizi ObjectPropertyve değeri olacaktır ObjectComponent. Bileşenler, nesneyi özellikler için sorgulayan güncelleme mantığına sahip olacaktır. Yönetici, bileşenin güncelleme yöntemini çağırmayı yönetir.

Bu bana aşırı mühendislik gibi görünüyor, ama ben bileşenleri kurtulmak sanmıyorum, çünkü yöneticilerin hangi bileşen mantığı çalıştırmak için hangi nesnelerin ihtiyacı olduğunu bilmek için bir yol gerekir (aksi takdirde sadece bileşeni kaldırmak istiyorum tamamen güncelleyin ve güncelleme mantığını yöneticiye aktarın).

  1. Bu (sahip ObjectPropertyolmak ObjectComponentve ComponentManagersınıflar) aşırı mühendislik mi?
  2. İyi bir alternatif ne olurdu?

1
Bileşen modelini öğrenmeye çalışarak doğru fikre sahipsiniz, ancak ne yapması gerektiğini daha iyi anlamanız gerekiyor - ve bunu yapmanın tek yolu bir oyunu kullanmadan [çoğunlukla] tamamlamaktır. Bence a SizeComponentyapmak aşırıya kaçmış - çoğu nesnenin bir boyutu olduğunu varsayabilirsiniz - bu bileşen modelinin kullanıldığı render, AI ve fizik gibi şeyler; Boyut her zaman aynı şekilde davranacaktır - böylece bu kodu paylaşabilirsiniz.
Jonathan Dickinson


@JonathanDickinson, @Den: Sanırım, o zaman benim sorunum ortak özellikleri nerede sakladığım. Örneğin a RenderingComponentve a tarafından kullanılan bir pozisyon olarak bir nesne PhysicsComponent. Mülkü nereye koyacağımı düşünmüyor musunuz? Ben sadece ya da sopa, diğer sorgu gerekli özelliği olan bileşen için bir nesne var mı?
George Duckett

Önceki yorumum ve arkasındaki düşünce süreci, beni bileşenlerin sorgulayabileceği bir özellik (veya ilgili özellik grubu) için ayrı bir sınıfa yönlendiren şey.
George Duckett

1
Bu fikri gerçekten seviyorum - denemeye değer olabilir; ancak her bir mülkü tanımlamak için bir nesneye sahip olmak gerçekten pahalıdır. PhysicalStateInstanceBir GravityPhysicsShared(oyun başına bir) ile birlikte ( nesne başına bir) deneyebilirsiniz ; ancak bunun mimarların öfori alemlerine girmeye başladığını söylemeye cazipim, kendinizi bir deliğe (tam olarak ilk bileşen sistemimle yaptığım gibi) mimar etmeyin. ÖPMEK.
Jonathan Dickinson

Yanıtlar:


6

İlk sorunuzun basit cevabı Evet, tasarımı aşırı tasarlamaktasınız. 'Bir şeyleri ne kadar yıkarım?' Soru bir sonraki adım atıldığında ve merkezi nesne (genellikle Varlık olarak adlandırılır) kaldırıldığında çok yaygındır.

Nesneleri kendi başına büyüklüğe sahip olacak kadar ayrıntılı bir seviyeye böldüğünüzde tasarım çok ileri gitti. Tek başına bir veri değeri bir bileşen değildir. Bu yerleşik bir veri türüdür ve genellikle tam olarak onları çağırmaya başladığınız bir özellik olarak adlandırılabilir. Özellik bir bileşen değildir, ancak bileşen özellikler içerir.

Bileşen sisteminde geliştirirken denediğim ve takip ettiğim birkaç kılavuz satır:

  • Hiç kaşık yok.
    • Bu, merkezi nesneden kurtulmak için atmış olduğunuz adımdır. Bu, Entity nesnesine neyin girdiği ve bir bileşene neyin girdiği konusundaki tartışmaların tümünü kaldırır çünkü artık sahip olduğunuz tek şey bileşenlerdir.
  • Bileşenler yapı değildir
    • Bir şeyi sadece veri içerdiği yere ayırırsanız, artık bir bileşen değil, sadece bir veri yapısıdır.
    • Bir bileşen, çok özel bir görevi belirli bir şekilde gerçekleştirmek için gereken tüm işlevleri içermelidir.
    • IRenderable arabirimi, oyundaki her şeyi görsel olarak görüntülemek için genel bir çözüm sunar. CRenderableSprite ve CRenderableModel, bu arayüzün sırasıyla 2D ve 3D olarak işlenmesini sağlayan özellikleri sağlayan bir bileşen uygulamasıdır.
    • IUseable, bir oyuncunun etkileşimde bulunabileceği bir şey için arabirimdir. CUseableItem, aktif silahı ateşleyen veya seçilen iksiri içen bileşen olurken, CUseableTrigger bir oyuncunun bir tarete atlaması veya asma köprüyü düşürmek için bir kol atması için olabilir.

Yani bileşenlerin yapıları olmadığından, SizeComponent çok uzaklara ayrılmıştır. Yalnızca veri içerir ve bir şeyin boyutunu tanımlayan şey değişebilir. Örneğin, bir oluşturma bileşeninde 1d skaler veya 2 / 3d vektör olabilir. Bir fizik bileşeninde, nesnenin sınırlayıcı hacmi olabilir. Bir envanter öğesinde, 2B ızgarada ne kadar yer kaplayabileceği olabilir.

Teori ve pratiklik arasında iyi bir çizgi çizmeye çalışın.

Bu yardımcı olur umarım.


Bazı platformlarda, bir Arayüzden bir işlev çağırmanın bir üst Sınıftan bir işlev çağırmaktan daha uzun olduğunu unutmayın (cevabınız arayüzler ve sınıflardan bahsedildiği için)
ADB

Hatırlamak için iyi bir nokta, ama dili agnostik olarak kullanmaya çalışıyordum ve sadece genel tasarım terimleriyle kullanmaya çalışıyordum.
James

"Bir şeyi sadece veri içerdiği yere ayırırsanız, artık bir bileşen değil, sadece bir veri yapısıdır." -- Neden? "Bileşen" o kadar genel bir kelimedir ki veri yapısı anlamına da gelebilir.
Paul Manta

@PaulManta Evet, genel bir terimdir, ancak bu sorunun ve cevabın tüm noktası, nerede çizgi çizileceğidir. Benim yanıtladığım gibi, cevabım, sadece bunu yapmak için basit bir kural önerim. Her zaman olduğu gibi, teori veya tasarım düşüncelerinin gelişmeyi yönlendiren şey olmasına asla izin vermemeyi savunuyorum, bu ona yardım etmeyi amaçlıyordu.
James

1
@ James Bu ilginç bir tartışma idi. :) chat.stackexchange.com/rooms/2175 Uygulamanızın diğer bileşenlerin ilgilendiği şey hakkında çok fazla bilgi sahibi olması benim en büyük yakınlığım . Gelecekte tartışmaya devam etmek istiyorum.
Paul Manta

14

Zaten bir yanıtı kabul ettiniz, ama işte bir CBS'deki bıçaklamam. Jenerik bir Componentsınıfın bazı sınırlamaları olduğunu buldum , bu yüzden GDC 2009'da Radical Entertainment tarafından tarif edilen Attributesve bileşenleri ayırmayı öneren bir tasarımla gittim Behaviors. (" Oyun Nesnesi Bileşen Mimarisinin Kuramı ve Uygulaması ", Marcin Chady)

Tasarım kararlarımı iki sayfalık bir belgede açıklıyorum. Bağlantıyı yayınlayacağım çünkü hepsini buraya yapıştırmak çok uzun. Şu anda sadece mantık bileşenlerini (oluşturma ve fizik bileşenlerini değil) kapsar, ancak ne yapmaya çalıştığım hakkında bir fikir vermelidir:

http://www.pdf-archive.com/2012/01/08/entity-component-system/preview/page/1

İşte belgeden bir alıntı:

Kısaca Öznitelikler ve Davranışlar

Attributesbir veri kategorisini yönetmek ve sahip oldukları mantık kapsamı sınırlıdır. Örneğin, Healthgeçerli değerinin hiçbir zaman maksimum değerinden daha büyük olmadığından emin olabilir ve geçerli değer belirli bir kritik düzeyin altına düştüğünde diğer bileşenleri bile bildirebilir, ancak daha karmaşık bir mantık içermez. Attributesbaşkasına bağımlı değildir Attributesveya Behaviors.

Behaviorsişletmenin oyun etkinliklerine nasıl tepki vereceğini kontrol edebilir, kararlar verebilir ve değerlerini Attributesgerektiği gibi değiştirebilir . Behaviorsbağımlı bazı ait Attributes, ancak doğrudan etkileşim birbirleriyle olamaz - sadece nasıl tepki Attributes’diğeri tarafından değiştirilir değerleri Behaviorsve gönderildikleri olaylara.


Düzenleme: Ve burada bileşenlerin birbirleriyle nasıl iletişim kurduğunu gösteren bir ilişki diyagramı:

Nitelikler ve Davranışlar arasındaki iletişim diyagramı

Bir uygulama ayrıntısı: varlık düzeyi EventManager yalnızca kullanılıyorsa oluşturulur. EntitySınıf sadece bir işaretçi saklar EventManageryalnızca bazı bileşen istekler bunu başlatılır söyledi.


Düzenleme: Başka bir soruya ben de buna benzer bir cevap verdi. Sistemin belki de daha iyi bir açıklaması için burada bulabilirsiniz:
/gamedev//a/23759/6188


2

Gerçekten ihtiyacınız olan özelliklere ve ihtiyacınız olan yere bağlıdır. Sahip olacağınız bellek miktarı ve kullanacağınız işlem gücü / türü. Aşağıdakileri gördüm ve yapmaya çalıştım:

  • Birden çok bileşen tarafından kullanılan ancak yalnızca bir bileşen tarafından değiştirilen özellikler bu bileşende depolanır. Şekil, AI sisteminin, fizik sisteminin ve oluşturma sisteminin temel şekle erişmesi gereken bir oyuna iyi bir örnektir, ağır bir özelliktir ve mümkünse sadece bir yerde kalmalıdır.
  • Konum gibi özelliklerin bazen çoğaltılması gerekir. Örneğin, birden fazla sistemi paralel olarak çalıştırırsanız, sistemlere göz atmaktan kaçınmak istersiniz ve konumu senkronize etmeyi tercih edersiniz (ana bileşenden kopyalayın veya deltalar üzerinden senkronize edin veya gerekirse bir çarpışma geçişi ile).
  • Kontrollerden veya AI "niyetlerinden" kaynaklanan özellikler, dışarıdan görünmeden diğer sistemlere uygulanabildikleri için özel bir sistemde saklanabilir.
  • Basit özellikler karmaşıklaşabilir. Bazen çok fazla veri paylaşmanız gerekiyorsa konumunuz için özel bir sistem gerekir (konum, yönlendirme, çerçeve deltası, toplam delta akımı hareketi, mevcut çerçeve ve önceki çerçeve için delta hareketi, döndürme ...). Bu durumda, sisteme gitmeniz ve özel bileşenden en son verilere erişmeniz ve akümülatörler (deltalar) aracılığıyla değiştirmeniz gerekebilir.
  • Bazen özellikleriniz ham bir dizide (çift *) saklanabilir ve bileşenlerinizde, farklı özellikleri barındıran dizilere yalnızca işaretçiler bulunur. En belirgin örnek, büyük paralel hesaplamalara (CUDA, OpenCL) ihtiyaç duyduğunuz zamandır. Bu nedenle, işaretçileri düzgün bir şekilde yönetmek için bir sisteme sahip olmak bir avuç olabilir.

Bu ilkelerin sınırlamaları vardır. Tabii ki geometriyi oluşturucuya itmeniz gerekecek, ancak muhtemelen oradan almak istemeyeceksiniz. Ana geometri, orada deformasyonların meydana gelmesi ve oluşturucu ile senkronize edilmesi durumunda (nesnelerin mesafesine bağlı olarak zaman zaman) fizik motorunda saklanacaktır. Yani bir şekilde yine de çoğaltacaksınız.

Mükemmel sistemler yoktur. Ve bazı oyunlar daha basit bir sistemle daha iyi olacak, diğerleri ise sistemler arasında daha karmaşık senkronizasyonlar gerektirecektir.

İlk önce tüm özelliklere bileşenlerinizden basit bir şekilde erişilebildiğinden emin olun, böylece sistemlerinizi hassas şekilde ayarlamaya başladıktan sonra özellikleri şeffaf bir şekilde saklama şeklinizi değiştirebilirsiniz.

Bazı özelliklerin kopyalanmasında utanç yoktur. Birkaç bileşenin yerel bir kopyası bulundurması gerekiyorsa, bazen "harici" bir değere erişmek yerine kopyalamak ve senkronize etmek daha verimlidir

Ayrıca senkronizasyonun her karede olması gerekmez. Bazı bileşenler diğerlerinden daha az senkronize edilebilir. İşleme bileşeni genellikle iyi bir örnektir. Oyuncularla etkileşime girmeyenler, tıpkı çok uzakta olanlar gibi daha az senkronize edilebilir. Kamera alanının dışında ve dışında olanlar daha az sıklıkta senkronize edilebilir.


Boyut bileşeniniz söz konusu olduğunda, muhtemelen konum bileşeninizde paketlenmiş olabilir:

  • büyüklükteki tüm varlıkların bir fizik bileşeni, örneğin alanları yoktur, bu nedenle onu fizikle birleştirmek sizin için en iyisi değildir.
  • boyut muhtemelen pozisyon olmadan önemli olmayacaktır
  • konumu olan tüm nesneler muhtemelen bir boyuta sahip olacaktır (komut dosyaları, fizik, AI, render ... için kullanılabilir).
  • boyut muhtemelen her döngüde güncellenmez, ancak konum olabilir.

Boyut yerine bir boyut değiştirici bile kullanabilirsiniz, daha kullanışlı olabilir.

Tüm mülkleri genel bir mülk depolama sisteminde depolamaya gelince ... Doğru yöne gittiğinizden emin değilim ... Oyununuzun çekirdeğini oluşturan özelliklere odaklanın ve mümkün olduğunca çok sayıda ilgili özelliği bir araya getiren bileşenler oluşturun. Bu özelliklere erişimi (örneğin, bunlara ihtiyaç duyan bileşenlerdeki alıcılar aracılığıyla) düzgün bir şekilde soyutladığınız sürece, çok fazla mantık kırmadan bunları taşıyabilir, kopyalayabilir ve senkronize edebilirsiniz.


2
BTW +1 veya -1 beni çünkü şu anki temsilcim 9 Kasım'dan beri 666 ... Ürpertici.
Coyote

1

Bileşenler keyfi olarak varlıklara eklenebilirse, bir varlıkta belirli bir bileşen olup olmadığını sorgulamak ve ona bir başvuru almak için bir yol bulmanız gerekir. Böylece, istediğinizi bulana kadar ObjectComponent öğesinden türetilen bir nesne listesi üzerinde yineleme yapabilirsiniz. Ancak doğru türde bir nesne döndürürsünüz.

C ++ veya C # 'da bu genellikle varlıkta bir şablon yönteminizin olacağı anlamına gelir T GetComponent<T>() . Ve bu referansa sahip olduğunuzda, hangi üye verilerini tuttuğunu tam olarak bilirsiniz, bu yüzden doğrudan erişin.

Lua veya Python gibi bir şeyde, o nesnenin açık bir türüne sahip olmanız gerekmez ve muhtemelen da umursamazsınız. Ancak yine, üye değişkenine erişebilir ve orada olmayan bir şeye erişmeye çalışmaktan kaynaklanan herhangi bir istisnayı işleyebilirsiniz.

Nesne özellikleri için sorgulama, statik olarak yazılan diller için derleme zamanında veya dinamik olarak yazılan diller için çalışma zamanında, dilin sizin için yapabileceği işi çoğaltmak gibi görünür.


Bir varlıktan (örneğin jenerikler veya benzeri) güçlü bir şekilde türetilen bileşenler almayı anlıyorum, sorum daha çok bu özelliklerin nereye gitmesi gerektiği hakkında, özellikle de bir özellik birden çok bileşen tarafından kullanıldığında ve tek bir bileşene sahip olduğu söylenemiyor. Soru hakkındaki 3. ve 4. yorumlarıma bakın.
George Duckett

Sadece uygunsa mevcut olanı seçin veya özelliği yoksa yeni bir bileşene ekleyin. Örneğin, Unity'nin sadece konum olan bir 'Dönüştürme' bileşeni vardır ve nesnenin konumunu değiştirmek için başka bir şey gerekiyorsa, bunu bu bileşen aracılığıyla yaparlar.
Kylotan

1

"Sanırım, benim sorunum nerede ortak özellikleri saklamak olduğunu. Örneğin bir RenderingComponent ve PhysicsComponent tarafından kullanılan bir konum olarak bir nesne. Ben özelliği koymak için karar aşırı düşünüyor musunuz? her ikisinde de, gerekli sorguya sahip bileşen için diğer sorguyu sorgula? "

Şey RenderingComponent olmasıdır kullanır pozisyon, ancak PhysicsComponent sağlar bunu. Her kullanıcı bileşenine hangi sağlayıcının kullanılacağını söylemenin bir yolu vardır. İdeal olarak agnostik bir şekilde, aksi takdirde bir bağımlılık olacaktır.

“... sorum daha çok bu özelliklerin nereye gitmesi gerektiğiyle ilgili, özellikle de bir özelliğin birden çok bileşen tarafından kullanıldığı ve hiçbir bileşenin kendisine ait olduğu söylenemediğinde. Soruyla ilgili 3. ve 4. yorumlarıma bakın.”

Ortak bir kural yoktur. Belirli bir özelliğe bağlıdır (yukarıya bakın).

Çirkin ama bileşen tabanlı bir mimariye sahip bir oyun yapın ve yeniden düzenleyin.


PhysicsComponentO zaman ne yapacağımı tam olarak anladığımı sanmıyorum . Fiziksel bir ortamda nesneyi simüle etmeyi yönetme olarak görüyorum, bu da beni bu karışıklığa götürüyor: Oluşturulması gereken her şeyin simüle edilmesi gerekmeyecek, bu yüzden bir konum içerdiğinden PhysicsComponenteklediğimde eklemem yanlış görünüyor RenderingComponento RenderingComponentkullanımlar. Kendimi birbirine bağlı bileşenlerden oluşan bir ağla bitirdiğimi kolayca görebiliyordum, yani her bir varlığa tüm / çoğunun eklenmesi gerekiyor.
George Duckett

Ben de benzer bir durum yaşadım :). Bir PhysicsBodyComponent ve SimpleSpatialComponent var. Her ikisi de Konum, Açı ve Boyut sağlar. Ancak birincisi fizik simülasyonuna katılır ve ek ilgili özelliklere sahiptir ve ikincisi bu uzamsal verileri tutar. Kendi fizik motorunuz varsa, birincisini ikincisinden miras alabilirsiniz.
Den

Diyerek şöyle devam etti: "Kendimi birbirine bağlı bileşenlerden oluşan bir ağla sonlandığımı kolayca görebiliyordum, yani her bir varlığa tüm / çoğunun eklenmesi gerekiyor." Çünkü gerçek bir oyun prototipiniz yok. Burada bazı temel bileşenlerden bahsediyoruz. Her yerde kullanılacaklarına şaşmamalı.
Den

1

Sizin gut o sahip size söylüyor ThingProperty, ThingComponentve ThingManagerher için Thingbileşen a türüne biraz overkill. Bence doğru.

Ancak, hangi bileşenleri kullandıkları, hangi varlıklara ait oldukları vb. Açısından ilgili bileşenleri takip etmek için bir yol bulmanız gerekir.

TransformPropertyoldukça yaygın olacak. Ama bundan kim sorumlu, oluşturma sistemi? Fizik sistemi? Ses sistemi? Neden bir Transformbileşenin kendini güncellemesi gerekiyor?

Çözüm, mülklerinizden alıcılar, ayarlayıcılar ve başlatıcılar dışındaki her türlü kodu kaldırmaktır. Bileşenler, oyundaki sistemler tarafından oluşturma, AI, ses çalma, hareket vb. Çeşitli görevleri yerine getirmek için kullanılan verilerdir.

Artemis hakkında okuyun: http://piemaster.net/2011/07/entity-component-artemis/

Koduna bakın ve bağımlılıklarını bir liste olarak bildiren Sistemler temelinde olduğunu göreceksiniz ComponentTypes. SystemSınıflarınızın her birini yazıyorsunuz ve yapıcı / init yönteminde sistemin hangi türlere bağlı olduğunu bildiriyorsunuz.

Seviyeler veya ne olursa olsun kurulum sırasında varlıklarınızı oluşturur ve bunlara bileşenler eklersiniz. Daha sonra bu işletmeye Artemis'e rapor vermesini söylersiniz ve Artemis, o sistemin bileşimine dayanarak sistemlerin o varlık hakkında bilgi edinmek isteyeceğini anlar.

Ardından, döngünüzün güncelleme aşamasında, Systemartık güncellemeniz gereken varlıkların bir listesi var. Eğer bir takım, yapı varlıkları deli sistemlerinin tasarlanması böylece Şimdi bileşenlerin ayrıntı düzeyini olabilir ModelComponent, TransformComponent, FliesLikeSupermanComponentveSocketInfoComponent marka gibi garip bir şey ve bunu müşterileri arasında sinekler çok oyunculu bir oyuna bağlı olduğu bir uçan daire. Tamam, belki de değil, ama fikir şu ki, şeyleri birbirinden ayrı ve esnek tutar.

Artemis mükemmel değildir ve sitedeki örnekler biraz basittir, ancak kod ve verilerin ayrılması güçlüdür. Doğru yaparsanız önbelleğiniz için de iyidir. Artemis muhtemelen bu cephede yapmıyor, ancak öğrenmesi iyi.

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.