
Perusahaan kami memiliki mesin gim sendiri, yang digunakan untuk semua gim yang dikembangkan. Ini menyediakan semua fungsi dasar yang penting:
- rendering
- bekerja dengan SDK;
- bekerja dengan sistem operasi;
- dengan jaringan dan sumber daya.
Namun, ia tidak memiliki nilai untuk Unity - sistem yang nyaman untuk mengatur adegan dan objek game, serta editor untuk mereka.
Di sini saya ingin memberi tahu bagaimana kami memperkenalkan semua fasilitas ini dan apa yang kami lakukan.
Apa yang sekarang
Sekarang kami memiliki beberapa kemiripan sistem komponen dalam Unity dengan semua subsistem dan editor penting. Namun, karena kami melanjutkan dari kebutuhan proyek spesifik kami, ada perbedaan yang cukup signifikan.
Kami memiliki objek visual yang disimpan dalam adegan. Objek-objek ini terdiri dari node yang diatur dalam hierarki dan setiap node dapat memiliki sejumlah entitas, seperti:
- Transform - transformasi node;
- Komponen - terlibat dalam rendering dan hanya ada satu atau tidak sama sekali. Komponennya adalah sprite, mesh, partikel, dan entitas lain yang dapat ditampilkan. Persamaan terdekat dengan Unity adalah Renderer;
- Perilaku - bertanggung jawab atas perilaku, dan mungkin ada beberapa. Ini adalah analog langsung dari MonoBehaviour in Unity, logika apa pun tertulis di dalamnya;
- Sortasi adalah suatu entitas yang bertanggung jawab atas urutan di mana node dalam sebuah adegan ditampilkan. Karena sistem kami seharusnya mudah diintegrasikan ke dalam game yang sudah berjalan, dengan logika yang ada dan beragam untuk menampilkan objek, perlu untuk dapat mengintegrasikan entitas baru ke yang lama. Jadi penyortiran memungkinkan Anda untuk mentransfer kendali atas urutan tampilan ke kode eksternal.
Seperti halnya Unity, programmer membuat komponen, perilaku, atau pengurutan. Untuk melakukan ini, cukup tulis sebuah kelas, definisikan kembali peristiwa yang diperlukan (Perbarui, OnStart, dll.) Dan tandai bidang yang diperlukan dengan cara khusus. Di UnrealEngine, ini dilakukan dengan makro, dan kami memutuskan untuk menggunakan tag di komentar.
Lebih lanjut di kelas, dengan mempertimbangkan tag, semua kode akan dihasilkan, yang diperlukan untuk menyimpan dan memuat data, untuk pekerjaan editor, untuk mendukung kloning dan fungsi kecil lainnya.
Serialisasi dan generasi editor otomatis didukung tidak hanya untuk entitas yang disimpan dalam objek visual, tetapi juga untuk kelas apa pun. Untuk melakukan ini, cukup mewarisi itu dari kelas Serializable khusus dan menandai properti yang diperlukan dengan tag. Dan jika Anda ingin instance kelas menjadi aset penuh (analog dari ScriptableObject dari Unity), maka kelas harus diwarisi dari kelas Aset.
Akibatnya, perpustakaan menyediakan kesempatan untuk dengan cepat mengembangkan fungsionalitas baru. Dan sekarang bagian dari pekerjaan mengembangkan game, misalnya, membuat efek, tata letak UI, desain adegan permainan, dapat ditransfer ke spesialis yang dapat menanganinya lebih baik daripada programmer.
Blok utama

Pembuatan Kode
Agar banyak sistem dapat berfungsi, Anda perlu menulis cukup banyak kode rutin, yang diperlukan karena kurangnya refleksi dalam C ++ (
refleksi - kemampuan untuk mengakses informasi tentang jenis-jenis dalam kode program). Karenanya, kami menghasilkan sebagian besar kode teknis ini.
Generator adalah satu set skrip python yang mengurai file header dan menghasilkan kode yang diperlukan berdasarkan mereka. Untuk pengaturan generasi yang fleksibel, tag khusus digunakan dalam komentar.
Kami dapat menghasilkan kode untuk subsistem berikut:
- Serialisasi - digunakan untuk menyimpan / memuat data dari disk atau saat transmisi melalui jaringan. Akan dipertimbangkan lebih detail nanti.
- Binding untuk perpustakaan refleksi - digunakan untuk secara otomatis menampilkan editor ke data. Akan dibahas pada bab tentang editor.
- Kode untuk entitas kloning - digunakan untuk mengkloning entitas dalam editor dan dalam game.
- Kode untuk refleksi runtime ringan kami.
→ Contoh kode yang dihasilkan untuk satu kelas dapat
ditemukan di sini.Parsing c ++
Hampir semua opsi untuk menyelesaikan masalah parsing file header menyebabkan parsing kode dengan dentang. Tetapi setelah percobaan, menjadi jelas bahwa kecepatan solusi seperti itu tidak cocok untuk kita sama sekali. Selain itu, kekuatan yang diberikan dentang tidak diperlukan untuk kita.
Oleh karena itu, solusi lain ditemukan:
CppHeaderParser . Ini adalah pustaka file tunggal python yang dapat membaca file header. Ini sangat primitif, tidak mengikuti #include, melompati makro, tidak menguraikan karakter, dan bekerja sangat cepat.
Kami masih menggunakannya hingga hari ini, namun, kami harus melakukan cukup banyak pengeditan untuk memperbaiki bug dan memperluas kemampuan kami, khususnya, dukungan untuk inovasi dari C ++ 17 telah ditambahkan.
Kami ingin menghindari kesalahpahaman terkait dengan ketidakpastian status pembuatan kode. Oleh karena itu, diputuskan bahwa generasi harus terjadi sepenuhnya secara otomatis. Kami menggunakan CMake, di mana generasi dimulai pada setiap kompilasi (kami tidak dapat mengonfigurasi generasi untuk memulai hanya ketika dependensi berubah). Agar ini tidak memakan banyak waktu dan tidak mengganggu, kami menyimpan cache dengan hasil parsing semua file dan isi direktori. Akibatnya, awal pembuatan kode hanya membutuhkan beberapa detik.
Pembuat kode
Dengan generasi, semuanya lebih sederhana. Ada banyak perpustakaan untuk menghasilkan apa pun dari templat. Kami memilih
Templite + , karena sangat kecil, memiliki fungsi yang diperlukan dan berfungsi dengan baik.
Ada dua pendekatan untuk generasi. Versi pertama berisi banyak kondisi, pemeriksaan, dan kode lainnya, sehingga templatnya sendiri minimal, dan sebagian besar logika dan teks yang dihasilkan dalam kode python. Itu nyaman, karena dalam kode python lebih mudah untuk menulis daripada di template, dan mudah untuk mengacaukan logika rumit sewenang-wenang. Namun, ini juga mengerikan, karena kode python, dicampur dengan sejumlah besar baris kode C ++, tidak nyaman untuk membaca atau menulis. Generator python yang digunakan menyederhanakan situasi, tetapi tidak menghilangkan masalah secara keseluruhan.
Oleh karena itu, versi generasi saat ini didasarkan pada template, dan kode python hanya menyiapkan data yang diperlukan dan sekarang terlihat jauh lebih baik.
Serialisasi
Untuk serialisasi, berbagai perpustakaan dipertimbangkan: protobuf, FlexBuffers, sereal, dll.
Perpustakaan dengan pembuatan kode (Protobuf, FlatBuffers dan lainnya) tidak cocok, karena kami memiliki struktur tulisan tangan dan tidak ada cara untuk mengintegrasikan struktur yang dihasilkan ke dalam kode pengguna. Dan untuk menggandakan jumlah kelas hanya untuk serialisasi terlalu boros.
Perpustakaan
sereal tampaknya menjadi kandidat terbaik - sintaksis yang bagus, implementasi yang jelas, lebih mudah untuk menghasilkan kode serialisasi. Namun, format binernya tidak sesuai dengan kami, seperti format sebagian besar pustaka lainnya. Persyaratan format penting adalah kebebasan dari perangkat keras (data harus dibaca terlepas dari urutan byte dan kedalaman bit) dan format biner harus sesuai untuk penulisan dari python.
Menulis file biner dari python adalah penting, karena kami ingin memiliki skrip universal platform-independen dan proyek-independen yang akan mengkonversi data dari tampilan teks ke file biner. Oleh karena itu, kami menulis skrip yang ternyata menjadi alat serialisasi yang sangat nyaman.
Gagasan utama diambil dari sereal, didasarkan pada arsip dasar untuk membaca dan menulis data. Ahli waris yang berbeda dibuat dari mereka yang mengimplementasikan catatan dalam format yang berbeda: xml, json, binary. Dan kode serialisasi dihasilkan oleh kelas dan menggunakan arsip ini untuk menulis data.

Editor
Kami menggunakan perpustakaan ImGui untuk editor, di mana kami menulis semua jendela editor utama: konten adegan, penampil file dan aset, pemeriksa aset, editor animasi, dll.
Kode editor utama ditulis dengan tangan, tetapi untuk melihat dan mengedit properti kelas tertentu, kami menggunakan pustaka rttr, binning yang dihasilkan untuknya dan kode inspektur umum yang dapat bekerja dengan rttr.
Perpustakaan Refleksi - rttr
Untuk mengatur refleksi dalam C ++, perpustakaan rttr dipilih. Itu tidak memerlukan intervensi di dalam kelas itu sendiri, memiliki API yang nyaman dan dapat dimengerti, memiliki dukungan untuk koleksi dan pembungkus atas jenis (seperti smart pointer) dengan kemampuan untuk mendaftarkan pembungkus Anda dan memungkinkan Anda untuk melakukan apa pun yang diperlukan (membuat jenis, beralih ke anggota kelas, mengubah properti, metode panggilan, dll.).
Ini juga memungkinkan Anda untuk bekerja dengan pointer, seperti dengan bidang biasa, dan menggunakan pola objek nol, yang sangat menyederhanakan bekerja dengannya.
Kekurangan dari perpustakaan adalah besar dan tidak terlalu cepat, jadi kami menggunakannya hanya untuk editor. Dalam kode permainan untuk bekerja dengan parameter objek, misalnya, untuk sistem animasi, kami menggunakan perpustakaan refleksi paling sederhana dari produksi kami sendiri.
Pustaka rttr membutuhkan penulisan penjilidan dengan pernyataan semua metode dan properti kelas. Ikatan ini dihasilkan dari kode python untuk semua kelas yang membutuhkan dukungan pengeditan. Dan karena fakta bahwa rttr dapat menambahkan metadata untuk entitas apa pun, pembuat kode dapat mengatur pengaturan yang berbeda untuk anggota kelas: tooltips, parameter batas nilai yang dapat diterima untuk bidang numerik, inspektur khusus untuk bidang tersebut, dll. Metadata ini digunakan di inspektur untuk menampilkan antarmuka pengeditan. .
→ Contoh kode untuk mendeklarasikan kelas dalam rttr dapat
ditemukan di siniInspektur
Kode editor itu sendiri sangat jarang bekerja dengan rttr secara langsung. Lapisan yang paling umum digunakan adalah bahwa objek dapat menggambar inspektur ImGui untuk itu. Ini adalah kode tulisan tangan yang berfungsi dengan data dari rttr dan menggambar kontrol ImGui untuknya.
Untuk menyesuaikan tampilan antarmuka pengeditan data, metadata yang ditentukan saat pendaftaran di rttr digunakan. Kami mendukung semua jenis primitif, koleksi, dimungkinkan untuk membuat objek yang disimpan oleh nilai dan oleh pointer. Jika anggota kelas adalah penunjuk ke kelas dasar, maka Anda dapat memilih pewaris tertentu selama pembuatan.
Juga, kode inspektur mengambil dukungan untuk membatalkan operasi - ketika mengubah nilai, perintah dibuat untuk mengubah data, yang kemudian dapat digulirkan kembali.
Sejauh ini, kami tidak memiliki sistem untuk menentukan perubahan atom dengan kemampuan untuk melihat dan menyimpannya. Ini berarti bahwa kami tidak memiliki dukungan untuk menyimpan properti yang diubah dari objek ke tempat kejadian dan menerapkan perubahan ini setelah memuat cetakan. Dan juga tidak ada pembuatan otomatis trek animasi ketika mengubah properti suatu objek.
Windows dan editor
Saat ini, berbagai subsistem dan editor telah dibuat berdasarkan editor, pembuatan kode, dan sistem penciptaan aset kami:
- Sistem antarmuka game menyediakan tata letak yang fleksibel dan nyaman dan mencakup semua elemen antarmuka yang diperlukan. Sistem scripting visual perilaku jendela dibuat untuknya.
- Sistem untuk beralih status animasi mirip dengan editor negara dalam animasi di Unity, tetapi agak berbeda dengan prinsip operasi dan memiliki aplikasi yang lebih luas.
- Perancang pencarian dan acara memungkinkan Anda untuk secara fleksibel menyesuaikan acara permainan, pencarian dan tutorial, hampir tanpa partisipasi programmer.
Ketika mengembangkan semua subsistem dan editor ini, kami mengamati
Unity ,
Unreal Engine, dan berusaha mengambil yang terbaik dari mereka. Dan beberapa subsistem ini dibuat di sisi proyek game.
Untuk meringkas
Sebagai kesimpulan, saya ingin menggambarkan bagaimana pengembangan dilakukan. Versi kerja pertama dibuat dan diintegrasikan ke dalam beberapa proyek game oleh beberapa orang hanya dalam waktu dua bulan. Belum memiliki generasi kode, dan banyaknya editor yang sekarang. Pada saat yang sama, itu adalah versi yang berfungsi, dengan mana gerakan maju dimulai. Tidak dapat dikatakan bahwa pada saat itu ini sesuai dengan vektor utama pengembangan mesin, semuanya bertumpu pada antusiasme beberapa orang dan pemahaman yang jelas tentang perlunya dan kebenaran dari apa yang kami lakukan.
Semua pengembangan selanjutnya dilakukan dengan sangat aktif dan evolusioner, langkah demi langkah, tetapi selalu mempertimbangkan kepentingan proyek game. Saat ini, lebih dari sepuluh orang sedang mengerjakan pengembangan "Persatuan kecil kami" dan pengembangan versi baru tidak lagi secepat dan secepat sebelumnya.
Namun demikian, kami telah mencapai hasil yang luar biasa hanya dalam beberapa tahun dan tidak akan berhenti. Saya berharap Anda bergerak maju ke apa yang Anda anggap benar dan penting bagi diri Anda dan perusahaan secara keseluruhan.