pengantar
Sistem entitas-komponen adalah teknik arsitektur berorientasi objek.
Tidak ada konsensus universal tentang apa arti istilah tersebut, sama dengan pemrograman berorientasi objek. Namun, jelas bahwa sistem entitas-komponen secara khusus dimaksudkan sebagai alternatif arsitektur untuk warisan . Hirarki warisan alami untuk mengekspresikan apa objek yang , tetapi dalam jenis tertentu dari perangkat lunak (seperti game), Anda lebih suka mengungkapkan apa yang obyek tidak .
Ini adalah model objek yang berbeda dari "kelas dan warisan" yang Anda terbiasa bekerja di C ++ atau Java. Entitas sama ekspresifnya dengan kelas, seperti halnya prototipe seperti dalam JavaScript atau Self — semua sistem ini dapat diimplementasikan dalam hal satu sama lain.
Contohnya
Katakanlah Mari kita bahwa Player
adalah sebuah entitas dengan Position
, Velocity
, dan KeyboardControlled
komponen, yang melakukan hal-hal yang jelas.
entity Player:
Position
Velocity
KeyboardControlled
Kita tahu Position
harus dipengaruhi oleh Velocity
, dan Velocity
oleh KeyboardControlled
. Pertanyaannya adalah bagaimana kita ingin memodelkan efek tersebut.
Entitas, Komponen, dan Sistem
Misalkan komponen tidak memiliki referensi satu sama lain; Physics
sistem eksternal menelusuri semua Velocity
komponen dan memperbarui Position
entitas yang sesuai; suatu Input
sistem menelusuri semua KeyboardControlled
komponen dan memperbarui Velocity
.
Player
+--------------------+
| Position | \
| | Physics
/ | Velocity | /
Input | |
\ | KeyboardControlled |
+--------------------+
Ini memenuhi kriteria:
Sistem sekarang bertanggung jawab untuk menangani peristiwa dan memberlakukan perilaku yang dijelaskan oleh komponen. Mereka juga bertanggung jawab untuk menangani interaksi antar entitas, seperti tabrakan.
Entitas dan Komponen
Namun, anggaplah bahwa komponen lakukan memiliki referensi satu sama lain. Sekarang entitas hanyalah sebuah konstruktor yang menciptakan beberapa komponen, mengikat mereka bersama, dan mengelola masa hidup mereka:
class Player:
construct():
this.p = Position()
this.v = Velocity(this.p)
this.c = KeyboardControlled(this.v)
Entitas sekarang dapat mengirimkan input dan memperbarui acara langsung ke komponennya. Velocity
akan menanggapi pembaruan, dan KeyboardControlled
akan menanggapi input. Ini masih memenuhi kriteria kami:
Di sini, interaksi komponen bersifat eksplisit, tidak dipaksakan dari luar oleh suatu sistem. Data yang menggambarkan perilaku (berapakah jumlah kecepatan?) Dan kode yang memberlakukannya (apa itu kecepatan?) Digabungkan, tetapi dengan cara alami. Data dapat dilihat sebagai parameter untuk perilaku. Dan beberapa komponen tidak bertindak sama sekali — a Position
adalah perilaku berada di suatu tempat .
Interaksi dapat ditangani pada tingkat entitas ("ketika Player
bertabrakan dengan Enemy
...") atau pada tingkat komponen individu ("ketika entitas dengan Life
bertabrakan dengan entitas dengan Strength
...").
Komponen
Apa alasan keberadaan entitas? Jika itu hanya sebuah konstruktor, maka kita dapat menggantinya dengan fungsi mengembalikan seperangkat komponen. Jika nanti kami ingin meminta entitas berdasarkan tipenya, kami juga dapat memiliki Tag
komponen yang memungkinkan kami melakukan hal itu:
function Player():
t = Tag("Player")
p = Position()
v = Velocity(p)
c = KeyboardControlled(v)
return {t, p, v, c}
Interaksi harus sekarang ditangani oleh query abstrak, benar-benar decoupling acara dari jenis entitas. Tidak ada lagi tipe entitas untuk di-query — Tag
data arbitrer mungkin lebih baik digunakan untuk debugging daripada logika game.
Kesimpulan
Entitas bukan fungsi, aturan, aktor, atau kombinator aliran data. Itu adalah kata benda yang mencontohkan fenomena konkret — dengan kata lain, itu adalah objek. Seperti yang dikatakan Wikipedia — sistem entitas-komponen adalah pola arsitektur perangkat lunak untuk memodelkan objek umum.
TIDAK. Dan saya terkejut berapa banyak orang yang memilih sebaliknya!
Paradigma
Ini Berorientasi Data alias Data-Driven karena kita berbicara tentang arsitektur dan bukan bahasa yang ditulisnya. Arsitektur adalah realisasi dari gaya pemrograman atau paradigma , yang biasanya dapat secara tidak sengaja bekerja di sekitar dalam bahasa yang diberikan.
Fungsional?
Perbandingan Anda dengan pemrograman Fungsional / Prosedural adalah perbandingan yang relevan dan bermakna. Perhatikan, bagaimanapun, bahwa bahasa "Fungsional" berbeda dari paradigma "Prosedural" . Dan Anda dapat menerapkan ECS dalam bahasa Fungsional seperti Haskell , yang dilakukan orang.
Di mana kohesi terjadi
Pengamatan Anda relevan dan tepat sasaran :
ECS / ES bukan EC / CE
Ada perbedaan antara arsitektur berbasis komponen, "Entity-Component" dan "Entity-Component-System". Karena ini adalah pola desain yang berkembang, saya telah melihat definisi ini digunakan secara bergantian. Arsitektur "EC" atau "CE" atau "Entity-Component" menempatkan perilaku dalam komponen , sedangkan arsitektur "ES" atau "ECS" menempatkan perilaku dalam sistem . Berikut adalah beberapa artikel ECS, yang keduanya menggunakan nomenklatur menyesatkan, tetapi dapatkan gagasan umum:
Jika Anda mencoba memahami istilah ini di 2015, pastikan referensi seseorang untuk "Sistem Komponen Entitas" tidak berarti "arsitektur Komponen Entitas".
sumber
Entity komponen systems (ECSs) dapat diprogram dalam OOP atau secara fungsional tergantung pada bagaimana sistem didefinisikan.
Cara OOP:
Saya telah bekerja pada game di mana entitas adalah objek yang terdiri dari berbagai komponen. Entitas memiliki fungsi pembaruan yang memodifikasi objek pada tempatnya dengan memanggil pembaruan pada semua komponennya secara bergantian. Ini jelas OOP dalam gaya - perilaku terkait dengan data, dan data dapat berubah. Entitas adalah objek dengan konstruktor / destruktor / pembaruan.
Cara yang lebih fungsional:
Alternatifnya adalah agar entitas menjadi data tanpa metode apa pun. Entitas ini dapat ada dalam dirinya sendiri atau hanya menjadi id yang terkait dengan berbagai komponen. Dengan cara ini dimungkinkan (tetapi tidak dilakukan secara umum) untuk berfungsi penuh dan memiliki entitas yang tidak berubah dan sistem murni yang menghasilkan status komponen baru.
Tampaknya (dari pengalaman pribadi) bahwa cara terakhir mendapatkan lebih banyak daya tarik dan untuk alasan yang baik. Memisahkan data entitas dari perilaku menghasilkan kode yang lebih fleksibel dan dapat digunakan kembali (imo). Secara khusus, menggunakan sistem untuk memperbarui komponen / entitas dalam batch dapat lebih berkinerja, dan sepenuhnya menghindari kompleksitas pesan antar-entitas yang mengganggu banyak OOP ECS.
TLDR: Anda bisa melakukannya dengan cara lain, tetapi saya berpendapat bahwa manfaat dari sistem komponen entitas yang baik berasal dari sifatnya yang lebih fungsional.
sumber
Sistem Komponen Entitas Berorientasi Data dapat hidup berdampingan dengan Paradigma Berorientasi Objek: - Sistem Komponen cocok untuk polimorfisme. - Komponen dapat berupa POD (data lama biasa) dan juga JUGA Objek (dengan Kelas dan Metode), dan semuanya masih 'berorientasi data', dengan ketentuan bahwa Metode Kelas Komponen hanya memanipulasi data yang dimiliki oleh objek lokal.
Jika Anda memilih jalur ini, saya sarankan Anda menghindari menggunakan Metode Virtual, karena jika Anda memilikinya, komponen Anda tidak lagi murni komponen data, ditambah metode-metode itu lebih mahal untuk memanggil - ini bukan COM. Jaga kelas komponen Anda bersih dari referensi apa pun untuk apa pun eksternal, sebagai suatu peraturan.
Contohnya adalah vec2 atau vec3, sebuah wadah data dengan beberapa metode untuk menyentuh data itu, dan tidak lebih.
sumber
Saya menganggap ECS secara fundamental berbeda dari OOP dan cenderung melihatnya dengan cara yang sama dengan Anda, lebih dekat dengan fungsional atau terutama prosedural di alam dengan pemisahan data yang sangat berbeda dari fungsionalitas. Ada juga beberapa kemiripan dengan pemrograman semacam berurusan dengan database pusat. Tentu saja saya orang terburuk dalam hal definisi formal. Saya hanya peduli dengan bagaimana hal-hal cenderung terjadi, bukan apa yang secara konseptual didefinisikan.
Saya mengasumsikan semacam ECS di mana komponen mengumpulkan bidang data dan membuatnya dapat diakses secara publik / global, entitas mengumpulkan komponen, dan sistem menyediakan fungsionalitas / perilaku pada data itu. Itu mengarah ke karakteristik arsitektur yang sangat sulit dari apa yang biasanya kita sebut basis kode berorientasi objek.
Dan tentu saja ada beberapa batas yang kabur dalam cara orang merancang / mengimplementasikan ECS, dan ada perdebatan tentang apa yang sebenarnya merupakan ECS di tempat pertama. Namun batas-batas tersebut juga kabur dalam kode yang ditulis dalam apa yang kita sebut bahasa fungsional atau prosedural. Di antara semua ketidakjelasan ini, konstanta fundamental ECS dengan pemisahan data dari fungsionalitas tampaknya jauh lebih dekat dengan pemrograman fungsional atau prosedural bagi saya daripada OOP.
Salah satu alasan utama yang saya pikir tidak membantu untuk mempertimbangkan ECS untuk menjadi bagian dari kelas OOP adalah bahwa sebagian besar praktik SE terkait dengan OOP berputar di sekitar stabilitas antarmuka publik, dengan fungsi pemodelan antarmuka publik , bukan data. Ide dasarnya adalah bahwa sebagian besar ketergantungan publik mengalir ke fungsi abstrak, bukan ke data konkret. Dan karena itu, OOP cenderung membuatnya sangat mahal untuk mengubah perilaku desain mendasar, sementara membuatnya sangat murah untuk mengubah detail konkret (seperti data dan kode yang diperlukan untuk mengimplementasikan fungsionalitas).
ECS sangat berbeda dalam hal ini mengingat bagaimana hal-hal digabungkan sebagai sebagian besar ketergantungan publik mengalir ke data konkret: dari sistem ke komponen. Akibatnya, setiap praktik SE yang terkait dengan ECS akan berputar di sekitar stabilitas data , karena antarmuka (komponen) yang paling umum dan banyak digunakan sebenarnya hanya data.
Akibatnya, ECS membuatnya sangat mudah untuk melakukan hal-hal seperti mengganti mesin rendering OpenGL untuk yang DirectX, bahkan jika keduanya diimplementasikan dengan fungsi yang sangat berbeda dan tidak berbagi desain yang sama sekali pun, asalkan mesin DX dan GL memiliki akses ke data stabil yang sama. Sementara itu akan sangat mahal dan memerlukan penulisan ulang banyak sistem untuk mengubah, katakanlah, representasi data dari a
MotionComponent
.Itu sangat berlawanan dengan apa yang secara tradisional kami kaitkan dengan OOP, setidaknya dalam hal karakteristik penggabungan dan apa yang merupakan "antarmuka publik" vs. "detail implementasi pribadi". Tentu saja dalam kedua kasus "detail implementasi" mudah diubah, tetapi dalam ECS desain data yang mahal untuk diubah (data bukan detail implementasi dalam ECS), dan dalam OOP desain desain fungsionalitas yang mahal untuk diubah (desain fungsi bukan detail implementasi dalam OOP). Jadi itu ide yang sangat berbeda dari "detail implementasi", dan salah satu daya tarik utama bagi saya untuk ECS dari perspektif pemeliharaan adalah bahwa dalam domain saya, data yang diperlukan untuk melakukan banyak hal lebih mudah untuk distabilkan dan dirancang dengan benar sekali dan untuk semua dimuka daripada semua hal yang bisa kita lakukan dengan data itu (yang akan berubah sepanjang waktu ketika klien berubah pikiran dan saran pengguna baru datang membanjir). Akibatnya, saya menemukan biaya pemeliharaan menurun ketika kami mulai mengarahkan ketergantungan dari fungsi abstrak ke data mentah, pusat (tetapi masih dengan hati-hati tentang sistem yang mengakses komponen mana yang memungkinkan mempertahankan invarian hingga tingkat yang waras meskipun semua data secara konseptual sedang dapat diakses secara global).
Dan dalam kasus saya setidaknya, ECS SDK dengan API dan semua komponen sebenarnya diimplementasikan dalam C dan tidak memiliki kemiripan dengan OOP. Saya telah menemukan C lebih dari cukup untuk tujuan seperti itu mengingat kurangnya OO dalam arsitektur ECS dan keinginan untuk memiliki arsitektur plugin yang dapat digunakan oleh berbagai bahasa dan kompiler terluas. Sistem masih diimplementasikan dalam C + + karena C + + membuat hal-hal yang sangat nyaman di sana dan sistem memodelkan sebagian besar kompleksitas dan di sana saya menemukan penggunaan untuk banyak hal yang mungkin dianggap lebih dekat dengan OOP, tapi itu untuk detail implementasi. Desain arsitekturnya sendiri masih sangat mirip dengan C.
Jadi saya pikir agak membingungkan, paling tidak, untuk mencoba mengatakan ECS adalah OO menurut definisi. Paling tidak fundamental melakukan hal-hal yang merupakan pergantian 180 derajat lengkap dari banyak prinsip dasar yang umumnya terkait dengan OOP, dimulai dengan enkapsulasi dan mungkin berakhir dengan apa yang dianggap sebagai karakteristik penggabungan yang diinginkan.
sumber