
Hai Habr! Dalam publikasi ini saya ingin berbagi pengalaman mengembangkan game mobile besar, dengan kota besar dan lalu lintas. Contoh dan teknik yang dijelaskan dalam publikasi tidak mengklaim disebut referensi dan ideal. Saya bukan spesialis bersertifikat dan tidak ingin mengulang pengalaman saya. Tujuan permainan ini adalah untuk mendapatkan pengalaman yang menarik, untuk mendapatkan game yang dioptimalkan dengan dunia terbuka. Selama pengembangan, saya mencoba menyederhanakan kode sebanyak mungkin. Sayangnya, saya tidak menggunakan ECS, tetapi berdosa dengan singleton.
Permainan
Sebuah game dengan tema mafia. Dalam permainan, saya mencoba membuat ulang Amerika 30-40. Pada dasarnya, permainan adalah strategi ekonomi orang pertama. Pemain menangkap bisnis dan mencoba untuk tetap bertahan.
Diimplementasikan: lalu lintas mobil (lampu lalu lintas, penghindaran tabrakan), lalu lintas manusia, bar, kasino, klub, apartemen pemain, membeli jas, mengganti jas, membeli / mengecat / mengisi bahan bakar, polisi, keamanan / gangster, ekonomi, sumber daya jual / beli.
Arsitektur

Saya menyesal tidak menggunakan ECS, tetapi mencoba bersepeda. Pada akhirnya, semuanya ternyata rumit dan terlalu tergantung. Aplikasi memiliki satu titik masuk - aplikasi (pergi) objek game, di mana kelas Aplikasi dengan nama yang sama hang. Dia bertanggung jawab untuk melakukan preloading pada basis data, mengisi kumpulan dan pengaturan awal. Selain itu, beberapa kelas komponen manajer singleton lainnya jatuh di pundak aplikasi (go).
- Audiomanager
- UIManager
- Manajer input
Saya dengan fanatik mencoba membuat arsitektur seperti itu di mana saya dapat mengatur berbagai komponen dari manajer. Misalnya, AudioManager mengelola semua suara, UIManager berisi semua elemen UI dan metode untuk manajemen. Semua input diproses melalui InputManager menggunakan acara dan delegasi.
Manajer Audio Sederhana. Ini memungkinkan Anda untuk menambahkan komponen Audio ke objek gim dan, jika perlu, memutar suara:
public class AudioManager : MonoBehaviour { public static AudioManager instance = null;
Saat startup, metode AddAudio menambahkan komponen, dan kemudian dari mana saja kita dapat memutar suara yang kita butuhkan:
AudioManager.instance.isMetalHit = true;
Dalam contoh ini, akan lebih bijaksana untuk menempatkan oneshot memutar kembali ke metode.
Seperti apa bentuk InputManager yang disederhanakan:
public class InputManager : MonoBehaviour { public static InputManager instance = null; public float horizontal, vertical; public delegate void ClickAction(); public static event ClickAction OnAimKeyClicked;
Saya
meletakkan metode
AimKeyDown pada
tombol , dan menandatangani skrip kontrol senjata di OnAimKeyClicked:
InputManager.instance.OnAimKeyClicked += GunShot;
Seluruh sistem input saya diimplementasikan dengan cara yang serupa. Saya tidak melihat adanya masalah dengan kecepatan. Ini memungkinkan kami mengumpulkan semua penangan klik di satu tempat - InputManager.
Optimasi
Mari kita beralih ke yang paling menarik. Untuk pemula, topik optimasi di Unity menyakitkan dan penuh dengan banyak jebakan. Saya akan membagikan apa yang saya hadapi.
1. Caching komponen (mulai dengan dasar-dasar sederhana)Seringkali di Toster Anda dapat menemukan pertanyaan dengan contoh kapan, di mana GetComponent digunakan dalam Pembaruan. Anda tidak dapat melakukan ini, GetComponent mencari komponen pada objek. Operasi ini lambat dan menyebabkannya dalam Pembaruan, Anda berisiko kehilangan FPS berharga. Berikut ini adalah penjelasan yang bagus tentang
komponen caching .
2. Menggunakan SendMessageMenggunakan SendMessage () lebih lambat dari GetComponent (). SendMessage memeriksa setiap skrip untuk menemukan metode dengan nama yang diinginkan menggunakan perbandingan string. GetComponent menemukan skrip melalui perbandingan jenis dan memanggil metode secara langsung.
3. Perbandingan tag objekGunakan metode CompareTag sebagai ganti obj.tag == βstringβ. Di Unity, mengekstraksi string dari objek game membuat string duplikat, yang menambahkan pekerjaan ke pengumpul sampah. Lebih baik untuk menghindari mendapatkan nama objek game. Anda tidak dapat memanggil CompareTag di Pembaruan serta membaca operasi berat.
4. BahanSemakin sedikit material, semakin baik. Kurangi jumlah bahan sebanyak mungkin. Untuk mencapai ini, bantu tekstur satin. Misalnya, hampir seluruh kota dalam permainan saya terdiri dari 2-3 atlas. Perlu dicatat bahwa tidak semua perangkat seluler dapat bekerja dengan atlas besar. Karena itu, jika Anda ingin mendukung perangkat yang berusia 11-13 tahun, perlu dipertimbangkan. Saya memutuskan untuk menolak dukungan untuk android di bawah 5.1, karena ini sebagian besar perangkat lama. Selain itu, permainan berjalan pada OpenGL 3.x karena Linear Rendering.
5. FisikaMudah untuk menarik FPS ke 10. Ternyata objek statis bahkan berinteraksi dan berpartisipasi dalam perhitungan. Saya keliru mengira bahwa objek fisik statis (objek yang memiliki komponen RigidBody) benar-benar pasif sesuai permintaan. Saya tersesat oleh tutorial lama yang mengatakan bahwa di mana pun ada collider harus ada RigidBody. Sekarang semua objek statis saya adalah Static + BoxCollider. Di mana saya memerlukan fisika, misalnya tiang lampu yang dapat dirobohkan, saya pikir untuk memotong komponen RigidBody jika perlu.
Lapisan adalah garis hidup untuk optimasi. Nonaktifkan interaksi yang tidak perlu menggunakan lapisan. Saat menyusun kembali, gunakan layer mask. Mengapa kita perlu salah perhitungan ekstra? Ingat bahwa jika objek Anda memiliki kisi-kisi collider yang kompleks dan Anda menembaknya dengan sinar, lebih baik membuat collider induk sederhana untuk "menangkap" sinar. Semakin kompleks collider, semakin salah perhitungan.
6. Pemusnahan oklusi + LodDengan adegan besar, pemusnahan oklusi sangat diperlukan. Untuk menonaktifkan objek (pohon, tiang, dll.) Pada jarak yang sangat jauh, saya menggunakan Lod.

7. Objek KolamSemua implementasi siap pakai dari kumpulan objek yang saya temukan menggunakan instantiate. Mereka juga menghapus dan membuat objek. Saya takut instantiate dalam semua manifestasinya. Operasi lambat, yang membekukan game, dengan objek yang kurang lebih besar. Saya memutuskan untuk menyusuri jalan yang sederhana dan cepat - seluruh kumpulan saya ada dalam bentuk objek game fisik yang baru saja saya matikan dan hidupkan jika perlu. Ini mengenai RAM, tetapi lebih baik. RAM untuk perangkat modern dari 1GB, game ini menghabiskan 300-500 MB.
Kumpulan sederhana untuk mengelola bot tempur:
public List<Enemy> enemyPool = new List<Enemy>(); private void Start() {
Basis data
Saya menggunakan sqlite sebagai basis data - dengan mudah dan cepat. Data disajikan dalam bentuk tabel, Anda dapat membuat kueri yang kompleks. Di kelas untuk bekerja dengan database, 800 baris ketika. Saya tidak bisa membayangkan bagaimana tampilannya dalam XML / JSON.
Masalah dan rencana untuk masa depan
Untuk pindah dari kota ke "kamar" saya memilih penerapan "teleport". Pemain mendekati pintu, ruang adegan dimuat dan pemain diteleportasi. Ini menyelamatkan Anda dari keharusan menjaga kamar di kota. Jika Anda menerapkan kamar di kota, yaitu +15 kamar dengan pengisian, maka konsumsi memori akan meningkat hingga minimal 1GB. Saya tidak suka implementasi ini, itu tidak realistis dan memberlakukan banyak pembatasan. Unity baru-baru ini menunjukkan demo
Megacity -nya, sangat mengesankan. Saya ingin secara bertahap mentransfer game untuk mengawal dan menggunakan teknologi dari Megacity untuk memuat bangunan dan tempat. Ini adalah pengalaman yang menarik dan menarik, saya pikir itu akan berubah menjadi kota yang benar-benar bersemangat. Mengapa saya tidak menggunakan
adegan pemuatan async ? Sederhana, tidak berfungsi, tidak ada adegan pemuatan async di luar kotak pada versi 2018.3. Awalnya, saya berharap adegan pemuatan async ketika merencanakan sebuah kota, tetapi ternyata, pada adegan besar itu membekukan game seperti adegan pemuatan biasa. Ini dikonfirmasi di forum Unity, Anda bisa berkeliling, tetapi kruk diperlukan.
Beberapa statistik:
Tekstur: 304 / 374,3 MB
Jerat: 295 / 304.0 MB
Bahan: 101 / 148.0 KB (kemungkinan perbedaan di sini)
Klip Animasi: 24 / 2.8 MB
AudioClips: 22 / 30,3 MB
Aset: 21761
GameObjects in Scene: 29450
Total Objek dalam Adegan: 111645
Jumlah Obyek Total: 133406
Alokasi GC per Frame: 70 / 2.0 KBTotal 4800 baris kode C #.
Seseorang mengatakan kepada saya bahwa permainan seperti itu dapat dilakukan dalam seminggu. Mungkin saya tidak produktif, mungkin orang ini berbakat, tetapi bagi saya sendiri saya mengerti satu hal - sulit untuk membangun permainan seperti itu sendirian. Saya ingin membuat sesuatu yang menarik dengan latar belakang "jari" santai, bagi saya tampaknya saya mendekati impian saya.
Anda dapat menjalankan tes beta terbuka dan merasakannya di sini:
play.google.com/store/apps/details?id=com.ag.mafiaProject01 (jika perakitan tiba-tiba tidak berfungsi, Anda perlu sedikit menyukainya, pembaruan tiba setiap malam). Saya harap ini tidak dianggap sebagai tautan iklan, karena beta dan unduhan ini tidak akan memberi saya peringkat dan dividen. Selain itu, saya tidak berpikir bahwa habr adalah target audiens permainan saya.
Tangkapan layar:

