Hal-hal yang saya lewatkan di Unity3D ECS dibandingkan dengan Entitas

Saya meluangkan waktu (beberapa jam selama beberapa hari) untuk melihat implementasi ECS yang baru dan menarik di Unity3D. Saya harus mengatakan, saya menyukai apa yang saya lihat. Saya juga sangat memahami bahwa saya mungkin melewatkan sesuatu, atau ini masih terlalu dini dan segala sesuatunya akan terjadi, tetapi melihat dari keadaan saat ini, saya memiliki dua (setengah) hal yang saya lewatkan, dibandingkan ke Entitas.

Pertama mari kita mulai dengan fitur kecil:

Komponen unik

Saat bekerja dengan ECS, kami cenderung berpikir secara kolektif, atau berkelompok, jika Anda mau.

Berikan saya semua “sesuatu” yang mempunyai kedudukan (atau berikan saja saya semua kedudukan). Beri aku semua benda yang mempunyai posisi dan kecepatan.

Namun terkadang, kita tahu bahwa paling banyak harus ada satu benda dengan beberapa komponen karakteristik. Misalnya, bayangkan kita memiliki komponen tag Selected dan kita mengetahui bahwa pemain hanya dapat memilih satu entitas. Komponen Selected inilah yang kami sebut sebagai komponen unik. Dan dengan itu, kami memastikan bahwa hanya satu entitas di dunia yang diperbolehkan memuat komponen ini. Sebagai efek sampingnya, kami mendapatkan cara sederhana untuk mengakses entitas tersebut.

Faktanya PlayerInput dalam proyek contoh penembak dua tongkat, adalah kandidat yang baik untuk menjadi komponen unik. Game ini tidak memiliki elemen multipemain lokal, jadi dapat dikatakan bahwa hanya ada satu entitas dengan komponen PlayerInput. Dalam implementasi saat ini kita dapat melihat di UpdatePlayerHud dan EnemyShootSystem bahwa keunikan tersirat, namun tidak dipaksakan dan tidak memberikan kesederhanaan dan manfaat yang bisa kita peroleh dengan mendefinisikan suatu komponen sebagai unik.

IMHO komponen unik juga harus memberikan manfaat serupa seperti ISharedComponentData dari perspektif efisiensi memori. Ini pada dasarnya adalah mitranya. Komponen bersama adalah satu instance dari suatu komponen yang dapat dikaitkan dengan beberapa entitas, dimana komponen unik hanya dapat dikaitkan dengan satu entitas.

Sekarang mari kita bicara tentang fitur kedua yang lebih kompleks:

Perilaku Reaktif

Fitur ini memiliki implikasi yang mendalam, namun motivasi awalnya cukup sederhana. Kami ingin menghindari banyak komponen tag.

Mari kita mendasarkan hal ini pada sebuah contoh. Bayangkan kita mempunyai permainan menara pertahanan dan kita perlu mengimplementasikan sistem pencarian target. Dalam implementasi yang naif, kita bisa pergi ke semua musuh dan mencari tahu apakah musuh cukup dekat. Namun kami dapat melakukan sedikit optimasi, kami dapat memperkenalkan komponen Moved, yang menunjukkan bahwa musuh baru saja bergerak, sehingga dalam sistem pencarian target, kami hanya dapat melakukan iterasi melalui musuh yang baru saja bergerak. Pengoptimalan ini mengurangi kumpulan musuh yang harus kita lalui.

Memiliki komponen tag seperti Moved bisa jadi rumit sehubungan dengan siklus hidupnya. Komponen Moved harus ditambahkan di setiap sistem yang mengubah/menambahkan Position. Jadi kita mungkin akan mengalami duplikasi kode. Komponen Moved mungkin digunakan oleh banyak sistem, ini berarti penghapusan komponen tag juga menjadi hal yang tidak sepele.

Semua kerumitan ini dapat disederhanakan dengan memperkenalkan konsep sistem reaktif.

Sistem reaktif dijalankan hanya pada entitas yang komponen tertentu ditambahkan/dihapus atau diganti. Dalam contoh kecil kita, sistem target adalah sistem reaktif, yang hanya bekerja pada entitas yang berpindah sejak terakhir kali sistem ini dijalankan. Semantik sistem tetap sama, tetapi sistem reaktif membuat komponen tag Moved menjadi mubazir dan menghilangkan beban kognitif dalam mengelola siklus hidup komponen tag ini.

Memperkenalkan konsep sistem reaktif, mengubah cara secara mendalam, kami menerapkan sistem UI dan permainan.

Namun ada sisi lain dari perilaku reaktif, yang merupakan efek samping yang menarik, berdasarkan detail implementasi sistem reaktif.

Cara kami menerapkan sistem reaktif adalah dengan membuat konteks, entitas, dan grup dapat diamati. Setiap sistem reaktif memiliki kolektor yang terkuras ketika sistem reaktif dijalankan. Detailnya dapat ditemukan di buku saya EntitasCookBook. Efek samping menarik yang saya sebutkan sebelumnya adalah:

Jika semuanya dapat diamati, kita dapat mengimplementasikan pengindeksan dan logging dengan cukup mudah.

Ide di balik pengindeksan dijelaskan dalam bab Indeks, tetapi jika Anda adalah tipe orang TL;DR, berikut adalah ringkasan singkatnya. Indeks adalah struktur data khusus yang memungkinkan kita mengkueri entitas berdasarkan nilai komponen. Jika kita memikirkan contoh menara pertahanan sebelumnya, indeks musuh bisa berupa pohon segi empat, yang diperbarui setiap kali posisi musuh diubah.

Faktanya, dalam contoh boids ada pekerjaan HashPositions, yang membuat struktur data serupa. Namun sejauh yang saya mengerti itu dibangun pada setiap frame dari awal, di mana indeks dapat diperbarui secara bertahap.

Sekarang mari kita bicara tentang logging. Logging adalah sesuatu yang sedang saya kerjakan. Dengan logging kita dapat menangkap riwayat lengkap entitas, kapan entitas tersebut dibuat/dihancurkan, komponen mana yang disimpan, dan sistem mana yang bertanggung jawab atas perubahan tersebut. Hal ini mengarah pada introspeksi lengkap ke dalam evolusi keadaan, yang dapat dikueri dengan bahasa kueri kecil. Pikirkan "DTrace" untuk ECS.

Fitur terakhir yang saya sebutkan sebagai setengah adalah pencocokan AnyOf. Saya menyebutnya setengah dari sebuah fitur, karena menurut saya ini adalah fitur yang bagus untuk dimiliki.

Di Entitas kita mendapatkan sekelompok entitas dengan mendefinisikan apa yang disebut matcher. Sekali lagi saya akan merujuk ke bab Grup di EntitasCookBook, jika Anda tertarik dengan detailnya. BTW, konsepnya didasarkan pada fitur serupa di Artemis ECS. Di Artemis disebut Aspect.

Dengan AnyOf kita dapat meminta entitas yang memiliki salah satu komponen yang terdaftar. Saat sistem meminta entitas dengan pencocokan apa pun, hal ini berarti bahwa komponen tersebut tidak diperlukan untuk transformasi data, melainkan untuk klasifikasi entitas yang tepat. Mirip dengan ide di balik SubtractiveComponent di Unity ECS.

Menurut saya ini bagus untuk dimiliki, karena berdasarkan pengalaman saya, penggunaan AnyOf matcher terbatas dan kami dapat menyiasatinya dengan memiliki beberapa sistem atau memperkenalkan komponen tag baru, yang mencerminkan klasifikasi yang ingin kami jelaskan dengan AnyOf.

Saya sepenuhnya menyadari bahwa fokus Unity3D ECS adalah kinerja dan fitur yang saya daftarkan tidak terlalu penting dari sudut pandang kinerja. Namun saya yakin fitur-fitur tersebut bisa diimplementasikan dengan mempertimbangkan performa dan fitur-fitur tersebut akan sedikit meningkatkan pengalaman developer, meskipun hanya mengurangi jumlah komponen tag.

Seperti biasa, saya senang mendiskusikan semua hal tentang ECS, jadi tulislah beberapa komentar, atau hubungi saya di twitter.