OOP ECS ve Saf ECS


11

İlk olarak, bu sorunun oyun geliştirme konusuyla bağlantılı olduğunun farkındayım ama gerçekten daha genel bir yazılım oluşturma sorununa geldiğinden burada sormaya karar verdim.

Geçtiğimiz ay boyunca Entity-Component-Systems hakkında çok şey okudum ve şimdi konseptten oldukça rahatım. Bununla birlikte, net bir 'tanımı' eksik gibi görünen bir yönü var ve farklı makaleler radikal olarak farklı çözümler önerdi:

Bu, bir ECS'nin kapsüllemeyi kırıp kırmayacağı sorusudur. Başka bir deyişle, OOP tarzı ECS (bileşenler, kendilerine özgü verileri kapsayan hem durum hem de davranışa sahip nesnelerdir) ve saf ECS'ye (bileşenler, yalnızca genel verilere sahip olan ve işlevsellik sağlayan sistemler c tarzı yapılardır).

Bir Framework / API / Engine geliştirdiğimi unutmayın. Dolayısıyla amaç, onu kullanan kişi tarafından kolayca genişletilebilmesidir. Bu, yeni bir oluşturma veya çarpışma bileşeni türü eklemek gibi şeyler içerir.

OOP yaklaşımıyla ilgili sorunlar

  • Bileşenler, diğer bileşenlerin verilerine erişmelidir. Örneğin, oluşturma bileşeninin draw yöntemi, dönüştürme bileşeninin konumuna erişmelidir. Bu kod bağımlılıkları oluşturur.

  • Bileşenler polimorfik olabilir ve bu da biraz karmaşıklık getirir. Örneğin, oluşturma bileşeninin sanal çizim yöntemini geçersiz kılan bir hareketli grafik oluşturma bileşeni olabilir.

Saf yaklaşımla ilgili sorunlar

  • Polimorfik davranış (örneğin renderleme için) bir yere uygulanması gerektiğinden, sadece sistemlere dışarıdan tedarik edilir. (örn. hareketli grafik oluşturma sistemi, oluşturma düğümünü devralan ve oluşturma motoruna ekleyen hareketli grafik oluşturma düğümü oluşturur)

  • Sistemler arasındaki iletişimi engellemek zor olabilir. Örneğin, çarpışma sistemi, hangi beton render bileşeninden hesaplanmış sınırlayıcı kutuya ihtiyaç duyabilir. Bu, veri yoluyla iletişim kurmalarına izin vererek çözülebilir. Ancak, oluşturma sistemi sınırlama kutusu bileşenini güncelleyeceği ve çarpışma sistemi bunu kullanacağı için bu, anlık güncellemeleri kaldırır. Sistemin güncelleme işlevlerinin çağrılma sırası tanımlanmamışsa bu durum preformasyonlara neden olabilir. Sistemlerin, diğer sistemlerin işleyicilerine abone olabileceği olayları yükseltmelerine olanak tanıyan bir olay sistemi vardır. Ancak bu sadece sistemlere ne yapılacağını söylemek için kullanılır; yani geçersiz işlevler.

  • Ek bayraklar gerekli. Örneğin bir döşeme haritası bileşenini ele alalım. Bir boyut, döşeme boyutu ve dizin listesi alanına sahip olacaktır. Döşeme eşleme sistemi ilgili tepe dizisini işleyecek ve bileşenin verilerine göre doku koordinatlarını atayacaktır. Bununla birlikte, her çerçevenin tüm yeniden hesaplanması her karenin pahalıdır. Bu nedenle, daha sonra bunları sistemde güncellemek için yapılan tüm değişiklikleri takip etmek için bir listeye ihtiyaç duyulacaktır. OOP yolunda bu, döşeme haritası bileşeni tarafından kapsüllenebilir. Örneğin, SetTile () yöntemi her çağrıldığında köşe dizisini güncelleyecektir.

Her ne kadar saf yaklaşımın güzelliğini görsem de, daha geleneksel bir OOP üzerinde ne tür bir somut fayda sağlayacağını gerçekten anlamıyorum. Bileşenler arasındaki bağımlılıklar, sistemlerde gizlenmesine rağmen hala mevcuttur. Ayrıca aynı hedefe ulaşmak için çok daha fazla sınıfa ihtiyacım olacaktı. Bu bana biraz iyi tasarlanmış bir çözüm gibi görünüyor ki bu asla iyi bir şey değil.

Dahası, performansa o kadar da bağlı değilim, bu yüzden veri odaklı tasarım ve cashe özledikleri hakkındaki bu fikir benim için gerçekten önemli değil. Sadece güzel bir mimari istiyorum ^^

Yine de, okuduğum makalelerin ve tartışmaların çoğu ikinci yaklaşımı önermektedir. NEDEN?

Animasyon

Son olarak, saf bir ECS'de animasyonu nasıl ele alacağım sorusunu sormak istiyorum. Şu anda bir animasyonu, 0 ile 1 arasındaki bazı ilerlemelere dayanarak bir varlığı manipüle eden bir functor olarak tanımladım. Animasyon bileşeni, animasyonlar listesine sahip animatörlerin bir listesine sahiptir. Güncelleme fonksiyonunda, o anda aktif olan animasyonları varlık için uygular.

Not:

Ben sadece bu yazı okudum Varlık Bileşen Sistemi mimari nesne tanımı ile odaklı mı? bu da sorunu benden daha iyi açıklıyor. Temel olarak aynı konuda olmasına rağmen , saf veri yaklaşımının neden daha iyi olduğu konusunda hala cevap vermiyor .


1
Belki de basit ama ciddi bir soru: ECS'lerin avantajlarını / dezavantajlarını biliyor musunuz? Bu çoğunlukla 'neden'i açıklar.
Caramiriel

Eh, çoklu miras ect aracılığıyla ölüm elmas önlemek için miras yerine bileşenleri yani kompozisyon kullanmanın avantajını anlıyorum. Bileşenlerin kullanılması, çalışma zamanında davranışın değiştirilmesine de izin verir. Ve modülerdir. Anlamadığım şey, neden veri ve fonksiyonların bölünmesinin istendiği. Şu anki uygulamam github üzerinde github.com/AdrianKoch3010/MarsBaseProject
Adrian Koch

ECS'lerle tam bir cevap eklemek için yeterli tecrübem yok. Ancak kompozisyon sadece DoD'dan kaçınmak için kullanılmaz; ayrıca çalışma zamanında OO yaklaşımı kullanılarak oluşturulması zor (er) olan varlıklar da oluşturabilirsiniz. Bununla birlikte, verileri / prosedürleri bölmek verilerin akıl yürütmesini kolaylaştırır. Seri hale getirme, durum kaydetme, geri alma / yineleme ve bunun gibi şeyleri kolay bir şekilde uygulayabilirsiniz. Verilerle ilgili akıl yürütmesi kolay olduğundan, verileri de optimize etmek daha kolaydır. Büyük olasılıkla varlıkları toplu olarak (çok iş parçacıklı) bölebilir veya tam potansiyeline ulaşmak için başka bir donanıma bile yükleyebilirsiniz.
Caramiriel

"Oluşturma bileşeninin sanal çizim yöntemini geçersiz kılan bir hareketli grafik oluşturma bileşeni olabilir." Bunu yaparsanız / talep ederseniz bunun artık ECS olmadığını savunuyorum .
wondra

Yanıtlar:


10

Bu zor bir soru. Sadece belirli deneyimlerime (YMMV) dayalı bazı soruları çözmeye çalışacağım:

Bileşenler, diğer bileşenlerin verilerine erişmelidir. Örneğin, oluşturma bileşeninin draw yöntemi, dönüştürme bileşeninin konumuna erişmelidir. Bu kod bağımlılıkları oluşturur.

Buradaki bağlantının / bağımlılıkların miktarını ve karmaşıklığını (derecesini değil) küçümsemeyin. Bunun arasındaki farka bakabilirsiniz (ve bu şema oyuncak benzeri seviyelere çok gülünç bir şekilde basitleştirilmiştir ve gerçek dünya örneğinde kuplajı gevşetmek için ara yüzler olacaktır):

resim açıklamasını buraya girin

... ve bu:

resim açıklamasını buraya girin

... veya bu:

resim açıklamasını buraya girin

Bileşenler polimorfik olabilir ve bu da biraz karmaşıklık getirir. Örneğin, oluşturma bileşeninin sanal çizim yöntemini geçersiz kılan bir hareketli grafik oluşturma bileşeni olabilir.

Yani? Bir vtable ve sanal dağıtımın analojik (veya gerçek) eşdeğeri, temel durumunu / verilerini gizleyen nesne yerine sistem üzerinden çağrılabilir. Polimorfizm, analog vtable veya fonksiyon işaretçisi (leri) sistemin çağırması için "veri" türüne dönüştüğünde "saf" ECS uygulaması ile hala çok pratik ve uygulanabilir.

Polimorfik davranış (örneğin renderleme için) bir yere uygulanması gerektiğinden, sadece sistemlere dışarıdan tedarik edilir. (örn. hareketli grafik oluşturma sistemi, oluşturma düğümünü devralan ve oluşturma motoruna ekleyen hareketli grafik oluşturma düğümü oluşturur)

Yani? Umarım bu alaycılık olarak ortaya çıkmaz (sık sık suçlandığım halde niyetim değil ama duyguları metin yoluyla daha iyi iletebilmeyi diliyorum), ancak bu durumda "dış kaynaklı" polimorfik davranış ek bir şekilde ortaya çıkmaz verimlilik maliyeti.

Sistemler arasındaki iletişimi önlemek zor olabilir. Örneğin, çarpışma sistemi, hangi beton render bileşeninden hesaplanmış sınırlayıcı kutuya ihtiyaç duyabilir.

Bu örnek benim için özellikle garip görünüyor. Neden bir oluşturucunun verileri tekrar sahneye çıkardığını bilmiyorum (genellikle bu bağlamda oluşturucuları salt okunur olarak görüyorum) veya bir oluşturucunun bunu hem renderer hem de yapmak için başka bir sistem yerine AABB'leri bulması için çarpışma / fizik (burada "oluşturma bileşeni" adına asılıyor olabilirim). Yine de, bu örnek üzerinde fazlaca asılmak istemiyorum çünkü anlamaya çalıştığınız nokta bu değil. Yine de sistemler arasındaki iletişimin (doğrudan başkaları tarafından yapılan dönüşümlere bağlı sistemlerle merkezi ECS veri tabanına dolaylı okuma / yazma biçiminde bile), gerektiğinde sık sık olması gerekmez. 'O

Sistemin güncelleme işlevlerinin çağrılma sırası tanımlanmamışsa bu durum preformasyonlara neden olabilir.

Bu kesinlikle tanımlanmalıdır. ECS, kod tabanındaki olası her sistemin sistem işleme değerlendirme sırasını yeniden düzenlemek ve çerçeveler ve FPS ile ilgilenen son kullanıcıya tam olarak aynı tür sonuçları almak için son çözüm değildir. Bu, bir ECS tasarlarken, en azından biraz açık bir şekilde öngörülmesini önerdiğim şeylerden biridir (ancak daha sonra aklını değiştirmek için çok fazla bağışlayıcı solunum odası ile, siparişin en kritik yönlerini değiştirmemesi şartıyla) sistem çağırma / değerlendirme).

Bununla birlikte, her çerçevenin tüm yeniden hesaplanması her karenin pahalıdır. Bu nedenle, daha sonra bunları sistemde güncellemek için yapılan tüm değişiklikleri takip etmek için bir listeye ihtiyaç duyulacaktır. OOP yolunda bu, döşeme haritası bileşeni tarafından kapsüllenebilir. Örneğin, SetTile () yöntemi her çağrıldığında köşe dizisini güncelleyecektir.

Veri odaklı bir endişe dışında bunu tam olarak anlamadım. Ve bu tür performans tuzaklarından kaçınmak için, bir ECS'de verileri temsil etme ve kaydetme konusunda herhangi bir tuzak yoktur (ECS'li en büyük olanlar, belirli bileşen türlerinin mevcut örnekleri için sorgulama yapan sistemler gibi genelleştirilmiş bir ECS'yi optimize etmenin en zorlu yönleri). Mantık ve verilerin "saf" bir ECS'de ayrılması, bir OOP temsilinde önbelleğe alabileceğiniz / not edebileceğiniz şeyleri aniden yeniden hesaplamanız gerektiği anlamına gelmez. Ben çok önemli bir şey üzerinde parlama sürece bu bir tartışma / ilgisiz nokta.

"Saf" ECS ile bu verileri döşeme haritası bileşeninde saklayabilirsiniz. Tek büyük fark, bu köşe dizisini güncelleme mantığının bir yerde bir sisteme taşınmasıdır.

Gibi ayrı bir bileşen oluşturursanız, bu önbelleğin geçersiz kılınmasını ve kaldırılmasını basitleştirmek için ECS'ye bile yaslanabilirsiniz TileMapCache. Bu noktada önbellek istendiğinde ancak bir TileMapbileşeni olan bir varlıkta kullanılamıyorsa , onu hesaplayabilir ve ekleyebilirsiniz. Geçersiz kılındığında veya artık gerekli olmadığında, bu tür geçersiz kılma ve kaldırma işlemleri için özel olarak daha fazla kod yazmak zorunda kalmadan ECS üzerinden kaldırabilirsiniz.

Bileşenler arasındaki bağımlılıklar, sistemlerde gizlenmesine rağmen hala var

"Saf" bir temsilcideki bileşenler arasında hiçbir bağımlılık yoktur (Bağımlılıkların burada sistemler tarafından gizlendiğini söylemenin oldukça doğru olduğunu düşünmüyorum). Veriler, verilere bağlı değildir. Mantık mantığa bağlıdır. Ve "saf" bir ECS, bir sistemin çalışması için gereken minimum veri ve mantığın (genellikle hiçbiri) mutlak minimal alt kümesine bağlı olacak şekilde mantığın yazılmasını teşvik etme eğilimindedir, bu da genellikle gerçek görev için gerekenden çok daha fazla işlevsellik. Saf ECS'yi doğru kullanıyorsanız, takdir etmeniz gereken ilk şeylerden biri, kapsülleme ve özellikle bilgi gizleme hakkında OOP'de takdir etmeyi öğrendiğiniz her şeyi aynı anda sorgularken ayrılma avantajlarıdır.

Ayrıştırarak özellikle sistemlerinizin ne kadar az bilgiye ihtiyaç duyduğunu kastediyorum. Hareket sisteminizin Particleveya gibi çok daha karmaşık bir şey hakkında bilgi sahibi olması bile gerekmez Character(sistemin geliştiricisi sistemde bile var olan bu tür fikirleri bilmek zorunda değildir). Sadece bir yapıda birkaç yüzer gibi basit bir pozisyon bileşeni gibi çıplak minimum verileri bilmek gerekir. Saf bir arayüzün IMotiononunla birlikte taşıma eğiliminden daha az bilgi ve daha az dış bağımlılık . Öncelikle, her bir sistemin ECS'yi her yerde kademeli arayüz kırılmalarına maruz kalmadan arka plandaki çok beklenmedik tasarım değişikliklerini işlemek için çok affedici hale getirmesi gereken bu minimal bilgi nedeniyle.

Önerdiğiniz "saf olmayan" yaklaşım, şu anda mantığınız kesinlikle değişikliklerin kademeli kırılmalara neden olmadığı sistemlerde yerelleştirilmediğinden, bu faydayı bir miktar azalttığını öneriyoruz. Mantık şimdi, onu kullanabilen tüm çeşitli sistemlerin arayüz gereksinimlerini karşılamak zorunda olan çoklu sistemlerin eriştiği bileşenlerde bir dereceye kadar merkezileştirilecek ve şimdi her sistemin daha fazla bilgiye sahip olması (buna bağlı) olması gerektiği gibi bu bileşenle çalışılması gerekenden daha fazla bilgi.

Verilere Bağımlılıklar

ECS hakkında tartışmalı olan şeylerden biri, soyut arayüzlere olan bağımlılıkları olabilecekleri sadece ham verilerle değiştirme eğiliminde olmasıdır ve bu genellikle daha az arzu edilen ve daha sıkı bir bağlantı şekli olarak kabul edilir. Ancak, ECS'nin çok faydalı olabileceği oyunlar gibi alan adlarında, veri sunumunu önceden tasarlamak ve sabit tutmak, sistemin bazı merkezi seviyelerinde bu verilerle yapabileceklerinizi tasarlamaktan daha kolaydır. Bu, kod tabanlarındaki tecrübeli gaziler arasında bile, benzer şeylerle daha fazla COM tarzı saf arayüz yaklaşımı kullanan acı verici bir şekilde gözlemlediğim bir şey IMotion.

Geliştiriciler, bu merkezi arayüze işlev eklemek, kaldırmak veya değiştirmek için nedenler bulmaya devam ettiler ve her değişiklik korkunç ve maliyetliydi çünkü IMotionkullanılan sistemdeki her yerle birlikte uygulanan her bir sınıfı kırma eğilimi gösterecekti IMotion. Bu arada çok acı verici ve basamaklı değişikliklerle tüm zaman boyunca, uygulanan nesnelerin IMotionhepsi sadece 4x4 şamandıra matrisini depolamaktaydı ve tüm arayüz sadece bu şamandıraların nasıl dönüştürüleceği ve erişileceği ile ilgiliydi; veri gösterimi baştan beri istikrarlıydı ve beklenmedik tasarım ihtiyaçları ile değişmeye eğilimli olan bu merkezi arayüz ilk etapta bile olmasaydı çok fazla acıdan kaçınılabilirdi.

Bu, küresel değişkenler gibi neredeyse iğrenç gelebilir, ancak ECS'nin bu verileri sistemler yoluyla tür tarafından açıkça alınan bileşenlere nasıl organize ettiği, bunu derlerken, derleyiciler bilgi gizleme, erişen ve mutasyona uğrayan yerler gibi bir şeyi zorlayamaz. veriler genellikle değişmezleri etkili bir şekilde sürdürmek ve bir sistemden diğerine ne tür dönüşümlerin ve yan etkilerin devam ettiğini tahmin etmek için yeterince açık ve açıktır (aslında, belirli alanlarda OOP'den tartışmasız daha basit ve daha öngörülebilir olabilecek şekillerde) sistem düz bir boru hattına dönüşür).

resim açıklamasını buraya girin

Son olarak, saf bir ECS'de animasyonu nasıl ele alacağım sorusunu sormak istiyorum. Şu anda bir animasyonu, 0 ile 1 arasındaki bazı ilerlemelere dayanarak bir varlığı manipüle eden bir functor olarak tanımladım. Animasyon bileşeni, animasyonlar listesine sahip animatörlerin bir listesine sahiptir. Güncelleme fonksiyonunda, o anda aktif olan animasyonları varlık için uygular.

Hepimiz burada pragmatistiz. Gamedev'de bile muhtemelen çelişkili fikirler / cevaplar alırsınız. En saf ECS bile, insanların kedilerin nasıl ciltleneceği konusunda en güçlü fikirleri formüle etmediği nispeten yeni bir fenomen, öncü bir bölgedir. Bağırsak reaksiyonum, oluşturma sisteminin görüntülenmesi için animasyonlu bileşenlerde bu tür animasyon ilerlemesini arttıran bir animasyon sistemidir, ancak bu, belirli uygulama ve bağlam için çok fazla nüansı göz ardı eder.

ECS ile gümüş bir mermi değil ve kendimi hala içeri girip yeni sistemler ekleme, bazılarını kaldırma, yeni bileşenler ekleme, mevcut yeni bir sistem türünü almak için mevcut bir sistemi değiştirme vb.Gibi eğilimlerle buluyorum. ilk seferinde her şey yolunda. Ancak benim durumumdaki fark, belirli tasarım ihtiyaçlarını önceden tahmin edemediğimde merkezi bir şeyi değiştirmemem. Her yere gitmemi ve ekilen bazı yeni ihtiyaçları karşılamak için çok fazla kod değiştirmemi gerektiren basamaklı kopmaların dalgalanma etkisini almıyorum ve bu oldukça zaman tasarrufu. Ayrıca beynimde daha kolay buluyorum çünkü belirli bir sistemle oturduğumda, üzerinde çalışmak için ilgili bileşenlerin (sadece veri olan) dışında başka bir şey hakkında çok fazla şey bilmem / hatırlamam gerekmiyor.

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.