Varlıklar ve Bileşenlerde yöntemleri saklamak neden kötü bir fikirdir? (Diğer Varlık Sistemi sorularıyla birlikte.)


16

Bu, cevapladığım bu sorunun bir takibi , ama bu çok daha spesifik bir konuyla başa çıkıyor.

Bu cevap Entity Systems'ı makaleden daha iyi anlamama yardımcı oldu.

Entity Systems hakkındaki (evet,) makaleyi okudum ve bana şunları söyledi:

Varlıklar sadece bir kimlik ve bir bileşen dizisidir (makaleler, varlıkları bileşenlerde depolamanın bir şeyler yapmak için iyi bir yol olmadığını, ancak bir alternatif sunmadığını söylüyor).
Bileşenler , belirli bir varlıkla neler yapılabileceğini gösteren veri parçalarıdır.
Sistemler "yöntemler" dir, varlıklar üzerindeki verilerin manipülasyonunu yaparlar.

Bu birçok durumda gerçekten pratik gibi görünüyor, ancak bileşenlerin sadece veri sınıfları olmasıyla ilgili kısım beni rahatsız ediyor. Örneğin, Vector2D sınıfımı (Pozisyon) bir Varlık Sistemine nasıl uygulayabilirim?

Vector2D sınıfı verileri tutar: x ve y koordinatları, ancak kullanışlılığı için çok önemli olan ve sınıfı sadece iki elemanlı bir diziden ayıran yöntemlere de sahiptir . Örnek yöntemler şunlardır: add(), rotate(point, r, angle), substract(), normalize(), ve diğer tüm standardı, ve kesinlikle gerekli yöntemleri (Vector2D sınıfının örnekleridir) konumları olması gerektiğini.

Bileşen yalnızca bir veri sahibi olsaydı, bu yöntemlere sahip olamazdı!

Muhtemelen ortaya çıkabilecek bir çözüm, bunları sistemlerin içine uygulamak olabilir, ancak bu çok sezgisel görünüyor. Bu yöntemler şimdi gerçekleştirmek istediğim , tam ve kullanıma hazır olmasını istediğim şeyler . MovementSystemBir varlığın pozisyonunda bir hesaplama yapmasını söyleyen bazı pahalı mesajları okumak için beklemek istemiyorum !

Makale, sadece sistemlerin herhangi bir işlevselliğe sahip olması gerektiğini açıkça belirtiyor ve bunun için bulabildiğim tek açıklama "OOP'dan kaçınmak" idi. Her şeyden önce, varlıklarda ve bileşenlerde yöntemleri kullanmaktan neden kaçınmam gerektiğini anlamıyorum. Bellek yükü pratik olarak aynıdır ve sistemlerle birleştiğinde bunların uygulanması ve ilginç şekillerde birleştirilmesi çok kolay olmalıdır. Örneğin sistemler, yalnızca uygulamayı bilen varlıklara / bileşenlere temel mantık sağlayabilir. Bana sorarsanız - bu temel olarak hem ES hem de OOP'tan güzellikler alıyor, makalenin yazarına göre yapılamayan bir şey, ama bana göre iyi bir uygulama gibi görünüyor.

Bu şekilde düşünün; oyunda birçok farklı çekilebilir nesne türü vardır. Sade eski görüntüler, animasyonlar ( update(), getCurrentFrame()vb.), Bu ilkel türlerin kombinasyonları ve hepsi draw()render sistemine bir varlık sağlayabilir, bu da bir varlığın sprite'ın nasıl uygulandığını umursamaya gerek yoktur, sadece arayüz (çizim) ve pozisyon hakkında. Ve sonra, yalnızca oluşturma ile ilgisi olmayan animasyona özgü yöntemleri çağıracak bir animasyon sistemine ihtiyacım olacaktı.

Ve bir şey daha ... Bileşenleri saklama konusunda dizilere gerçekten bir alternatif var mı? Bir Entity sınıfındaki dizilerden başka bileşenlerin depolanacağı başka bir yer göremiyorum ...

Belki de bu daha iyi bir yaklaşımdır: bileşenleri varlıkların basit özellikleri olarak saklayın. Örneğin, bir konum bileşenine yapıştırılabilir entity.position.

Diğer tek yol , farklı varlıklara gönderme yapan sistemlerin içinde garip bir arama tablosuna sahip olmaktır . Ancak bu, bileşenlerin yalnızca varlıkta depolanmasından çok, verimsiz ve daha karmaşık görünmektedir .


Alexandre, başka bir rozet almak için sadece çok sayıda düzenleme mi yapıyorsun? Bu yaramaz yaramaz olduğu için bir ton eski ipliği çarpmaya devam ediyor.
jhocking

Yanıtlar:


25

Bileşenlerdeki verilere erişmek, bunları güncellemek veya değiştirmek için basit yöntemlere sahip olmanın tamamen iyi olduğunu düşünüyorum. Bileşenlerden uzak durması gereken işlevsellik mantıksal işlevsellik olduğunu düşünüyorum. Fayda fonksiyonları gayet iyi. Unutmayın, varlık bileşeni sistemi, uymanız gereken katı kurallar değil, sadece bir kılavuzdur. Onları takip etmek için kendi yolundan gitme. Bir şekilde yapmanın daha mantıklı olduğunu düşünüyorsanız, bu şekilde yapın :)

DÜZENLE

Açıklığa kavuşturmak için hedefiniz OOP'tan kaçınmak değildir . Bu günlerde kullanılan ortak dillerin çoğunda bu oldukça zor olurdu. OOP'nin büyük bir yönü olan ancak gerekli olmayan kalıtımı en aza indirmeye çalışıyorsunuz . Object-> MobileObject-> Creature-> Bipedal-> İnsan türü mirastan kurtulmak istiyorsunuz.

Ancak, bazı miras almak sorun değil! Kalıtımdan büyük ölçüde etkilenen bir dil ile uğraşıyorsunuz, hiçbirini kullanmamak çok zor. Örneğin, Componenttüm diğer bileşenlerinizin genişlettiği veya uyguladığı bir sınıfınız veya arabiriminiz olabilir. SystemSınıfınızla aynı anlaşma . Bu işleri kolaylaştırır. Artemis çerçevesine bir göz atmanızı şiddetle tavsiye ederim . Açık kaynak kodlu ve bazı örnek projeleri var. Bu şeyleri açın ve nasıl çalıştığını görün.

Artemis için varlıklar basit bir dizide saklanır. Ancak, bileşenleri bir dizide veya dizilerde (varlıklardan ayrı olarak) depolanır. Üst düzey dizi, alt düzey diziyi bileşen türüne göre gruplandırır. Yani her bileşen türünün kendi dizisi vardır. Alt düzey dizi, varlık kimliğine göre dizine eklenir. (Şimdi bu şekilde yapıp yapmayacağımdan emin değilim, ama işte burada yapıldı). Artemis varlık kimliklerini yeniden kullanır, bu nedenle maksimum varlık kimliği geçerli varlık sayınızdan daha büyük olmaz, ancak bileşen sık kullanılan bir bileşen değilse yine de seyrek diziler olabilir. Her neyse, bunu fazla ayırmayacağım. Varlıkları ve bileşenlerini depolamak için bu yöntem işe yarıyor gibi görünüyor. Bence kendi sisteminizi hayata geçirirken ilk adımınızı atacaksınız.

Varlıklar ve bileşenler ayrı bir yöneticide saklanır.

Bahsettiğiniz strateji, varlıkların kendi bileşenlerini ( entity.position) depolamasını sağlamak, varlık bileşeni temasına aykırıdır, ancak bunun en mantıklı olduğunu düşünüyorsanız tamamen kabul edilebilir.


1
Hmm, durumu büyük ölçüde basitleştiriyor, teşekkürler! "Daha sonra pişman olacaksın" diye bir şey olduğunu düşündüm ve devam edemedim!
jcora

1
Na, bunları tamamen bileşen bileşen sistemimde kullanıyorum. Hatta bazı bileşenleri ortak bir ebeveynden o devralır, sahip gasp . Sana geçici bir çözüm çalıştı keşke yapardın pişman olduğunu düşünüyorum değil böyle yöntemler kullanılarak. Her şey sizin için en anlamlı olanı yapmakla ilgilidir. Kalıtım kullanmak veya bir bileşene bazı yöntemler koymak mantıklıysa, bunun için gidin.
MichaelHouse

2
Bu konudaki son cevabımdan öğrendim. Yasal Uyarı: Bu demiyorum bunu yapmanın yolu. :)
MichaelHouse

1
Evet, yeni bir paradigma öğrenmenin ne kadar göz korkutucu olabileceğini biliyorum. Neyse ki, işleri kolaylaştırmak için eski paradigmanın yönlerini kullanıyorsunuz! Cevabımı depolama bilgileriyle güncelledim. Artemis'e bakarsanız, EntityManagerişlerin saklandığı yer olan şeye bakın.
MichaelHouse

1
Güzel! Bu bittiğinde oldukça tatlı bir motor olacak. Onunla iyi şanslar! İlginç sorular sorduğunuz için teşekkürler.
MichaelHouse

10

'Bu' makale özellikle katıldığım bir makale değil, bu yüzden cevabımın biraz kritik olacağını düşünüyorum.

Bu birçok durumda gerçekten pratik gibi görünüyor, ancak bileşenlerin sadece veri sınıfları olmasıyla ilgili kısım beni rahatsız ediyor. Örneğin, Vector2D sınıfımı (Pozisyon) bir Varlık Sistemine nasıl uygulayabilirim?

Fikir, programınızdaki hiçbir şeyin varlık kimliği, bileşeni veya sistemden başka bir şey olmadığından emin olmak değildir - karmaşık bir miras ağacı kullanmaktan veya kötü bir şey denemekten ziyade nesne oluşturma yoluyla varlık verilerinin ve davranışlarının oluşturulmasını sağlamaktır. olası tüm işlevleri tek bir nesneye koyun. Bu bileşenleri ve sistemleri uygulamak için, çoğu dilde en iyi sınıf olarak temsil edilen vektörler gibi normal verilere sahip olursunuz.

Makalede bunun OOP olmadığını düşündüren biti görmezden gelin - diğer yaklaşımlar kadar OOP. Birçok derleyici veya dil çalışma zamanları nesne yöntemlerini uygulamak diye bir gizli argümanı hariç, bu tıpkı diğer işlev gibi temelde thisya selfo nesnenin verilerinin depolandığı bellekte bir yere bir göstericisi olan. Bileşen tabanlı bir sistemde varlık kimliği, belirli bir varlık için ilgili bileşenlerin (ve dolayısıyla verilerin) nerede olduğunu bulmak için kullanılabilir. Böylece varlık kimliği bu / kendi işaretçisine eşdeğerdir ve kavramlar temelde aynı şeydir, sadece biraz yeniden düzenlenmiştir.

Makale, sadece sistemlerin herhangi bir işlevselliğe sahip olması gerektiğini açıkça gösteriyor ve bunun için bulabildiğim tek açıklama "OOP'dan kaçınmak" idi. Her şeyden önce, varlıklarda ve bileşenlerde yöntemleri kullanmaktan neden kaçınmam gerektiğini anlamıyorum.

İyi. Yöntemler, kodunuzu düzenlemenin etkili bir yoludur. "OOP önlemek" fikrinden uzaklaşmak için önemli olan şey, işlevselliği genişletmek için her yerde kalıtım kullanmaktan kaçınmaktır. Bunun yerine, aynı şeyi yapmak için birleştirilebilecek bileşenlere işlevselliği ayırın.

Bu şekilde düşünün; oyunda birçok farklı çekilebilir nesne türü vardır. Düz eski görüntüler, animasyonlar (update (), getCurrentFrame (), vb), bu ilkel türlerin kombinasyonları ve hepsi sadece render sistemine bir draw () yöntemi sağlayabilir [...]

Bileşen tabanlı bir sistem fikri, bunlar için ayrı sınıflara sahip olmayacağınız, ancak tek bir Object / Entity sınıfına sahip olacağınız ve görüntünün ImageRenderer olan bir Object / Entity olacağı, Animations bir Object / AnimationRenderer, vb. Olan varlık. İlgili sistemler bu bileşenlerin nasıl oluşturulacağını bilir ve bu nedenle Draw () yöntemiyle herhangi bir temel sınıf olması gerekmez.

[...] o zaman bir varlığın hareketli grafiğinin nasıl uygulandığı, sadece arabirim (çizim) ve konum hakkında umurunda değildir. Ve sonra, yalnızca oluşturma ile ilgisi olmayan animasyona özgü yöntemleri çağıracak bir animasyon sistemine ihtiyacım olacaktı.

Tabii, ama bu bileşenlerle iyi çalışmıyor. 3 seçeneğiniz var:

  • Her bileşen bu arabirimi uygular ve hiçbir şey çizilmemiş olsa bile bir Draw () yöntemine sahiptir. Bunu her işlevsellik biti için yaptıysanız, bileşenler oldukça çirkin görünecektir.
  • Yalnızca çizilecek bir şeyleri olan bileşenler arabirimi uygular, ancak Draw () 'ı hangi bileşenleri çağırmaya karar verir? Bir sistemin hangi arabirimin desteklendiğini görmek için bir şekilde her bileşeni sorgulaması gerekir mi? Bu hataya açık ve bazı dillerde uygulanması potansiyel olarak zor olabilir.
  • Bileşenler yalnızca kendi sahip olma sistemleri tarafından ele alınır (bağlantılı makaledeki fikir budur). Bu durumda, arabirim önemsizdir, çünkü bir sistem hangi sınıf veya nesne türüyle çalıştığını tam olarak bilir.

Ve bir şey daha ... Bileşenleri saklama konusunda dizilere gerçekten bir alternatif var mı? Bir Entity sınıfındaki dizilerden başka bileşenlerin depolanacağı başka bir yer göremiyorum ...

Bileşenleri sistemde saklayabilirsiniz. Dizi sorun değil, bileşeni depoladığınız yerdir.


+1 Başka bir bakış açısı için teşekkürler. Bu kadar belirsiz bir konuyla uğraşırken birkaç tane almak iyidir! Bileşenleri sistemde saklıyorsanız, bu, bileşenlerin yalnızca bir sistem tarafından değiştirilebileceği anlamına mı gelir? Örneğin, çizim sistemi ve hareket sistemi konum bileşenine erişecektir. Nerede saklıyorsunuz?
MichaelHouse

Eh, bu bileşenlere yalnızca bir işaretçi depolarlardı, bu da bir yerde olduğum sürece ... Bileşenleri neden sistemlerde depolarsınız? Bunun bir avantajı var mı?
jcora

Doğru muyum, @Kylotan? Ben böyle yaparım, mantıklı görünüyor ...
jcora

Adam / T-Machine örneğinde, amaç bileşen başına 1 sistem olması, ancak bir sistemin diğer bileşenlere kesinlikle erişebilmesi ve değiştirebilmesidir. (Bu, bileşenlerin çok iş parçacıklı faydalarını engeller, ancak bu farklı bir konudur.)
Kylotan

1
Bileşenleri sistemde depolamak, o sistem için daha iyi referans konumlandırmasına izin verir - yalnızca bu sistem (genellikle) bu verilerle çalışır, bu yüzden neden almak için bilgisayarınızın belleğinden kurumun belleğine gidelim? Aynı zamanda eşzamanlılığa da yardımcı olur, çünkü tüm sistemi ve verilerini tek bir çekirdeğe veya işlemciye (hatta MMO'larda ayrı bir bilgisayara) koyabilirsiniz. Yine, 1 sistem 1'den fazla bileşene eriştiğinde bu avantajlar azalır, bu nedenle bileşen / sistem sorumluluklarının nereye ayrılacağına karar verilirken dikkate alınmalıdır.
Kylotan

2

Bir vektör veridir. Fonksiyonlar daha çok yardımcı program fonksiyonlarına benzer - verilerin o örneğine özgü değildirler, tüm vektörlere bağımsız olarak uygulanabilirler. Bunu düşünmenin güzel bir yolu şudur: Bu işlevler statik yöntemler olarak yeniden yazılabilir mi? Eğer öyleyse, sadece yarar.


Biliyorum, ama sorun şu ki, çağrı yöntemleri daha hızlı ve bir sistem veya bir varlığın konumunu manipüle etmesi gerekebilecek başka bir şey tarafından yerinde yapılabilir. Ben de açıkladım, ayrıca, sorunun sadece bundan çok daha fazlası olduğunu düşünüyorum.
jcora
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.