Plugin Isometrik untuk Unity3D


Ini adalah cerita tentang bagaimana menulis sebuah plugin untuk Unity Asset Store , selesaikan pemecahan masalah isometrik yang terkenal di dalam game, dan dapatkan sedikit uang dari itu, dan juga untuk memahami bagaimana editor Unity yang dapat dikembangkan. Gambar, kode, grafik dan pemikiran di dalamnya.


Prolog


Jadi, itu suatu malam ketika saya tahu saya tidak punya banyak hal untuk dilakukan. Tahun yang akan datang tidak benar-benar menjanjikan dalam kehidupan profesional saya (tidak seperti yang pribadi, tetapi itu adalah cerita yang sama sekali berbeda). Lagi pula, saya mendapat ide untuk menulis sesuatu yang menyenangkan untuk masa lalu, itu akan sangat pribadi, sesuatu sendiri, tetapi masih memiliki sedikit keuntungan komersial (saya hanya suka perasaan hangat ketika proyek Anda menarik untuk orang lain, kecuali untuk atasan Anda). Dan semua ini berjalan seiring dengan fakta bahwa saya telah lama menunggu untuk memeriksa kemungkinan ekstensi editor Unity dan untuk melihat apakah ada gunanya dalam platform untuk menjual ekstensi mesin sendiri.


Saya mencurahkan satu hari untuk mempelajari Asset Store: model, skrip, integrasi dengan berbagai layanan. Dan pertama-tama sepertinya semuanya telah ditulis dan diintegrasikan, bahkan memiliki sejumlah opsi dengan tingkat kualitas dan detail yang berbeda, seperti halnya harga dan dukungan. Jadi segera saya mempersempitnya menjadi:


  • hanya kode (setelah semua, saya seorang programmer)
  • Hanya 2D (karena saya suka 2D dan mereka baru saja membuat dukungan out-of-the-box yang layak untuk itu di Unity)

Dan kemudian saya ingat berapa banyak kaktus yang saya makan dan berapa banyak tikus yang mati ketika kami membuat permainan isometrik sebelumnya. Anda tidak akan percaya berapa banyak waktu yang telah kami habiskan untuk mencari solusi yang layak dan berapa banyak salinan yang kami pecahkan dalam upaya untuk memilah isometri ini dan menariknya. Jadi, berjuang untuk tetap diam, saya mencari dengan kata kunci yang berbeda dan tidak terlalu banyak kata kunci dan tidak dapat menemukan apa pun kecuali tumpukan besar seni isometrik, sampai akhirnya saya memutuskan untuk membuat plugin isometrik dari awal.


Menetapkan tujuan


Yang pertama saya butuhkan adalah untuk menjelaskan secara singkat masalah apa yang seharusnya dipecahkan oleh plugin ini dan apa yang akan dilakukan oleh pembuat game isometrik itu. Jadi, masalah isometri adalah sebagai berikut:


  • menyortir objek dengan keterpencilan untuk menggambarnya dengan benar
  • ekstensi untuk pembuatan, penentuan posisi, dan perpindahan objek isometrik di editor

Jadi, dengan tujuan utama untuk versi pertama yang dirumuskan, saya menetapkan batas waktu 2-3 hari untuk versi konsep pertama. Jadi, tidak bisa ditunda, Anda tahu, karena antusiasme adalah hal yang rapuh dan jika Anda tidak memiliki sesuatu yang siap di hari-hari pertama, ada kemungkinan besar Anda merusaknya. Dan liburan Tahun Baru tidak begitu lama kelihatannya, bahkan di Rusia, dan saya ingin merilis versi pertama dalam waktu sepuluh hari.


Menyortir


Singkatnya, isometry adalah upaya yang dilakukan oleh sprite 2D agar terlihat seperti model 3D. Itu, tentu saja, menghasilkan puluhan masalah. Yang utama adalah bahwa sprite harus diurutkan dalam urutan di mana mereka harus ditarik untuk menghindari masalah dengan saling tumpang tindih.



Pada tangkapan layar, Anda dapat melihat bagaimana sprite hijau yang digambar pertama (2,1), dan kemudian yang biru (1,1)


Tangkapan layar menunjukkan penyortiran yang salah saat sprite biru digambar pertama

Dalam kasus sederhana ini penyortiran tidak akan menjadi masalah, dan akan ada opsi, misalnya:


  • mengurutkan berdasarkan posisi Y di layar, yaitu * (isoX + isoY) 0.5 + isoZ **
  • menggambar dari sel kisi isometrik jarak jauh dari kiri ke kanan, dari atas ke bawah [(3,3), (2,3), (3,2), (1,3), (2,2), (3, 1), ...]
  • dan sejumlah cara lain yang menarik dan tidak terlalu menarik

Mereka semua cukup bagus, cepat dan berfungsi, tetapi hanya dalam kasus objek atau kolom bersel tunggal yang diperluas ke arah isoZ :) Bagaimanapun, saya tertarik pada solusi yang lebih umum yang akan bekerja untuk objek yang diperluas dalam arah satu koordinat, atau bahkan "pagar" yang sama sekali tidak memiliki lebar, tetapi diperluas ke arah yang sama dengan ketinggian yang diperlukan.



Tangkapan layar menunjukkan cara yang tepat untuk menyortir objek yang diperluas 3x1 dan 1x3 dengan "pagar" berukuran 3x0 dan 0x3

Dan di situlah masalah kita dimulai dan menempatkan kita di tempat di mana kita harus memutuskan jalan ke depan:


  • membagi objek "bersel banyak" menjadi yang "bersel tunggal", yaitu memotongnya secara vertikal dan kemudian mengurutkan garis-garis yang muncul


  • pikirkan metode penyortiran baru, lebih rumit dan menarik



Saya memilih opsi kedua, tidak memiliki keinginan khusus untuk masuk ke pemrosesan rumit dari setiap objek, menjadi pemotongan (bahkan otomatis), dan pendekatan khusus untuk logika. Sebagai catatan, mereka menggunakan cara pertama dalam beberapa game terkenal seperti Fallout 1 dan Fallout 2 . Anda benar-benar dapat melihat strip itu jika Anda masuk ke data permainan.


Jadi, opsi kedua tidak menyiratkan kriteria penyortiran. Ini berarti bahwa tidak ada nilai pra-perhitungan dengan mana Anda bisa mengurutkan objek. Jika Anda tidak mempercayai saya (dan saya kira banyak orang yang tidak pernah bekerja dengan isometry tidak), ambil selembar kertas dan gambar benda-benda kecil berukuran 2x8 dan, misalnya, 2x2 . Jika Anda entah bagaimana berhasil mengetahui nilai untuk perhitungan kedalaman dan pengurutannya - cukup tambahkan objek 8x2 dan cobalah untuk mengurutkannya di posisi yang berbeda relatif satu sama lain.


Jadi, tidak ada nilai seperti itu, tetapi kita masih bisa menggunakan dependensi di antara mereka (secara kasar, yang tumpang tindih mana) untuk penyortiran topologis . Kita dapat menghitung dependensi objek dengan menggunakan proyeksi koordinat isometrik pada sumbu isometrik.



Cuplikan layar memperlihatkan kubus biru memiliki ketergantungan pada kubus merah


Cuplikan layar memperlihatkan kubus hijau yang memiliki ketergantungan pada kubus biru

Sebuah pseudocode untuk penentuan ketergantungan untuk dua sumbu (bekerja sama dengan sumbu Z):


bool IsIsoObjectsDepends(IsoObject obj_a, IsoObject obj_b) { var obj_a_max_size = obj_a.position + obj_a.size; return obj_b.position.x < obj_a_max_size.x && obj_b.position.y < obj_a_max_size.y; } 

Dengan pendekatan seperti itu kami membangun dependensi antara semua objek, melewati di antara mereka secara rekursif dan menandai tampilan Z koordinat. Metode ini cukup universal, dan yang paling penting, ini berhasil. Anda dapat membaca deskripsi terperinci tentang algoritma ini, misalnya, di sini atau di sini . Mereka juga menggunakan pendekatan semacam ini di pustaka isometrik flash populer ( as3isolib ).


Dan semuanya sangat bagus kecuali bahwa kompleksitas waktu dari pendekatan ini adalah O (N ^ 2) karena kita harus membandingkan setiap objek dengan yang lainnya untuk membuat dependensi. Saya telah meninggalkan pengoptimalan untuk versi yang lebih baru, setelah menambahkan hanya pemilahan ulang yang malas sehingga tidak ada yang akan diurutkan sampai sesuatu bergerak. Jadi kita akan berbicara tentang pengoptimalan sedikit nanti.


Ekstensi editor


Mulai sekarang, saya memiliki tujuan-tujuan berikut:


  • pemilahan objek harus bekerja di editor (tidak hanya dalam permainan)
  • pasti ada jenis lain dari Gizmos-Arrow (panah untuk memindahkan objek)
  • secara opsional, akan ada keberpihakan dengan ubin ketika objek dipindahkan
  • ukuran ubin akan diterapkan dan diatur di inspektur dunia isometrik secara otomatis
  • Objek AABB digambar sesuai dengan ukuran isometriknya
  • output dari koordinat isometrik di inspektur objek, dengan mengubah mana kita akan mengubah posisi objek di dunia game

Dan semua tujuan ini telah tercapai. Unity benar-benar memungkinkan untuk memperluas editornya secara signifikan. Anda dapat menambahkan tab baru, jendela, tombol, bidang baru di pemeriksa objek. Jika mau, Anda bahkan dapat membuat inspektur khusus untuk komponen dengan tipe persis yang Anda butuhkan. Anda juga dapat menampilkan informasi tambahan di jendela editor (dalam kasus saya, pada objek AABB), dan mengganti gizmos objek bergerak standar juga. Masalah pengurutan di dalam editor diselesaikan melalui tag sihir ExecuteInEditMode ini , yang memungkinkan untuk menjalankan komponen objek dalam mode editor, yaitu melakukannya dengan cara yang sama seperti dalam permainan.


Semua ini dilakukan, tentu saja, bukan tanpa kesulitan dan trik dari semua jenis, tetapi tidak ada masalah tunggal yang telah saya habiskan lebih dari beberapa jam (Google, forum dan komunitas yakin membantu saya untuk menyelesaikan semua masalah muncul yang tidak disebutkan dalam dokumentasi).



Cuplikan layar memperlihatkan gizmos saya untuk objek gerakan dalam dunia isometrik

Lepaskan


Jadi, saya menyiapkan versi pertama, mengambil tangkapan layar. Saya bahkan menggambar ikon dan menulis deskripsi. Sudah waktunya. Jadi, saya menetapkan harga nominal $ 5, unggah plugin di toko dan tunggu sampai disetujui oleh Unity. Saya tidak terlalu memikirkan harganya, karena saya belum benar-benar ingin mendapatkan uang besar. Tujuan saya adalah untuk mengetahui apakah ada permintaan umum dan jika memang demikian, saya ingin memperkirakannya. Saya juga ingin membantu pengembang game isometrik yang entah bagaimana akhirnya benar-benar kehilangan peluang dan tambahan.


Dalam 5 hari yang agak menyakitkan (saya menghabiskan waktu yang hampir bersamaan untuk menulis versi pertama, tetapi saya tahu apa yang saya lakukan, tanpa bertanya-tanya dan berpikir berlebihan, itu memberi saya kecepatan yang lebih tinggi dibandingkan dengan orang-orang yang baru mulai bekerja dengan isometry) Saya mendapat tanggapan dari Unity yang mengatakan bahwa plugin itu disetujui dan saya sudah bisa melihatnya di toko, sama halnya dengan nol (sejauh ini) penjualannya. Itu diperiksa di forum lokal, membangun Google Analytics ke halaman plugin di toko dan mempersiapkan diri untuk menunggu rumput tumbuh.


Tidak butuh waktu lama sebelum penjualan pertama, seperti umpan balik di forum dan toko muncul. Selama sisa hari 12 Januari, salinan plugin saya telah terjual, yang saya anggap sebagai tanda ketertarikan publik dan memutuskan untuk melanjutkan.


Optimasi


Jadi, saya tidak senang dengan dua hal:


  • Kompleksitas waktu penyortiran - O (N ^ 2)
  • Masalah dengan pengumpulan sampah dan kinerja umum

Algoritma


Memiliki 100 objek dan O (N ^ 2) Saya memiliki 10.000 iterasi untuk membuat hanya untuk menemukan dependensi, dan juga saya harus melewati semuanya dan menandai layar Z untuk menyortir. Seharusnya ada beberapa solusi untuk itu. Jadi, saya mencoba sejumlah besar pilihan, tidak bisa tidur memikirkan masalah ini. Ngomong-ngomong, saya tidak akan memberi tahu Anda tentang semua metode yang telah saya coba, tetapi saya akan menjelaskan metode yang saya temukan sejauh ini.


Hal pertama yang pertama, tentu saja, kita hanya mengurutkan objek yang terlihat. Maksudnya adalah kita harus selalu tahu apa yang ada dalam foto kita. Jika ada objek baru, kita harus menambahkannya dalam proses penyortiran, dan jika salah satu yang lama hilang - abaikan saja. Sekarang, Unity tidak memungkinkan untuk menentukan Bounding Box objek bersama-sama dengan anak-anaknya di pohon adegan. Lewati anak-anak (setiap saat, karena mereka dapat ditambahkan dan dihapus) tidak akan bekerja - terlalu lambat. Kami juga tidak dapat menggunakan OnBecameVisible dan acara lainnya karena ini hanya berfungsi untuk objek induk. Tetapi kita bisa mendapatkan semua komponen Renderer dari objek yang diperlukan dan anak-anaknya. Tentu saja, ini kedengarannya bukan pilihan terbaik kami, tetapi saya tidak dapat menemukan cara lain, sama universal dan dapat diterima oleh kinerja.


 List<Renderer> _tmpRenderers = new List<Renderer>(); bool IsIsoObjectVisible(IsoObject iso_object) { iso_object.GetComponentsInChildren<Renderer>(_tmpRenderers); for ( var i = 0; i < _tmpRenderers.Count; ++i ) { if ( _tmpRenderers[i].isVisible ) { return true; } } return false; } 

Ada sedikit trik menggunakan fungsi GetComponentsInChildren yang memungkinkan untuk mendapatkan komponen tanpa alokasi di buffer yang diperlukan, tidak seperti yang lain yang mengembalikan array komponen baru


Kedua, saya masih harus melakukan sesuatu tentang O (N ^ 2) . Saya telah mencoba sejumlah teknik pemisahan ruang sebelum saya berhenti di kisi dua dimensi sederhana di ruang pajangan tempat saya memproyeksikan objek isometrik saya. Setiap sektor tersebut berisi daftar objek isometrik yang melintasinya. Jadi, idenya sederhana: jika proyeksi objek tidak disilangkan, maka tidak ada gunanya membangun ketergantungan antara objek sama sekali. Kemudian kami melewati semua objek yang terlihat dan membangun dependensi hanya di sektor-sektor yang diperlukan, sehingga menurunkan kompleksitas waktu dari algoritma dan meningkatkan kinerja. Kami menghitung ukuran setiap sektor sebagai rata-rata antara ukuran semua objek. Saya menemukan hasilnya lebih dari memuaskan.


Kinerja umum


Tentu saja, saya bisa menulis artikel terpisah tentang ini ... Oke, mari kita coba untuk mempersingkat ini. Pertama, kami menguangkan komponen (kami menggunakan GetComponent untuk menemukannya, yang tidak cepat). Saya merekomendasikan semua orang untuk berjaga-jaga ketika bekerja dengan apa pun yang berkaitan dengan Pembaruan . Anda harus selalu ingat bahwa hal itu terjadi untuk setiap frame, jadi Anda harus benar-benar berhati-hati. Juga, ingat tentang semua fitur menarik seperti operator kustom == . Ada banyak hal yang perlu diingat, tetapi pada akhirnya Anda harus tahu tentang masing-masing dari mereka di profiler bawaan. Itu membuatnya lebih mudah untuk menghafal dan menggunakannya :)


Anda juga harus benar-benar memahami rasa sakit pengumpul sampah. Perlu kinerja yang lebih tinggi? Kemudian lupakan segala sesuatu yang dapat mengalokasikan memori, yang dalam C # (terutama di compiler Mono lama) dapat dilakukan dengan apa saja, mulai dari foreach (!) Untuk lambda yang muncul, apalagi LINQ yang sekarang dilarang untuk Anda bahkan dalam kasus yang paling sederhana. Pada akhirnya, bukannya C # dengan gula sintaksisnya, Anda mendapatkan kemiripan C dengan kapasitas yang konyol.


Di sini saya akan memberikan beberapa tautan pada topik yang mungkin bermanfaat bagi Anda:
Bagian1 , Bagian2 , Bagian3 .


Hasil


Saya belum pernah kenal orang yang menggunakan teknik optimasi ini, jadi saya sangat senang melihat hasilnya. Dan jika dalam versi pertama butuh 50 objek bergerak untuk game untuk mengubahnya menjadi tampilan slide, sekarang bekerja cukup baik bahkan ketika ada 800 objek dalam sebuah bingkai: semuanya berputar dengan kecepatan tinggi dan mengurutkan ulang hanya untuk 3-6 ms yang sangat baik untuk jumlah objek dalam isometry ini. Selain itu, setelah inisialisasi hampir tidak mengalokasikan memori untuk sebuah bingkai :)


Peluang lebih lanjut


Setelah saya membaca umpan balik dan saran, ada beberapa fitur yang saya tambahkan di versi sebelumnya.


Campuran 2D / 3D


Menggabungkan 2D dan 3D dalam game isometrik adalah peluang yang menarik yang memungkinkan untuk meminimalkan menggambar berbagai gerakan dan opsi rotasi (misalnya, model 3D karakter animasi). Ini tidak terlalu sulit untuk dilakukan, tetapi membutuhkan integrasi dalam sistem penyortiran. Yang Anda butuhkan adalah mendapatkan Bounding Box model dengan semua anak-anaknya, dan kemudian memindahkan model di sepanjang layar Z dengan lebar kotak.


 Bounds IsoObject3DBounds(IsoObject iso_object) { var bounds = new Bounds(); iso_object.GetComponentsInChildren<Renderer>(_tmpRenderers); if ( _tmpRenderers.Count > 0 ) { bounds = _tmpRenderers[0].bounds; for ( var i = 1; i < _tmpRenderers.Count; ++i ) { bounds.Encapsulate(_tmpRenderers[i].bounds); } } return bounds; } 

itulah contoh bagaimana Anda bisa mendapatkan Bounding Box model dengan semua anak-anaknya



dan seperti itulah rasanya ketika itu dilakukan

Pengaturan isometrik khusus


Itu relatif sederhana. Saya diminta untuk memungkinkan untuk mengatur sudut isometrik, aspek rasio, ketinggian ubin. Setelah menderita beberapa rasa sakit yang terlibat dalam matematika, Anda mendapatkan sesuatu seperti ini:




Fisika


Dan di sini semakin menarik. Karena isometri mensimulasikan dunia 3D, fisika juga seharusnya tiga dimensi, dengan ketinggian dan segalanya. Saya datang dengan trik yang menarik ini. Saya meniru semua komponen fisika, seperti Rigidbody , Collider dan sebagainya, untuk dunia isometrik. Menurut uraian dan pengaturan ini saya membuat salinan dunia fisik tiga dimensi yang tak terlihat menggunakan mesin itu sendiri dan PhysX bawaan . Setelah itu saya mengambil data simulasi dihitung dan mendapatkan bacl dalam komponen duplikat untuk dunia isometrik. Lalu saya melakukan hal yang sama untuk mensimulasikan peristiwa menabrak dan memicu.



Demo fisik toolset GIF

Epilog dan kesimpulan


Setelah saya menerapkan semua saran dari forum, saya memutuskan untuk menaikkan harga hingga 40 dolar, sehingga tidak akan terlihat seperti plugin murah lain dengan lima baris kode :) Saya akan sangat senang untuk menjawab pertanyaan dan dengarkan saran Anda. Karena ini pertama kalinya saya menulis sesuatu tentang Habr, saya menyambut semua jenis kritik, terima kasih! Dan sekarang, sesuatu yang saya simpan untuk yang terakhir, statistik penjualan bulan ini:


Bulan5 $40 $
Januari120
Februari220
Maret170
April90
Mei90
Juni90
Juli74
Agustus04
September05

Tautan halaman Unity Asset Store: Perangkat Isometrik 2.5D

Source: https://habr.com/ru/post/id436372/


All Articles