Saya sarankan mulai dengan membaca 3 kebohongan besar Mike Acton, karena Anda melanggar dua dari mereka. Saya serius, ini akan mengubah cara Anda mendesain kode Anda: http://cellperformance.beyond3d.com/articles/2008/03/three-big-lies.html
Jadi, yang mana yang Anda langgar?
Kebohongan 3 - Kode lebih penting daripada data
Anda berbicara tentang injeksi ketergantungan, yang mungkin berguna dalam beberapa (dan hanya beberapa) contoh tetapi harus selalu membunyikan bel alarm besar jika Anda menggunakannya, terutama dalam pengembangan game! Mengapa? Karena itu seringkali merupakan abstraksi yang tidak perlu. Dan abstraksi di tempat yang salah mengerikan. Jadi kamu punya game. Gim ini memiliki manajer untuk berbagai komponen. Semua komponen sudah ditentukan. Jadi buat kelas di suatu tempat di dalam kode loop permainan utama Anda yang "memiliki" para manajer. Suka:
private CollissionManager _collissionManager;
private BulletManager _bulletManager;
Berikan beberapa fungsi getter untuk mendapatkan setiap kelas manajer (getBulletManager ()). Mungkin kelas ini sendiri adalah Singleton atau dapat dicapai dari satu (Anda mungkin memiliki singleton Game sentral di suatu tempat). Tidak ada yang salah dengan data dan perilaku hard-code yang didefinisikan dengan baik.
Jangan membuat Manajer Manajer yang memungkinkan Anda mendaftar Manajer menggunakan kunci, yang dapat diambil menggunakan kunci itu oleh kelas lain yang ingin menggunakan manajer. Ini adalah sistem yang hebat dan sangat fleksibel, tetapi berbicara tentang permainan di sini. Anda tahu persis sistem mana yang ada dalam game. Kenapa berpura-pura tidak? Karena ini adalah sistem untuk orang yang menganggap kode lebih penting daripada data. Mereka akan berkata "Kode ini fleksibel, data hanya mengisinya". Tetapi kode hanyalah data. Sistem yang saya jelaskan jauh lebih mudah, lebih dapat diandalkan, lebih mudah dirawat dan jauh lebih fleksibel (misalnya, jika perilaku satu manajer berbeda dari manajer lain, Anda hanya perlu mengubah beberapa baris alih-alih mengerjakan ulang seluruh sistem)
Kebohongan 2 - Kode harus dirancang di sekitar model dunia
Jadi, Anda memiliki entitas di dunia game. Entitas memiliki sejumlah komponen yang mendefinisikan perilakunya. Jadi, Anda membuat kelas Entitas dengan daftar objek Komponen, dan fungsi Pembaruan () yang memanggil fungsi Pembaruan () dari setiap Komponen. Baik?
Tidak :) Itu mendesain model dunia: kamu punya peluru di gimmu, jadi kamu menambahkan kelas Bullet. Kemudian Anda memperbarui masing-masing Bullet dan beralih ke yang berikutnya. Ini benar-benar akan membunuh kinerja Anda dan itu memberi Anda basis kode berbelit-belit mengerikan dengan kode duplikat di mana-mana dan tidak ada penataan logis dari kode yang sama. (Lihat jawaban saya di sini untuk penjelasan yang lebih terperinci tentang mengapa desain OO tradisional menyebalkan, atau lihat Desain Berorientasi Data)
Mari kita lihat situasi tanpa bias OO kita. Kami menginginkan yang berikut, tidak lebih tidak kurang (harap dicatat tidak ada persyaratan untuk membuat kelas untuk entitas atau objek):
- Anda memiliki banyak entitas
- Entitas terdiri dari sejumlah komponen yang menentukan perilaku entitas
- Anda ingin memperbarui setiap komponen dalam gim setiap frame, lebih disukai dengan cara yang terkontrol
- Selain mengidentifikasi komponen sebagai milik bersama, tidak ada entitas yang perlu dilakukan. Ini adalah tautan / ID untuk beberapa komponen.
Dan mari kita lihat situasinya. Sistem komponen Anda akan memperbarui perilaku setiap objek dalam game setiap frame. Ini jelas merupakan sistem kritis mesin Anda. Kinerja sangat penting di sini!
Jika Anda terbiasa dengan arsitektur komputer atau Desain Berorientasi Data, Anda tahu bagaimana kinerja terbaik dicapai: memori penuh sesak dan dengan mengelompokkan eksekusi kode. Jika Anda mengeksekusi cuplikan kode A, B dan C seperti ini: ABCABCABC, Anda tidak akan mendapatkan kinerja yang sama seperti ketika Anda mengeksekusinya seperti ini: AAABBBCCC. Ini bukan hanya karena instruksi dan cache data akan digunakan lebih efisien, tetapi juga karena jika Anda mengeksekusi semua "A" satu sama lain, ada banyak ruang untuk optimasi: menghapus kode duplikat, data prakalkulasi yang digunakan oleh semua "A", dll.
Jadi jika kita ingin memperbarui semua komponen, jangan buat kelas / objek dengan fungsi pembaruan. Jangan panggil fungsi pembaruan itu untuk setiap komponen di setiap entitas. Itulah solusi "ABCABCABC". Mari kita kelompokkan semua pembaruan komponen identik secara bersamaan. Kemudian kita dapat memperbarui semua komponen-A, diikuti oleh B, dll. Apa yang kita butuhkan untuk membuat ini?
Pertama, kita membutuhkan Manajer Komponen. Untuk setiap jenis komponen dalam game, kita membutuhkan kelas manajer. Ini memiliki fungsi pembaruan yang akan memperbarui semua komponen jenis itu. Ini memiliki fungsi buat yang akan menambahkan komponen baru dari jenis itu dan fungsi hapus yang akan menghancurkan komponen yang ditentukan. Mungkin ada fungsi pembantu lain untuk mendapatkan dan mengatur data khusus untuk komponen itu (misalnya: mengatur model 3D untuk Komponen Model). Perhatikan bahwa dalam beberapa hal manajer adalah kotak hitam bagi dunia luar. Kami tidak tahu bagaimana data masing-masing komponen disimpan. Kami tidak tahu bagaimana setiap komponen diperbarui. Kami tidak peduli, selama komponen berperilaku sebagaimana mestinya.
Selanjutnya kita membutuhkan entitas. Anda bisa menjadikan ini kelas, tapi itu tidak perlu. Entitas bisa tidak lebih dari ID integer unik atau string hash (demikian juga integer). Saat Anda membuat komponen untuk Entitas, Anda meneruskan ID sebagai argumen kepada Manajer. Saat Anda ingin menghapus komponen, Anda meneruskan ID lagi. Mungkin ada beberapa keuntungan untuk menambahkan sedikit lebih banyak data ke Entity daripada hanya menjadikannya ID, tetapi itu hanya akan menjadi fungsi pembantu karena seperti yang saya sebutkan dalam persyaratan, semua perilaku entitas ditentukan oleh komponen itu sendiri. Ini mesin Anda, jadi lakukan apa yang masuk akal untuk Anda.
Yang kami butuhkan adalah Manajer Entitas. Kelas ini akan menghasilkan ID unik jika Anda menggunakan solusi ID-saja, atau dapat digunakan untuk membuat / mengelola objek Entitas. Itu juga dapat menyimpan daftar semua entitas dalam game jika Anda membutuhkannya. Entity Manager bisa menjadi kelas pusat dari sistem komponen Anda, menyimpan referensi ke semua Manajer Komponen dalam game Anda dan memanggil fungsi pembaruan mereka dalam urutan yang benar. Dengan cara itu semua permainan harus dilakukan adalah memanggil EntityManager.update () dan seluruh sistem dipisahkan dengan baik dari sisa mesin Anda.
Itulah pandangan mata burung, mari kita lihat bagaimana manajer komponen bekerja. Inilah yang Anda butuhkan:
- Buat data komponen saat buat (entityID) dipanggil
- Hapus data komponen saat menghapus (entityID) dipanggil
- Perbarui semua (komponen) data komponen ketika pembaruan () dipanggil (yaitu tidak semua komponen perlu memperbarui setiap frame)
Yang terakhir adalah di mana Anda mendefinisikan perilaku komponen / logika dan sepenuhnya bergantung pada jenis komponen yang Anda tulis. AnimationComponent akan memperbarui data Animasi berdasarkan bingkai yang ada di. DragableComponent hanya akan memperbarui komponen yang diseret oleh mouse. PhysicsComponent akan memperbarui data dalam sistem fisika. Namun, karena Anda memperbarui semua komponen dari tipe yang sama dalam sekali jalan, Anda dapat melakukan beberapa optimasi yang tidak mungkin dilakukan ketika setiap komponen adalah objek yang terpisah dengan fungsi pembaruan yang dapat dipanggil kapan saja.
Perhatikan bahwa saya masih belum pernah meminta untuk membuat kelas XxxComponent untuk menyimpan data komponen. Terserah kamu. Anda suka Desain Berorientasi Data? Kemudian susun data dalam array terpisah untuk setiap variabel. Anda suka Object Oriented Design? (Saya tidak akan merekomendasikannya, itu masih akan membunuh kinerja Anda di banyak tempat) Kemudian buat objek XxxComponent yang akan menyimpan data masing-masing komponen.
Hal hebat tentang manajer adalah enkapsulasi. Sekarang enkapsulasi adalah salah satu filosofi yang paling banyak disalahgunakan dalam dunia pemrograman. Inilah yang harus digunakan. Hanya manajer yang tahu data komponen apa yang disimpan di mana, bagaimana logika komponen bekerja. Ada beberapa fungsi untuk mendapatkan / mengatur data tetapi hanya itu. Anda dapat menulis ulang seluruh manajer dan kelas-kelas yang mendasarinya dan jika Anda tidak mengubah antarmuka publik, tidak seorang pun memperhatikan. Mesin fisika berubah? Tulis ulang Manajer FisikaComponent dan selesai.
Lalu ada satu hal terakhir: komunikasi dan berbagi data antar komponen. Sekarang ini rumit dan tidak ada solusi satu ukuran untuk semua. Anda bisa membuat fungsi get / set di manajer untuk memungkinkan misalnya komponen tumbukan untuk mendapatkan posisi dari komponen posisi (yaitu PositionManager.getPosition (entityID)). Anda dapat menggunakan sistem acara. Anda dapat menyimpan beberapa data bersama dalam entitas (solusi paling jelek menurut saya). Anda dapat menggunakan (ini sering digunakan) sistem pesan. Atau gunakan kombinasi beberapa sistem! Saya tidak punya waktu atau pengalaman untuk masuk ke masing-masing sistem ini, tetapi pencarian google dan stackoverflow adalah teman Anda.