Judulnya sengaja hiperbolik dan mungkin hanya pengalaman saya dengan polanya tapi inilah alasan saya:
Cara "biasa" atau bisa langsung dari entitas pelaksana adalah dengan mengimplementasikannya sebagai objek dan mensubklasifikasikan perilaku umum. Ini mengarah ke masalah klasik "adalah EvilTreesubclass dari Treeatau Enemy?". Jika kita mengizinkan beberapa warisan, masalah intan muncul. Sebagai gantinya, kita dapat menarik fungsionalitas gabungan Treedan Enemylebih jauh ke atas hierarki yang mengarah ke kelas-kelas Tuhan, atau kita dapat dengan sengaja meninggalkan perilaku di kelas kita Treedan Entity(membuat mereka menjadi penghubung dalam kasus ekstrem) sehingga EvilTreedapat mengimplementasikannya sendiri - yang mengarah ke duplikasi kode jika kita pernah punya SomewhatEvilTree.
Sistem Entitas-Komponen mencoba untuk memecahkan masalah ini dengan membagi Treedan Enemyobjek menjadi komponen yang berbeda - katakanlah Position, Healthdan AI- dan menerapkan sistem, seperti AISystemyang mengubah posisi Entitiy sesuai dengan keputusan AI. Sejauh ini bagus tapi bagaimana jika EvilTreebisa mengambil powerup dan menangani kerusakan? Pertama kita perlu a CollisionSystemdan DamageSystem(kita mungkin sudah memiliki ini). The CollisionSystemkebutuhan untuk berkomunikasi dengan DamageSystem: Setiap kali dua hal bertabrakan dengan CollisionSystemmengirimkan pesan ke DamageSystemkesehatan sehingga dapat mengurangi. Kerusakan juga dipengaruhi oleh powerups sehingga kita perlu menyimpannya di suatu tempat. Apakah kita membuat yang baru PowerupComponentyang kita lampirkan ke entitas? Tapi kemudianDamageSystemperlu tahu tentang sesuatu yang lebih baik tidak tahu tentang - setelah semua, ada juga hal-hal yang menangani kerusakan yang tidak dapat mengambil powerups (misalnya a Spike). Apakah kami mengizinkan PowerupSystemuntuk memodifikasi StatComponentyang juga digunakan untuk perhitungan kerusakan yang mirip dengan jawaban ini ? Tetapi sekarang dua sistem mengakses data yang sama. Ketika permainan kami menjadi lebih kompleks, itu akan menjadi grafik ketergantungan yang tidak berwujud di mana komponen dibagi di antara banyak sistem. Pada titik itu kita bisa menggunakan variabel statis global dan menyingkirkan semua boilerplate.
Apakah ada cara yang efektif untuk menyelesaikan ini? Satu ide yang saya miliki adalah membiarkan komponen memiliki fungsi tertentu, misalnya memberikan StatComponent attack()yang baru saja mengembalikan integer secara default tetapi dapat dikomposisi ketika powerup terjadi:
attack = getAttack compose powerupBy(20) compose powerdownBy(40)
Ini tidak menyelesaikan masalah yang attackharus disimpan dalam komponen yang diakses oleh banyak sistem, tetapi setidaknya saya bisa mengetik fungsi dengan benar jika saya memiliki bahasa yang mendukungnya secara memadai:
// 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
Dengan cara ini saya setidaknya menjamin urutan yang benar dari berbagai fungsi yang ditambahkan oleh sistem. Either way, sepertinya saya dengan cepat mendekati pemrograman reaktif fungsional di sini jadi saya bertanya pada diri sendiri apakah saya seharusnya tidak menggunakannya sejak awal (saya baru saja melihat ke FRP, jadi saya mungkin salah di sini). Saya melihat bahwa ECS merupakan peningkatan dari hirarki kelas yang kompleks tetapi saya tidak yakin itu ideal.
Apakah ada solusi untuk ini? Apakah ada fungsi / pola yang saya lewatkan untuk memisahkan ECS dengan lebih bersih? Apakah FRP hanya lebih cocok untuk masalah ini? Apakah masalah ini hanya muncul karena kompleksitas yang melekat pada apa yang saya coba programkan; yaitu apakah FRP akan memiliki masalah yang sama?