Sebelumnya, saya menulis tentang pengalaman saya dalam mengembangkan
permainan kata seluler di Android dan iOS, yang sangat populer, dan saya memutuskan untuk mengencangkan mode multipemain ke sana, ketika dua pemain bersaing di antara mereka sendiri, menulis kata-kata pada gilirannya, sebagai putaran terakhir dari siaran televisi Sergey Suponev. jam. "

Butuh waktu satu setengah bulan untuk mempelajari dan mengimplementasikan multiplayer, dalam artikel ini saya akan mencoba untuk menggambarkan konsep tanpa contoh-contoh kode sumber, membuat memeras dari jumlah pekerjaan yang dilakukan.
Sedikit sejarah
Aplikasi ini ditulis dalam C ++ menggunakan Marmalade SDK. Sejak itu, vendor telah berhenti mendukung platform ini, menjual barang-barang tersebut ke Jepang, dan masa depan lingkungan pengembangan ini menjadi sangat kabur.

Muncul pertanyaan tentang apa yang akan dikirimi proyek saat ini untuk dukungan lebih lanjut.
Kenapa tidak cocos2d-x
Cocos2d-x adalah salah satu mesin pengembangan game mobile lintas platform C ++ yang paling umum. Rupanya, karena kode sumbernya gratis dan terbuka. Mesinnya tidak terdokumentasi dengan baik. Deskripsi tersebut mencakup bagian mesin yang sedikit dan sebagian besar material sudah usang.
Berdasarkan hasil periode, saya masih berhasil membuat prototipe aplikasi saya. Tapi kesan itu sangat buruk: rasanya seperti cocos2d-x dipasang di lutut. Tingkat adegan abstraksi, Sprite, Delegasi Aplikasi bagi saya terasa sangat tidak nyaman, dan kebutuhan untuk mencari jawaban atas pertanyaan di forum kelapa semakin membuat Anda berpikir bahwa Anda melakukan sesuatu yang salah. Mungkin tangan saya tumbuh dari tempat yang salah.
Pilihan saya jatuh pada SDL
SDL , seperti Marmalade SDL, bukan mesin, ini platform. Ini menyediakan API tingkat rendah, dari mana saya kemudian membangun tingkat abstraksi yang nyaman bagi saya. Semua ini ditulis dalam C, kode sumbernya terbuka.
Singkatnya, SDL adalah perpustakaan lintas platform gratis untuk bekerja dengan grafik, suara, dan memproses pesan dari sistem operasi. Sangat mudah untuk membuat win32-build dan men-debug logika game di Windows, hanya untuk emulator seluler dan perangkat fisik yang hanya men-debug fungsi OS khusus.
Untungnya atau sayangnya, SDL tidak menyediakan alat untuk tugas sesempit itu seperti mengembangkan multiplayer untuk iOS dan Android, jadi saya harus mengintegrasikan sendiri layanan yang sesuai.
Arsitektur Aplikasi Multithreaded
Logika aplikasi dan semua pekerjaan dengan grafis diimplementasikan di utas utama, yang merupakan siklus pemrosesan pesan dan dimulai pada fungsi utama. Sebut aliran ini SDL Thread. Utas lainnya, pada gilirannya, melempar acara (SDL_PushEvent) untuk diproses ke dalam antrian, dan ia membacanya dari sana menggunakan SDL_WaitEvent dan SDL_PollEvent. Ini adalah salah satu peristiwa sistem yang dilemparkan oleh sistem dan dukungan yang sudah diterapkan di SDL, atau panggilan Callbacks dan Listener-s, yang kami implementasikan sudah melampaui fungsi SDL.

Semua logika game ditulis dalam C ++. Direktori proyek berisi sekumpulan file * .cpp yang dapat dibagi menjadi tiga grup:
- cross-platform - file-file yang termasuk dalam perakitan semua platform (logika game);
- monoplatform, mis. termasuk dalam aplikasi platform apa saja untuk mengimplementasikan fitur-fiturnya.
Dengan demikian, ada tiga direktori terpisah untuk desain setiap platform:
- proj.win32 - proyek VS2017 Edisi Komunitas;
- proj.android - proyek Android menggunakan Gradle;
- proj.ios - proyek Xcode untuk iOS.
Integrasi dengan layanan multi-pemain
Sekarang kita perlu merekatkan lapisan terpisah, yang akan bertanggung jawab untuk fungsi seperti:
- mencari lawan, koneksi ke permainan;
- olahpesan di antara pesaing;
- keluar dari ruang permainan;
- memperbaiki poin pemain di Papan Peringkat.
Kedua platform iOS dan Android mendukung Real-time Multiplayer (RTMP). Dalam kasus Android, kami berintegrasi dengan Google Play Services (GPS), dalam kasus iOS, Game Center. Sebelumnya, Google mendukung integrasi dengan iOS, tetapi tahun ini memutuskan untuk mengabaikannya.
Dalam artikel ini, saya tidak akan menjelaskan tindakan yang perlu Anda lakukan di Google Play Console dan AppStoreConnect untuk mengkonfigurasi multiplayer, saya tidak akan menjelaskan spesifikasi kelas dan metode integrasi - semua ini dijelaskan di situs vendor.
Selanjutnya, saya akan menjelaskan secara singkat perubahan apa yang perlu dilakukan dalam proyek untuk setiap platform.
Android
Bagaimana? Saya belum mengatakan ini?
Android NDK digunakan untuk mengkompilasi kode C ++. Meskipun, jika Anda adalah pengembang Android, Anda sudah tahu.
Petunjuk umum untuk mengintegrasikan Layanan Google Play ke proyek Android dijelaskan di situs untuk pengembang Android. Dalam proyek saya, saya menggunakan dependensi berikut:
implementation 'com.google.android.gms:play-services-games:16.0.0' implementation 'com.google.android.gms:play-services-nearby:16.0.0' implementation 'com.google.android.gms:play-services-auth:16.0.1'
Awalnya, idenya adalah menggunakan
api C ++ , yang datang dalam bentuk pustaka statis terkompilasi tanpa sumber. Karena kenyataan bahwa tidak ada perakitan untuk platform x86_64 dalam daftar perpustakaan, saya memutuskan bahwa orang-orang dari Google tidak benar-benar memantau relevansi SDK ini dan memutuskan untuk
menciptakan sepeda mereka untuk menulis lapisan ini di Jawa, membungkusnya dengan pembungkus
JNI . Dan kemudian, mengapa saya perlu ketergantungan ekstra dalam bentuk libs tanpa kode sumber yang di dalam Jawa masih menarik Java? Selain relevansi kelas Java, Anda juga harus memantau relevansi lib ini.
Sebagai panduan, saya menggunakan contoh yang baik dari
Google Samples . Terima kasih kepada Google untuk ini. Apple, ambil contoh dari Google!
iOS
Untuk berintegrasi dengan Game Center, Anda harus menghubungkan kerangka kerja GameKit. Kami menjelaskan seluruh lapisan integrasi dengan Game Center dalam satu file * .m dan menyediakan antarmuka untuknya melalui file * .h yang terpisah. Karena C ++ adalah bagian dari bahasa objektif-C, tidak akan ada masalah dengan perakitan file * .cpp dan * .m dalam satu proyek.
Selain
dokumentasi resmi, ia dipandu oleh proyek ini:
GameCenterManager . Benar, beberapa hal dari contoh sudah usang, Xcode 10 akan memberi tahu Anda hal ini dan Anda akan mengganti fungsionalitas yang sudah ketinggalan zaman dengan yang baru.
Prinsip bekerja dengan lapisan Multiplayer
Titik masuk tunggal
Setelah mempelajari fitur-fitur bekerja dengan multi-pemain pada kedua platform, saya membuat satu asbstraksi C ++ tunggal untuk aplikasi saya dan pada saat kompilasi, implementasi yang sesuai βcocokβ, tergantung pada platform tertentu. Artinya, aplikasi saya tidak tahu tentang Google Play Services, Game Center, dan fitur-fiturnya. Hanya tahu api C ++ yang disediakan untuknya, di mana misalnya, ada metode seperti:
SignIn()
Cari lawan
Pemain dapat mengundang teman dari daftar kontaknya, atau memulai permainan dengan lawan acak. Pemain yang menerima undangan dapat menerima atau menolaknya. Untuk semua skenario ini, saya menggunakan antarmuka standar dari layanan yang digunakan. Saya ingin mencatat bahwa moncong Google terlihat jauh lebih baik iOS iOS. Mungkin suatu hari tangan saya akan sampai di sana dan saya akan menulis antarmuka saya dengan kartu domino dan wanita muda.
Koneksi ke ruang permainan
Ketika dua pemain terhubung ke ruang permainan virtual, mereka menerima Callback yang sesuai. Sekarang Anda harus memilih siapa yang akan menjadi tuan rumah.
Seleksi tuan rumah
Di antara pemain Anda harus memilih tuan rumah, sehingga ia menentukan kondisi awal permainan.
Pertimbangkan cara-cara yang memungkinkan untuk merutekan pesan antar pemain dalam kasus umum. Harap dicatat bahwa dalam perwujudan kedua, tuan rumah juga diberi peran sebagai router.

Karena saya selalu memiliki hanya dua pemain dalam permainan, ternyata saya memiliki kasus khusus koneksi peer-to-peer. Dan oleh karena itu, hanya definisi dari keadaan awal jatuh pada peran tuan rumah, yaitu, pilihan kata dari mana kata-kata akan disusun.
Jadi, setelah pemain terhubung ke ruang permainan, masing-masing pemain mengetahui daftar pengidentifikasi peserta permainan yang telah dimulai. Kami akan menyebutnya daftar
participantID . participantID adalah pengenal string tertentu dari peserta game, yang ditugaskan oleh layanan. Anda harus memilih yang mana dari mereka yang akan menjadi tuan rumah, bawalah ini ke tuan rumah itu sendiri dan beri tahu yang lain bahwa lawannya dipilih sebagai tuan rumah. Bagaimana cara melakukannya?
Pilihan host Android
Saya tidak menemukan saran untuk memilih host di google dock. Mereka diam, partisan. Tetapi orang-orang baik di
stackoverflow.com melemparkan tautan ke
video tersebut , yang menjelaskan prinsip berikut secara rinci:
- setiap peserta mengurutkan daftar participantID (naik atau turun - tidak masalah, yang utama adalah bahwa setiap orang melakukannya dalam urutan yang sama);
- setiap peserta membandingkan ID partisipan dengan ID peserta pertama dari daftar;
- jika mereka cocok, maka pemain saat ini diberikan hak untuk memilih siapa yang akan menjadi tuan rumah. Dia
melempar koin, menarik acak (), sehingga memilih tuan rumah dari peserta yang ada, dan memberi tahu semua orang yang menjadi tuan rumah.
Seleksi host di iOS
Untuk iOS, ada metode
selectBestHostPlayerWithCompletionHandler , yang sangat menyederhanakan skenario pemilihan host dibandingkan dengan apa yang saya jelaskan untuk Android. Tetapi menilai dari keterlambatan nyata selama panggilan ke metode ini, ia memperkirakan parameter respon dari jaringan, mengukur ping, dan berdasarkan statistik ini memutuskan siapa yang harus menjadi tuan rumah. Ini lebih mungkin untuk arsitektur client-server di atas, di mana tuan rumah bertindak sebagai router. Dalam versi saya dari koneksi peer-to-peer pribadi, ini tidak masuk akal, dan untuk menghemat waktu, saya menggunakan prinsip yang mirip dengan apa yang saya lakukan untuk Android.
Perpesanan antar pemain
Apa itu pesan? Pesan adalah array byte.
- di java, ini adalah tipe:
byte[]
- dalam objektif-C, ini adalah:
NSData *
- di C ++, saya memetakan semua hal di atas ke
std::vector<Uint8>
Ada 2 jenis pengiriman pesan:
- Andal - pengiriman terjamin melalui antrian. Digunakan untuk mengirimkan pesan penting.
- Tidak dapat diandalkan - pengiriman tidak dijamin. Pesan bekas yang keberhasilan pengirimannya dapat diabaikan.
Tidak dapat diandalkan biasanya disampaikan lebih cepat daripada Handal. Anda dapat membaca lebih lanjut di situs web vendor:
Bagaimana kita akan menggunakan array ini? Sangat sederhana:
- pada byte pertama kita akan menulis jenis pesan.
- jika pesan memiliki parameter, maka kita akan memasukkannya dalam byte berikut. Untuk setiap jenis pesan yang telah ditambahkan. parameter, kami menerapkan fungsi serialisasi dan deserialization kami.
- di akhir pesan untuk memeriksa integritas kami akan menempatkan sebuah checksum.
Jadi, kami mendefinisikan
enum dengan jenis pesan yang akan ditukar pemain di antara mereka sendiri selama pertandingan:
- Saya dipilih oleh tuan rumah. Saya menyampaikan keadaan awal. Sekarang giliranku (parameter: nomor versi protokol pengiriman pesan, kata sumber);
- Anda dipilih oleh tuan rumah. Saya berharap dapat mendengar dari Anda;
- Saya membuka (memanggil) kata itu. Sekarang giliran Anda (parameter: kata bernama);
- Saya menyerah. Anda telah menang;
- Saya tidak bisa mengucapkan sepatah kata pun selama beraktivitas. Anda telah menang;
- Saya setuju untuk membalas dendam;
- Saya keluar dari game;
- Kesalahan saat menguraikan pesan. Terputus;
- Versi protokol pengiriman pesan Anda kedaluwarsa. Periksa pembaruan aplikasi. Terputus;
- Versi protokol pengiriman pesan saya kedaluwarsa. Perlu memeriksa pembaruan. Terputus;
- Ping (pesan sistem);
Ketika aplikasi menerima pesan masuk dari lawan, Callback yang sesuai dipanggil, yang pada gilirannya meneruskannya ke Thread SDL utama untuk diproses.
Pemantauan koneksi
Layanan game (layanan Google, layanan Apple) memiliki fungsi pendengar yang dalam satu atau lain bentuk dirancang untuk memberi tahu kami tentang pemutusan sambungan dari lawan. Tapi, saya perhatikan bahwa jika salah satu pemain terputus dari Internet, maka yang kedua tidak segera mengenali bahwa yang pertama telah terputus dan tidak ada yang bisa bermain. Callback tidak dipanggil dalam kasus seperti itu, atau dipanggil setelah waktu yang agak lama. Sehingga dalam hal ini pemain kedua tidak menunggu kanker bersiul di gunung, saya harus melakukan pemantauan sendiri koneksi, bekerja pada prinsip:
- Setiap pemain mengirim pesan ping ke lawan setiap detik;
- Setiap pemain memeriksa: jika tidak ada pesan dari lawan selama lebih dari 5 detik, maka koneksi terputus, kami keluar dari permainan.
Hasil
Sebagai hasil dari pekerjaan yang dilakukan, saya mendapat permainan yang saya mainkan sendiri dengan teman dan keluarga saya. Saya bermain di iOS dan Android.
Benar, ada nuansa pada iOS - untuk beberapa alasan, kacamata tidak diperbaiki di Papan, tentang yang saat ini saya korespondensi dengan dukungan Apple.
Saya harap artikel ini bermanfaat bagi anggota tim saya dan mereka yang tertarik mengembangkan aplikasi mobile. Terima kasih atas perhatian anda