Project Hospital adalah permainan tentang mengelola bangunan rumah sakit dengan semua aspek standar genre: adegan dinamis yang dibuat oleh pemain, banyak karakter dan objek aktif, yang digunakan oleh sistem UI. Untuk membuat gim bekerja pada peralatan yang berbeda, kami harus melakukan banyak upaya, dan ini adalah contoh yang bagus dari "kematian akibat ribuan pemotongan" yang terkenal - banyak langkah kecil yang memecahkan banyak masalah yang sangat spesifik dan banyak waktu yang dihabiskan untuk membuat profil.
Tingkat kinerja: apa yang ingin kita capai
Pada tahap awal pengembangan, kami memutuskan parameter utama: ukuran maksimum adegan, tingkat kinerja, dan persyaratan sistem.
Kami menetapkan sendiri tugas memberikan dukungan untuk setidaknya seratus karakter aktif dan animasi penuh pada satu layar, total tiga ratus karakter aktif, peta ubin berukuran sekitar 100x100 dan hingga empat lantai di gedung.
Kami sangat yakin bahwa permainan harus bekerja dalam 1080p dengan frame rate yang layak bahkan pada kartu grafis terintegrasi, dan tujuan itu sendiri tidak begitu sulit untuk dicapai: faktor pembatas utama adalah CPU, terutama dengan peningkatan volume rumah sakit. Kartu grafis terintegrasi modern mulai mengalami masalah hanya pada resolusi sekitar 2560 x 1440.
Untuk menyederhanakan dukungan mod, sebagian besar data dibuat terbuka, yaitu, kami harus mengorbankan kinerja yang dicapai dengan mengemas file, tetapi ini tidak memiliki dampak yang sangat kuat, kecuali untuk waktu pengunduhan yang sedikit lebih lama.
Grafik
Project Hospital adalah gim 2D isometrik "klasik", sehingga Anda dapat memahami bahwa semuanya ditarik kembali ke depan - di Unity ini dilakukan dengan menetapkan nilai yang sesuai di sepanjang sumbu Z (atau jarak ke kamera) untuk masing-masing objek grafis. Jika memungkinkan, objek yang tidak berinteraksi satu sama lain diatur dalam lapisan, misalnya, lantai tidak tergantung pada objek dan karakter.
Semua geometri dalam adegan yang dirender secara isometrik secara dinamis dibuat dalam C #, jadi salah satu dari dua aspek terpenting untuk kinerja grafis adalah frekuensi pembangunan kembali geometri. Aspek kedua adalah jumlah panggilan undian.
Buat panggilan
Jumlah objek individu yang ditarik dalam satu bingkai, terlepas dari kesederhanaannya, adalah batasan utama, terutama pada peralatan yang buruk (di samping itu, mesin Unity sendiri menambah konsumsi sumber daya yang berlebihan). Solusi yang jelas adalah mengelompokkan (batch) objek grafik sebanyak mungkin menjadi satu panggilan draw. Jadi, Anda bisa mendapatkan beberapa hasil yang cukup menarik, misalnya, mengelompokkan objek yang berada pada jarak yang sama dari kamera sehingga sisa gambar ditampilkan dengan benar di belakang atau di depan mereka.
Berikut adalah beberapa angka: pada ubin 96 x 96, Anda secara teoritis dapat menempatkan 9216 objek, yang akan membutuhkan 9216 panggilan draw. Setelah batching, jumlah ini turun menjadi 192.
Namun, dalam kehidupan nyata, semuanya sedikit lebih rumit, karena Anda hanya dapat mengelompokkan objek dengan tekstur yang sama, yang membuat hasilnya sedikit kurang optimal, tetapi sistem masih bekerja dengan cukup baik.
Sebagian besar batch dilakukan secara manual untuk memiliki kendali atas hasil. Selain itu, sebagai upaya terakhir, kami juga menggunakan batching dinamis Unity, tetapi ini adalah pedang bermata dua - ini sebenarnya membantu mengurangi jumlah panggilan draw, tetapi mengarah ke sumber daya yang tidak perlu di setiap frame, dan dalam beberapa kasus itu bisa tidak dapat diprediksi. Sebagai contoh, dua sprite yang dilapiskan pada jarak yang sama dari kamera dalam bingkai yang berbeda dapat dirender dalam urutan yang berbeda, yang menyebabkan kerlipan yang tidak muncul saat batching manual.
Bertingkat
Pemain dapat membangun bangunan dengan beberapa lantai, dan ini meningkatkan kompleksitas, tetapi, yang mengejutkan, membantu kinerja. Hanya karakter di lantai aktif dan di jalan yang harus dirender dan dianimasikan, dan semua yang ada di lantai lain rumah sakit dapat disembunyikan.
Shader
Rumah Sakit Proyek menggunakan shader yang ditulis sendiri yang relatif sederhana dengan trik-trik kecil, seperti pertukaran warna. Misalkan karakter shader dapat menggantikan hingga lima warna (tergantung pada kondisi dalam kode shader), dan karenanya cukup mahal, tetapi ini sepertinya tidak menimbulkan masalah, karena karakter jarang menempati banyak ruang layar. Shader membenarkan upaya yang dimasukkan ke dalamnya, karena kemampuan untuk menggunakan warna pakaian dalam jumlah tak terbatas dapat sangat meningkatkan variabilitas karakter dan lingkungan.
Selain itu, kami cukup cepat belajar untuk menghindari menentukan parameter shader dan alih-alih menggunakan warna titik jika memungkinkan.
Kualitas tekstur
Fakta menarik - di Rumah Sakit Proyek kami tidak menggunakan kompresi tekstur: grafik dilakukan dalam gaya vektor, dan pada beberapa tekstur kompresi terlihat sangat buruk.
Untuk menghemat memori CPU pada sistem dengan kurang dari 1 GB, kami secara otomatis mengurangi ukuran tekstur dalam game menjadi setengah resolusi (kecuali untuk tekstur antarmuka pengguna) - ini dapat dipahami dengan melihat parameter “kualitas tekstur: rendah” pada opsi. Tekstur UI mempertahankan resolusi aslinya.
Optimalkan Kinerja CPU - Multithreading
Meskipun logika skrip Unity pada dasarnya adalah single-threaded, kami selalu memiliki kemampuan untuk menjalankan banyak utas secara langsung di C #. Mungkin pendekatan ini tidak cocok untuk logika game, tetapi seringkali ada tugas-tugas penting yang dapat dilakukan dalam utas terpisah dengan mengatur sistem tugas. Dalam kasus kami, utas digunakan untuk dua fungsi:
1. Tugas menemukan jalur, terutama pada peta besar dengan pengaturan yang membingungkan, dapat memakan waktu hingga ratusan milidetik, jadi ini adalah kandidat utama untuk transfer dari aliran utama. Tugas paralel memperhitungkan jumlah utas perangkat keras sebuah mesin.
2. Kartu pencahayaan juga dapat diperbarui dalam aliran terpisah, tetapi hanya satu lantai pada satu waktu - ini bukan sistem yang kritis, dan lampu otomatis di kamar padam dengan kecepatan yang cukup sehingga pembaruan langka sudah cukup.
Animasi
Hampir di awal pengembangan, kami memutuskan untuk menggunakan sistem animasi kerangka dua dimensi. Setelah mempelajari berbagai program animasi modern, kami akhirnya memutuskan untuk memodifikasi sistem sederhana yang saya buat beberapa tahun yang lalu (pada dasarnya sebagai proyek hobi), menyesuaikannya dengan kebutuhan Rumah Sakit Proyek - menyerupai Tulang Belakang yang disederhanakan dengan dukungan langsung untuk menciptakan variasi karakter. Seperti Spine, ia menggunakan runtime C #, yang jelas lebih mahal daripada kode asli, jadi selama proses pengembangan kami melakukan beberapa siklus optimisasi. Untungnya, rig kami cukup sederhana, hanya sekitar 20 tulang per karakter.
Fakta yang aneh: peningkatan yang paling berguna dalam mengoptimalkan akses untuk mengubah tulang individu ternyata merupakan transisi dari pencarian peta ke pengindeksan array sederhana.
Selain fakta bahwa karakter tidak dianimasikan di luar kamera, ada trik lain: karakter yang tersembunyi di balik jendela UI utama juga tidak perlu dianimasikan. Sayangnya, di versi final game, kami beralih ke UI yang tembus cahaya, jadi kami tidak bisa menggunakannya.
Caching
Jika memungkinkan, kami mencoba melakukan perhitungan paling mahal hanya dengan perubahan yang memengaruhi nilainya. Contoh terbaik dari ini adalah kamar dan elevator: ketika pemain menempatkan lift atau membangun dinding, kami menjalankan algoritma pengisian yang menandai ubin tempat elevator dan kamar tersedia. Ini mempercepat pencarian selanjutnya untuk jalur dan dapat digunakan untuk menunjukkan kepada pemain kamar mana yang belum tersedia.
Pembaruan yang tersebar dan ditangguhkan
Dalam beberapa kasus, logis untuk melakukan pembaruan tertentu hanya sebagian. Berikut ini beberapa contohnya:
Beberapa pembaruan dapat dilakukan di setiap bingkai hanya untuk sebagian karakter, misalnya, skrip perilaku setengah dari pasien diperbarui hanya dalam bingkai aneh, dan untuk paruh kedua - dalam bingkai genap (meskipun animasi dan gerakan dilakukan dengan lancar).
Dalam kondisi tertentu, terutama ketika karakter dalam mode siaga, tetapi memanggil bagian mahal dari kode (misalnya, karyawan memeriksa apa yang perlu diisi dan mencari peralatan yang tidak dihuni), operasi hanya dilakukan pada interval tertentu, misalnya, sekali per detik.
Salah satu tantangan umum yang paling mahal dan sekaligus adalah memeriksa tes mana yang tersedia untuk setiap pasien. Pada saat yang sama, banyak faktor yang perlu dievaluasi - misalnya, personil departemen mana yang sedang sibuk dan peralatan apa yang dipesan. Selain itu, informasi ini tidak umum untuk semua pasien karena dipengaruhi, misalnya, oleh dokter yang ditugaskan kepada mereka dan kemampuan mereka untuk berbicara. Penting untuk memeriksa lusinan jenis analisis yang tersedia, oleh karena itu, dalam satu bingkai pembaruan dilakukan hanya untuk beberapa orang, dan berlanjut di yang berikutnya.
Kesimpulan
Mengoptimalkan manajer permainan dengan banyak bagian yang berinteraksi telah terbukti menjadi proses yang panjang. Saya secara teratur harus bekerja dengan profiler Unity dan memperbaiki masalah yang paling jelas, ini telah menjadi bagian integral dari proses pengembangan.
Tentu saja, selalu ada ruang untuk perbaikan, tetapi kami cukup senang dengan hasilnya. Gim ini mengatasi tugas kami, dan pemain terus-menerus membuat mod untuk itu, secara signifikan melebihi batas asli pada jumlah karakter.
Perlu juga disebutkan bahwa bahkan dibandingkan dengan beberapa game AAA yang saya kerjakan, di Rumah Sakit Proyek saya bertemu dengan logika game paling kompleks dalam praktik saya, begitu banyak masalah khusus untuk proyek ini. Meskipun demikian, saya tetap merekomendasikan untuk menyisihkan cukup waktu dalam proyek apa pun untuk pengoptimalan sesuai dengan kompleksitas permainan.