Beberapa waktu yang lalu saya mulai bekerja dengan Unity dan saya masih bergumul dengan masalah skrip yang sangat erat. Bagaimana saya bisa menyusun kode saya untuk menghindari masalah ini?
Sebagai contoh:
Saya ingin memiliki sistem kesehatan dan kematian dalam skrip terpisah. Saya juga ingin memiliki skrip berjalan dipertukarkan yang berbeda yang memungkinkan saya untuk mengubah cara karakter pemain dipindahkan (berbasis fisika, kontrol inersia seperti di Mario versus kontrol ketat, berkedut seperti di Super Meat Boy). Skrip Kesehatan perlu memiliki referensi ke skrip Kematian, sehingga dapat memicu metode Die () ketika kesehatan pemain mencapai 0. Skrip Kematian harus menyimpan beberapa referensi untuk skrip berjalan yang digunakan, untuk menonaktifkan walk on death (I lelah dengan zombie).
Saya biasanya akan membuat antarmuka, seperti IWalking
, IHealth
dan IDeath
, sehingga saya dapat mengubah elemen-elemen ini dengan cepat tanpa merusak sisa kode saya. Saya ingin mereka mengaturnya dengan skrip terpisah pada objek pemain, katakanlah PlayerScriptDependancyInjector
. Mungkin skrip itu akan memiliki publik IWalking
, IHealth
dan IDeath
atribut, sehingga dependensi dapat ditetapkan oleh desainer level dari inspektur dengan menyeret dan menjatuhkan skrip yang sesuai.
Itu akan memungkinkan saya untuk menambahkan perilaku ke objek game dengan mudah dan tidak khawatir tentang dependensi hard-coded.
Masalah di Unity
The masalah adalah bahwa dalam Persatuan saya tidak dapat mengekspos antarmuka dalam inspektur, dan jika saya menulis inspektur saya sendiri, referensi wont mendapatkan serial, dan itu banyak pekerjaan yang tidak perlu. Itu sebabnya saya pergi dengan menulis kode yang sangat erat. Death
Skrip saya memaparkan referensi ke InertiveWalking
skrip. Tetapi jika saya memutuskan saya ingin karakter pemain untuk mengontrol dengan ketat, saya tidak bisa hanya drag and drop TightWalking
script, saya perlu mengubah Death
skrip. Itu menyebalkan. Saya dapat menghadapinya, tetapi jiwa saya menangis setiap kali saya melakukan hal seperti ini.
Apa alternatif yang disukai untuk antarmuka di Unity? Bagaimana saya memperbaiki masalah ini? Saya menemukan ini , tetapi itu memberi tahu saya apa yang sudah saya ketahui, dan itu tidak memberi tahu saya bagaimana melakukannya di Unity! Ini juga membahas apa yang harus dilakukan, bukan bagaimana, itu tidak membahas masalah kopling ketat antar skrip.
Secara keseluruhan, saya merasa itu ditulis untuk orang-orang yang datang ke Unity dengan latar belakang desain game yang baru belajar cara membuat kode, dan ada sangat sedikit sumber daya di Unity untuk pengembang reguler. Apakah ada cara standar untuk menyusun kode Anda di Unity, atau apakah saya harus mencari tahu metode saya sendiri?
The problem is that in Unity I can't expose interfaces in the inspector
Saya rasa saya tidak mengerti apa yang Anda maksud dengan "mengekspos antarmuka di inspektur" karena pikiran pertama saya adalah "mengapa tidak?"Jawaban:
Ada beberapa cara yang bisa Anda lakukan untuk menghindari pemasangan skrip yang ketat. Internal to Unity adalah fungsi SendMessage yang ketika ditargetkan pada GameObject Monobehaviour mengirimkan pesan itu ke semua yang ada di objek game. Jadi Anda mungkin memiliki sesuatu seperti ini di objek kesehatan Anda:
Ini sangat disederhanakan tetapi harus menunjukkan dengan tepat apa yang saya maksudkan. Properti yang melempar pesan seperti Anda akan menghadiri suatu acara. Skrip kematian Anda akan mencegat pesan ini (dan jika tidak ada yang mencegatnya, SendMessageOptions.DontRequireReceiver akan menjamin Anda tidak menerima pengecualian) dan melakukan tindakan berdasarkan nilai kesehatan baru setelah ditetapkan. Seperti itu:
Namun, tidak ada jaminan pemesanan. Dalam pengalaman saya, selalu ada skrip paling atas pada GameObject ke skrip terbawah, tapi saya tidak akan mengandalkan apa pun yang tidak bisa Anda amati sendiri.
Ada solusi lain yang dapat Anda selidiki yaitu membangun fungsi GameObject khusus yang mendapatkan Komponen dan hanya mengembalikan komponen yang memiliki antarmuka spesifik alih-alih Tipe, itu akan memakan waktu sedikit lebih lama tetapi dapat melayani tujuan Anda dengan baik.
Selain itu, Anda dapat menulis editor khusus yang memeriksa objek yang ditugaskan ke posisi di editor untuk Antarmuka itu sebelum benar-benar melakukan tugas (dalam hal ini Anda akan menetapkan skrip seperti biasa memungkinkan serialisasi, tetapi melemparkan kesalahan jika tidak menggunakan antarmuka yang benar dan menolak penugasan). Saya telah melakukan kedua hal ini sebelumnya dan dapat menghasilkan kode itu tetapi akan membutuhkan waktu untuk menemukannya terlebih dahulu.
sumber
Saya pribadi TIDAK PERNAH menggunakan SendMessage. Masih ada ketergantungan antara komponen Anda dengan SendMessage, hanya saja tampilannya sangat buruk dan mudah rusak. Menggunakan antarmuka dan / atau delegasi benar-benar menghilangkan kebutuhan untuk menggunakan SendMessage (yang lebih lambat, meskipun itu seharusnya tidak menjadi masalah sampai perlu).
http://forum.unity3d.com/threads/free-vfw-full-set-of-drawers-savesystem-serialize-interfaces-generics-auto-props-delegates.266165/
Gunakan barang orang ini. Ini menyediakan banyak hal editor seperti antarmuka yang terbuka, DAN delegasi. Ada banyak barang di luar sana seperti ini, atau Anda bahkan dapat membuatnya sendiri. Apa pun yang Anda lakukan, JANGAN kompromi desain Anda karena kurangnya dukungan skrip Unity. Hal-hal ini harus dalam Unity secara default.
Jika ini bukan opsi, seret dan jatuhkan kelas monobehaviour sebagai gantinya dan secara dinamis melemparkan mereka ke antarmuka yang diperlukan. Ini lebih banyak pekerjaan, dan kurang elegan, tetapi itu menyelesaikan pekerjaan.
sumber
Pendekatan Unity-spesifik yang terjadi pada saya akan awalnya
GetComponents<MonoBehaviour>()
untuk mendapatkan daftar skrip, dan kemudian melemparkan skrip-skrip tersebut ke dalam variabel pribadi untuk antarmuka spesifik. Anda ingin melakukan ini di Mulai () pada skrip injector ketergantungan. Sesuatu seperti:Bahkan, dengan metode ini Anda bahkan bisa meminta masing-masing komponen memberi tahu skrip ketergantungan injeksi, dengan menggunakan perintah typeof () dan tipe 'Type' . Dengan kata lain, komponen memberikan injector dependensi daftar jenis, dan kemudian injector dependensi mengembalikan daftar objek jenis tersebut.
Atau, Anda bisa menggunakan kelas abstrak alih-alih antarmuka. Kelas abstrak dapat mencapai tujuan yang hampir sama dengan antarmuka, dan Anda dapat merujuknya pada Inspektur.
sumber
GetComponent<IMyInterface>()
danGetComponents<IMyInterface>()
secara langsung, yang mengembalikan komponen yang mengimplementasikannya.Dari pengalaman saya sendiri, game dev secara tradisional melibatkan pendekatan yang lebih pragmatis daripada pengembang industri (dengan lapisan abstraksi yang lebih sedikit). Sebagian karena Anda ingin mengunggulkan kinerja daripada pemeliharaan, dan juga karena Anda cenderung menggunakan kembali kode Anda dalam konteks lain (biasanya ada penggabungan intrinsik yang kuat antara grafik dan logika permainan)
Saya benar-benar memahami kekhawatiran Anda, terutama karena masalah kinerja kurang penting dengan perangkat terbaru, tetapi perasaan saya adalah bahwa Anda harus mengembangkan solusi sendiri jika Anda ingin menerapkan praktik terbaik OOP industri untuk pengembangan game Unity (seperti injeksi ketergantungan, dll ...).
sumber
Lihatlah IUnified ( http://u3d.as/content/wounded-wolf/iunified/5H1 ), yang memungkinkan Anda mengekspos antarmuka di editor, seperti yang Anda sebutkan. Ada sedikit lebih banyak kabel yang harus dilakukan dibandingkan dengan deklarasi variabel normal, itu saja.
Selain itu, seperti jawaban lain menyebutkan, menggunakan SendMessage tidak menghapus kopling. Sebaliknya itu membuatnya tidak keluar pada saat kompilasi. Sangat buruk - saat Anda meningkatkan proyek Anda, itu bisa menjadi mimpi buruk untuk dipertahankan.
Jika Anda lebih lanjut mencari untuk memisahkan kode Anda tanpa menggunakan antarmuka di editor, Anda dapat menggunakan sistem berbasis peristiwa yang diketik dengan kuat (atau bahkan yang diketik dengan longgar sebenarnya, tetapi sekali lagi, lebih sulit untuk dipelihara). Saya mendasarkan tulisan saya pada posting ini , yang sangat nyaman sebagai titik awal. Berhati-hatilah dalam menciptakan banyak acara karena dapat memicu GC. Saya mengatasi masalah dengan kumpulan objek, tapi itu karena saya bekerja dengan ponsel.
sumber
Sumber: http://www.udellgames.com/posts/ultra-useful-unity-snippet-developers-use-interfaces/
sumber
Saya menggunakan beberapa strategi berbeda untuk mengatasi keterbatasan yang Anda sebutkan.
Salah satu yang saya tidak lihat disebutkan adalah menggunakan
MonoBehavior
yang memperlihatkan akses ke berbagai implementasi dari antarmuka yang diberikan. Sebagai contoh, saya memiliki antarmuka yang mendefinisikan bagaimana Raycast diimplementasikan bernamaIRaycastStrategy
Saya kemudian mengimplementasikannya di kelas yang berbeda dengan kelas suka
LinecastRaycastStrategy
danSphereRaycastStrategy
dan mengimplementasikan MonoBehaviorRaycastStrategySelector
yang memperlihatkan contoh tunggal dari setiap jenis. Sesuatu sepertiRaycastStrategySelectionBehavior
, yang diimplementasikan sesuatu seperti:Jika implementasinya cenderung merujuk fungsi Unity dalam konstruksi mereka (atau hanya untuk berada di sisi yang aman, saya hanya lupa ini ketika mengetikkan contoh ini) digunakan
Awake
untuk menghindari masalah dengan memanggil konstruktor atau merujuk fungsi Unity di editor atau di luar main benangStrategi ini memang memiliki beberapa kelemahan, terutama ketika Anda harus mengelola banyak implementasi berbeda yang berubah dengan cepat. Masalah dengan kurangnya waktu kompilasi dapat dibantu dengan menggunakan editor khusus, karena nilai enum dapat diserialisasi tanpa ada pekerjaan khusus (meskipun saya biasanya melupakan ini, karena implementasi saya cenderung tidak sebanyak itu).
Saya menggunakan strategi ini karena fleksibilitas yang diberikannya kepada Anda dengan didasarkan pada MonoBehaviors tanpa benar-benar memaksa Anda untuk mengimplementasikan antarmuka menggunakan MonoBehaviors. Ini memberi Anda "yang terbaik dari kedua dunia" karena Selector dapat diakses menggunakan
GetComponent
, serialisasi semua ditangani oleh Unity dan Selector dapat menerapkan logika pada saat run-time untuk secara otomatis beralih implementasi berdasarkan faktor-faktor tertentu.sumber