Itu naif. Super: kode dan arsitektur gim sederhana

Kita hidup di dunia yang kompleks dan, tampaknya, sudah mulai melupakan hal-hal sederhana. Misalnya, tentang pisau cukur Occam, prinsipnya adalah: "Apa yang dapat dilakukan atas dasar jumlah yang lebih kecil tidak boleh dilakukan atas dasar pisau yang lebih besar." Pada artikel ini saya akan berbicara tentang solusi sederhana dan bukan yang paling dapat diandalkan yang dapat digunakan dalam pengembangan game sederhana.



Pada musim gugur DotNext di Moskow, kami memutuskan untuk mengembangkan game. Itu adalah variasi IT dari Alkimia populer. Pemain harus mengumpulkan 128 konsep yang terkait dengan IT, pizza dan Dodo dari 4 elemen yang tersedia. Dan kami perlu mewujudkan ini dari sebuah ide hingga game yang berfungsi dalam waktu lebih dari sebulan.

Dalam artikel sebelumnya , saya menulis tentang komponen desain pekerjaan: perencanaan, pemalsuan dan emosi. Dan artikel ini tentang bagian teknis. Bahkan akan ada beberapa kode!

Penafian: Pendekatan, kode, dan arsitektur yang saya tulis di bawah ini tidak rumit, asli, atau dapat diandalkan. Sebaliknya, mereka sangat sederhana, terkadang naif dan tidak dirancang untuk beban berat berdasarkan desain. Namun, jika Anda belum pernah membuat game atau aplikasi yang menggunakan logika di server, maka artikel ini dapat berfungsi sebagai dorongan awal.

Kode Klien


Singkatnya tentang arsitektur proyek: kami memiliki klien seluler untuk Android dan iOS di Unity dan server backend di ASP.NET dengan CosmosDB sebagai penyimpanan.

Klien di Unity hanya mewakili interaksi dengan UI. Pemain mengklik elemen, mereka bergerak di sekitar layar dengan cara yang tetap. Ketika elemen baru dibuat, sebuah jendela muncul dengan deskripsinya.


Proses permainan

Proses ini dapat dijelaskan dengan mesin keadaan yang cukup sederhana. Hal utama dalam mesin negara ini adalah menunggu animasi transisi ke kondisi berikutnya, memblokir UI untuk pemain.



Saya menggunakan perpustakaan UnitRx yang sangat keren untuk menulis kode Unity dengan gaya yang sepenuhnya tidak sinkron. Pada awalnya saya mencoba menggunakan Tugas asli saya, tetapi mereka berperilaku tidak stabil pada build untuk iOS. Tapi UniRx.Async bekerja seperti jam.

Setiap tindakan yang membutuhkan animasi dipanggil melalui kelas AnimationRunner:

public static class AnimationRunner
   {
       private const int MinimumIntervalMs = 20;
     public static async UniTask Run(Action<float> action, float durationInSeconds)
       {
           float t = 0;
           var delta = MinimumIntervalMs / durationInSeconds / 1000;
           while (t <= 1)
           {
               action(t);
               t += delta;
               await UniTask.Delay(TimeSpan.FromMilliseconds(MinimumIntervalMs));
           }
       }
   }

Ini sebenarnya menggantikan coroutine klasik dengan UnitTask. Selain itu, setiap panggilan yang harus memblokir UI dipanggil melalui metode HandleUiOperationkelas global GameManager:

public async UniTask HandleUiOperation(UniTask uiOperation)
       {
           _inputLocked = true;
           await uiOperation;
           _inputLocked = false;
       }

Dengan demikian, di semua kontrol, nilai InputLocked pertama kali diperiksa, dan hanya jika salah, kontrol bereaksi.

Ini membuatnya cukup mudah untuk mengimplementasikan mesin negara yang digambarkan di atas, termasuk panggilan jaringan dan I / O, menggunakan pendekatan async / menunggu dengan panggilan bersarang, seperti pada boneka Rusia.

Fitur penting kedua dari klien adalah bahwa semua penyedia yang menerima data pada elemen dibuat dalam bentuk antarmuka. Setelah konferensi, ketika kami mematikan backend kami, itu memungkinkan secara harfiah dalam satu malam untuk menulis ulang kode klien sehingga permainan menjadi benar-benar offline. Versi ini dapat diunduh sekarang dari Google Play .

Interaksi klien-kembali


Sekarang mari kita bicara tentang keputusan apa yang kita buat saat mengembangkan arsitektur client-server.



Gambar disimpan di klien, dan server bertanggung jawab untuk semua logika. Pada saat startup, server membaca file csv dengan id dan deskripsi semua elemen dan menyimpannya dalam memorinya. Setelah itu, dia siap pergi.

Metode API adalah minimum yang diperlukan - hanya lima. Mereka menerapkan semua logika permainan. Semuanya cukup sederhana, tetapi saya akan memberi tahu Anda tentang beberapa poin menarik.

Otentikasi dan Elemen Pemula


Kami meninggalkan semua sistem otentikasi yang rumit dan umumnya kata sandi apa pun. Ketika seorang pemain memasukkan nama di layar mulai dan menekan tombol "Mulai", token acak unik (ID) dibuat di klien. Namun, itu tidak terpasang ke perangkat. Nama pemain beserta token dikirim ke server. Semua permintaan lain dari klien ke belakang berisi token ini.



Kerugian yang jelas dari solusi ini adalah:

  1. Jika pengguna menghancurkan aplikasi dan menginstalnya kembali, ia akan dianggap sebagai pemain baru, dan semua kemajuannya akan hilang.
  2. Anda tidak dapat melanjutkan permainan di perangkat lain.

Ini adalah asumsi yang disengaja, karena kami mengerti bahwa orang akan bermain di confe hanya dari satu perangkat. Dan mereka tidak akan punya waktu untuk beralih ke perangkat lain.

Jadi, dalam skenario yang direncanakan, klien memanggil metode server AddNewUserhanya sekali.

Saat memuat layar permainan GetBaseElements, metode ini juga dipanggil sekali , yang mengembalikan id, nama sprite dan deskripsi untuk empat elemen dasar. Klien menemukan sprite yang diperlukan dalam sumber dayanya, menciptakan objek elemen, menulisnya sendiri secara lokal dan melukis di layar.

Setelah peluncuran berulang kali, klien tidak lagi terdaftar di server dan tidak meminta elemen awal, tetapi mengambilnya dari penyimpanan lokal. Alhasil, layar game langsung terbuka.

Gabungkan Elemen


Ketika seorang pemain mencoba menghubungkan dua elemen, suatu metode disebut MergeElementsyang mengembalikan informasi tentang elemen baru atau melaporkan bahwa kedua elemen ini tidak dikumpulkan. Jika pemain telah mengumpulkan elemen baru, informasi tentang ini dicatat dalam database.



Kami menerapkan solusi yang jelas: untuk mengurangi beban di server, setelah pemain mencoba menambahkan dua elemen, hasilnya di-cache pada klien (dalam memori dan di csv). Jika pemain mencoba untuk menumpuk kembali item, cache diperiksa terlebih dahulu. Dan hanya jika hasilnya tidak ada, permintaan dikirim ke server.

Dengan demikian, kita tahu bahwa setiap pemain dapat membuat sejumlah interaksi dengan punggung, dan jumlah entri dalam database tidak melebihi jumlah elemen yang tersedia (yang kami miliki 128). Kami dapat menghindari masalah dari banyak aplikasi konferensi lainnya, yang setelah masuknya peserta yang besar dan simultan sering kali didukung.

Tabel skor tinggi


Peserta bermain "Alkimia" kami tidak hanya seperti itu, tetapi demi hadiah. Oleh karena itu, kami membutuhkan tabel catatan, yang kami tampilkan di layar di stand kami, dan juga di jendela terpisah di game kami.



Untuk membentuk tabel catatan, dua metode terakhir digunakan GetCurrentLadderdan GetUser.ada juga nuansa penasaran. Jika seorang pemain berada di 20 hasil teratas, namanya disorot dalam tabel. Masalahnya adalah bahwa informasi kedua metode ini tidak berhubungan langsung.

Metode GetCurrentLaddermengakses koleksi Stats, mendapat 20 hasil dan melakukannya dengan cepat. Metode GetUsermengakses koleksi.Usersoleh UserId dan melakukannya terlalu cepat. Penggabungan hasil sudah di sisi klien. Hanya saja kami tidak ingin menyinari UserId dalam hasilnya, jadi mereka tidak ada di sana. Perbandingan dilakukan sesuai dengan nama pemain dan jumlah poin yang dicetak. Dalam kasus ribuan pemain, tabrakan pasti akan terjadi. Tapi kami mengandalkan fakta bahwa di antara semua pemain tidak mungkin ada pemain dengan nama dan poin yang sama. Dalam kasus kami, pendekatan ini sepenuhnya dibenarkan.

Permainan telah berakhir


Tugas awal kami adalah membuat game untuk konferensi dua hari dalam sebulan. Semua keputusan yang kami wujudkan dalam arsitektur dan kode telah sepenuhnya membenarkan diri mereka sendiri. Server tidak berbaring, semua permintaan diproses dengan benar, dan para pemain tidak mengeluh tentang bug dalam aplikasi.

Pada Moscow DotNext berikutnya, kami kemungkinan besar akan membangkitkan permainan lain, karena sekarang ini telah menjadi tradisi baik kami ( CMAN-2018 , IT-Alchemy-2019 ). Tulis di komentar yang mana pembunuh waktu Anda siap untuk bertukar laporan hardcore dari bintang pembangunan. :)
Untuk naif dan tertarik yang sama, kami telah memposting kode klien alkimia TI di domain publik .

Dan lihat ke saluran telegram , di mana saya menulis semua tentang perkembangan, kehidupan, matematika dan filsafat.

All Articles