Başlık kasıtlı olarak hiperboliktir ve sadece desenle deneyimim olmayabilir, ancak işte mantığım:
Varlıkları uygulamanın "olağan" veya tartışmasız açık yolu, onları nesne olarak uygulamak ve ortak davranışı alt sınıflamaktır. Bu klasik soruna yol açar " EvilTreebir alt sınıfı Treeveya Enemy?". Birden fazla mirasa izin verirsek, elmas problemi ortaya çıkar. Bunun yerine , Tanrı sınıflarına götüren hiyerarşinin birleşik işlevselliğini Treeve Enemydaha da üstünü alabiliriz veya kasıtlı olarak bizim Treeve Entitysınıflarımızdaki davranışları (aşırı durumda arayüzler haline getirerek) dışarıda bırakabiliriz EvilTree; kod çoğaltma hiç varsa SomewhatEvilTree.
Varlık-Bileşen Sistemleri bu sorunu, Treeve Enemynesneyi farklı bileşenlere (örneğin Position, Healthve AI- bölerek) bölerek çözmeye çalışır ve örneğin, AISystembir Yetki'nin konumunu AI kararlarına göre değiştiren sistemler gibi . Şimdiye kadar çok iyi ama EvilTreebir powerup alabilir ve hasar verebilirse ne olur ? Öncelikle a CollisionSystemve a'ya ihtiyacımız var DamageSystem(muhtemelen bunlara zaten sahibiz). CollisionSystemİhtiyaçları ile iletişim kurmak için DamageSystem: iki şey çarpışır Her zaman CollisionSystembir mesaj gönderir DamageSystemo çıkarma böylece sağlık. Hasar aynı zamanda güçlendirmelerden de etkilenir, bu yüzden bunu bir yerde saklamamız gerekir. PowerupComponentVarlıklara eklediğimiz yeni bir öğe mi yaratıyoruz ? Ama sonraDamageSystemhiçbir şey bilmeyeceği bir şey bilmelidir - sonuçta, powerups'ı alamayan hasar veren şeyler de vardır (örneğin a Spike). Bu cevaba benzer hasar hesaplamaları için kullanılan PowerupSystema'nın değiştirilmesine izin veriyor muyuz ? Ancak şimdi iki sistem aynı verilere erişiyor. Oyunumuz daha karmaşık hale geldikçe, bileşenlerin birçok sistem arasında paylaşıldığı somut olmayan bir bağımlılık grafiği haline gelecektir. Bu noktada global statik değişkenleri kullanabilir ve tüm kazan plakasından kurtulabiliriz.StatComponent
Bunu çözmenin etkili bir yolu var mı? Sahip olduğum bir fikir, bileşenlerin belirli işlevlere sahip olmasına izin vermekti, örneğin StatComponent attack(), varsayılan olarak sadece bir tamsayı döndüren ver, ancak bir güçlendirme gerçekleştiğinde oluşturulabilir:
attack = getAttack compose powerupBy(20) compose powerdownBy(40)
Bu, attackbirden çok sistem tarafından erişilen bir bileşende kaydedilmesi gereken sorunu çözmez, ancak en azından onu destekleyen bir dil varsa işlevleri düzgün bir şekilde yazabilirim:
// In StatComponent
type Strength = PrePowerup | PostPowerup
type Damage = Int
type PrePowerup = Int
type PostPowerup = Int
attack: Strength = getAttack //default value, can be changed by systems
getAttack: PrePowerup
// these functions can be defined in other components or in PowerupSystems
powerupBy: Strength -> PostPowerup
powerdownBy: Strength -> PostPowerup
subtractArmor: Strength -> Damage
// in DamageSystem
dealDamage: Damage -> () = attack compose subtractArmor compose hurtSomeEntity
Bu şekilde en azından sistemler tarafından eklenen çeşitli fonksiyonların doğru sıralanmasını garanti ederim. Her iki durumda da, burada işlevsel reaktif programlamaya hızla yaklaşıyorum gibi görünüyor, bu yüzden kendime bunu başından beri kullanmamam gerektiğini soruyorum (sadece FRP'ye baktım, bu yüzden burada yanlış olabilirim). ECS'nin karmaşık sınıf hiyerarşileri üzerinde bir gelişme olduğunu görüyorum ama bunun ideal olduğuna ikna olmadım.
Bunun etrafında bir çözüm var mı? ECS'yi daha temiz bir şekilde ayırmak için kaçırdığım bir işlevsellik / desen var mı? FRP bu sorun için kesinlikle daha uygun mu? Bu sorunlar sadece programlamaya çalıştığım şeyin doğasında var olan karmaşıklıktan mı kaynaklanıyor; yani FRP'nin benzer sorunları olur mu?