Saat ini saya sedang mencoba untuk mengimplementasikan sistem entitas berbasis komponen, di mana suatu entitas pada dasarnya hanya ID dan beberapa metode pembantu yang mengikat sekelompok komponen bersama-sama untuk membentuk objek permainan. Beberapa tujuan itu antara lain:
- Komponen hanya berisi keadaan (mis. Posisi, kesehatan, jumlah amunisi) => logika masuk ke "sistem", yang memproses komponen-komponen ini dan keadaannya (mis. PhysicsSystem, RenderSystem, dll.)
- Saya ingin mengimplementasikan komponen dan sistem baik dalam C # murni dan melalui scripting (Lua). Pada dasarnya saya ingin dapat mendefinisikan komponen dan sistem yang sepenuhnya baru secara langsung di Lua tanpa harus mengkompilasi ulang sumber C # saya.
Sekarang saya sedang mencari ide tentang bagaimana menangani hal ini secara efisien dan konsisten, jadi saya tidak perlu menggunakan sintaks yang berbeda untuk mengakses komponen C # atau komponen Lua, misalnya.
Pendekatan saya saat ini adalah mengimplementasikan komponen C # menggunakan properti publik biasa, mungkin dihiasi dengan beberapa atribut yang memberi tahu editor tentang nilai dan hal-hal standar. Lalu saya akan memiliki kelas C # "ScriptComponent", yang hanya membungkus tabel Lua secara internal dengan tabel ini dibuat oleh sebuah skrip dan menahan semua status jenis komponen tertentu. Saya tidak benar-benar ingin mengakses keadaan itu banyak dari sisi C #, karena saya tidak akan tahu pada waktu kompilasi, apa ScriptComponents dengan properti apa yang akan tersedia untuk saya. Namun, editor perlu mengaksesnya, tetapi antarmuka sederhana seperti berikut ini sudah cukup:
public ScriptComponent : IComponent
{
public T GetProperty<T>(string propertyName) {..}
public void SetProperty<T>(string propertyName, T value) {..}
}
Ini hanya akan mengakses tabel Lua dan mengatur dan mengambil properti-properti itu dari Lua dan itu dapat dengan mudah dimasukkan dalam komponen C # murni juga (tetapi gunakan properti C # biasa kemudian, melalui refleksi atau sesuatu). Ini hanya akan digunakan dalam editor, bukan oleh kode game biasa, jadi kinerja tidak sepenting di sini. Ini akan membuatnya perlu untuk menghasilkan atau menulis beberapa deskripsi komponen yang mendokumentasikan properti apa yang sebenarnya ditawarkan oleh tipe komponen tertentu, tetapi itu tidak akan menjadi masalah besar dan bisa cukup otomatis.
Namun, bagaimana cara mengakses komponen dari sisi Lua? Jika saya melakukan panggilan ke sesuatu seperti getComponentsFromEntity(ENTITY_ID)
saya mungkin hanya akan mendapatkan banyak komponen C # asli, termasuk "ScriptComponent", sebagai data pengguna. Mengakses nilai dari tabel Lua yang dibungkus akan menghasilkan saya memanggil GetProperty<T>(..)
metode daripada mengakses properti secara langsung, seperti dengan komponen C # lainnya.
Mungkin menulis getComponentsFromEntity()
metode khusus hanya untuk dipanggil dari Lua, yang mengembalikan semua komponen C # asli sebagai userdata, kecuali untuk "ScriptComponent", di mana ia akan mengembalikan tabel yang dibungkus. Tetapi akan ada metode lain yang berhubungan dengan komponen dan saya tidak benar-benar ingin menduplikasi semua metode ini baik untuk dipanggil dari kode C # atau dari skrip Lua.
Tujuan utamanya adalah menangani semua jenis komponen dengan cara yang sama, tanpa sintaksis kasus khusus yang membedakan antara komponen asli dan komponen Lua - terutama dari sisi Lua. Misalnya saya ingin dapat menulis skrip Lua seperti itu:
entity = getEntity(1);
nativeComponent = getComponent(entity, "SomeNativeComponent")
scriptComponent = getComponent(entity, "SomeScriptComponent")
nativeComponent.NativeProperty = 5
scriptComponent.ScriptedProperty = 3
Script seharusnya tidak peduli komponen apa yang sebenarnya didapat dan saya ingin menggunakan metode yang sama yang saya gunakan dari sisi C # untuk mengambil, menambah atau menghapus komponen.
Mungkin ada beberapa contoh implementasi mengintegrasikan scripting dengan sistem entitas seperti itu?
Jawaban:
Sebuah akibat wajar dari jawaban FxIII adalah bahwa Anda harus mendesain segalanya (yang akan dapat dimodifikasi) terlebih dahulu dalam bahasa scripting (atau setidaknya sebagian dari itu) untuk memastikan bahwa logika integrasi Anda benar-benar memenuhi ketentuan untuk modding. Ketika Anda yakin bahwa integrasi skrip Anda cukup fleksibel, tulis ulang bit yang diperlukan dalam C #.
Saya pribadi benci
dynamic
fitur di C # 4.0 (saya pecandu bahasa statis) - tapi ini tanpa keraguan skenario di mana ia harus digunakan dan di mana itu benar-benar bersinar. Komponen C # Anda akan menjadi objek perilaku yang kuat / diperluas .Komponen Lua Anda akan benar-benar dinamis (artikel yang sama di atas akan memberi Anda gambaran tentang bagaimana menerapkan ini). Sebagai contoh:
Sekarang karena Anda menggunakan
dynamic
Anda dapat menggunakan objek Lua seolah-olah mereka adalah kelas nyata di C #.sumber
Saya lebih suka melakukan yang sebaliknya: menulis semua menggunakan bahasa yang dinamis dan kemudian melacak bottleneks (jika ada). Setelah Anda menemukannya, Anda dapat secara selektif mengimplementasikan kembali fungsionalitas yang terlibat menggunakan C # atau (lebih tepatnya) C / C ++.
Saya memberi tahu Anda hal ini terutama karena yang ingin Anda lakukan adalah membatasi fleksibilitas sistem Anda di bagian C #; ketika sistem menjadi kompleks, "mesin" C # Anda akan mulai terlihat seperti penerjemah dinamis, mungkin bukan yang terbaik. Pertanyaannya adalah: apakah Anda bersedia menginvestasikan sebagian besar waktu Anda untuk menulis mesin C # generik atau untuk memperpanjang permainan Anda?
Jika target Anda adalah yang kedua, seperti yang saya yakini, Anda mungkin akan menggunakan waktu Anda dengan berfokus pada mekanisme permainan Anda, membiarkan penerjemah berpengalaman untuk menjalankan kode dinamis.
sumber