Cara Membuat Infinite Runner di iOS: Cocos2D, Automation, dan Lainnya
Diterbitkan: 2022-03-11Mengembangkan game iOS dapat menjadi pengalaman yang memperkaya dalam hal pertumbuhan pribadi dan finansial. Awal tahun ini, saya menggunakan game berbasis Cocos2D, Bee Race, ke App Store. Aspek gimnya sederhana: pelari tak terbatas di mana pemain (dalam hal ini, lebah) mengumpulkan poin dan menghindari rintangan. Lihat di sini untuk demo.
Dalam tutorial ini, saya akan menjelaskan proses di balik pengembangan game untuk iOS, dari Cocos2D hingga penerbitan. Untuk referensi, berikut adalah daftar isi singkat:
- Sprite dan objek fisik
- Pengantar singkat untuk Cocos2D
- Menggunakan Cocos2D dengan storyboard
- Gameplay dan deskripsi proyek (singkat)
- Mengotomatiskan pekerjaan. Gunakan alat. Jadilah keren.
- Penagihan dalam aplikasi
- Gameplay multipemain dengan Game Center
- Ruang untuk perbaikan
- Kesimpulan
Sprite dan objek fisik
Sebelum kita masuk ke detail berpasir, akan sangat membantu untuk memahami perbedaan antara sprite dan objek fisik.
Untuk setiap entitas yang muncul di layar permainan endless runner, representasi grafis dari entitas tersebut disebut sebagai sprite , sedangkan representasi poligonal dari entitas tersebut dalam mesin fisika disebut sebagai objek fisik .
Jadi sprite digambar di layar, didukung oleh objek fisik yang sesuai, yang kemudian ditangani oleh mesin fisika Anda. Pengaturan itu dapat divisualisasikan di sini, di mana sprite ditampilkan di layar, dengan rekan poligonal fisiknya digariskan dalam warna hijau:
Objek fisik tidak terhubung ke sprite masing-masing secara default, yang berarti bahwa Anda sebagai pengembang iOS dapat memilih mesin fisika mana yang akan digunakan dan cara menghubungkan sprite dan badan. Cara yang paling umum adalah dengan mensubklasifikasikan sprite default dan menambahkan badan fisik beton ke dalamnya.
Dengan mengingat hal itu…
Tutorial singkat tentang pengembangan game Cocos2D iOS
Cocos2D-iphone adalah kerangka kerja sumber terbuka untuk iOS yang menggunakan OpenGL untuk akselerasi grafis perangkat keras dan mendukung mesin fisika Chipmunk dan Box2D.
Pertama-tama, mengapa kita membutuhkan kerangka kerja seperti itu? Sebagai permulaan, framework mengimplementasikan komponen pengembangan game yang sering digunakan. Misalnya, Cocos2D dapat memuat sprite (khususnya, lembar sprite (mengapa?)), meluncurkan atau menghentikan mesin fisika, dan menangani waktu dan animasi dengan benar. Dan ia melakukan semua ini dengan kode yang telah ditinjau dan diuji secara ekstensif—mengapa mencurahkan waktu Anda sendiri untuk menulis ulang kode yang kemungkinan lebih rendah?
Namun, mungkin yang paling penting— Pengembangan game Cocos2D menggunakan akselerasi perangkat keras grafis . Tanpa akselerasi seperti itu, game pelari tak terbatas iOS apa pun dengan jumlah sprite sedang akan berjalan dengan kinerja yang sangat buruk. Jika kita mencoba membuat aplikasi yang lebih rumit, maka kita mungkin akan mulai melihat efek “bullet-time” di layar, yaitu, banyak salinan dari setiap sprite saat mencoba untuk menganimasikan.
Terakhir, Cocos2D mengoptimalkan penggunaan memori karena menyimpan sprite. Jadi, setiap sprite yang digandakan membutuhkan memori tambahan minimal, yang jelas berguna untuk game.
Menggunakan Cocos2D dengan storyboard
Setelah semua pujian yang saya berikan pada Cocos2D, mungkin tampak tidak logis untuk menyarankan menggunakan Storyboards. Mengapa tidak memanipulasi objek Anda dengan Cocos2D, dll.? Sejujurnya, untuk jendela statis seringkali lebih nyaman menggunakan Pembuat Antarmuka Xcode dan mekanisme Storyboard-nya.
Pertama, ini memungkinkan saya untuk menyeret dan memposisikan semua elemen grafis saya untuk game endless runner saya dengan mouse saya. Kedua, Storyboard API sangat, sangat berguna. (Dan ya, saya tahu tentang Cocos Builder).
Berikut ini sekilas tentang Storyboard saya:
Pengontrol tampilan utama gim ini hanya berisi adegan Cocos2D dengan beberapa elemen HUD di atasnya:
Perhatikan latar belakang putih: ini adalah adegan Cocos2D, yang akan memuat semua elemen grafis yang diperlukan saat runtime. Tampilan lain (indikator langsung, dandelion, tombol, dll.) adalah semua tampilan Kakao standar, ditambahkan ke layar menggunakan Interface Builder.
Saya tidak akan membahas detailnya—jika Anda tertarik, contohnya dapat ditemukan di GitHub.
Gameplay dan deskripsi proyek (singkat)
(Untuk memberikan lebih banyak motivasi, saya ingin menjelaskan permainan endless runner saya dengan sedikit lebih detail. Jangan ragu untuk melewati bagian ini jika Anda ingin melanjutkan ke diskusi teknis.)
Selama permainan langsung, lebah tidak bergerak, dan ladang itu sendiri sebenarnya bergegas, membawa serta berbagai bahaya (laba-laba dan bunga beracun) dan keuntungan (dandelion dan bijinya).
Cocos2D memiliki objek kamera yang didesain mengikuti karakter; dalam praktiknya, lebih mudah untuk memanipulasi CCLayer yang berisi dunia game.
Kontrolnya sederhana: mengetuk layar akan memindahkan lebah ke atas, dan ketukan lain akan memindahkannya ke bawah.
Lapisan dunia itu sendiri sebenarnya memiliki dua sublapisan. Saat permainan dimulai, sublapisan pertama diisi dari 0 hingga BUF_LEN dan ditampilkan pada awalnya. Sublapisan kedua diisi terlebih dahulu dari BUF_LEN ke 2*BUF_LEN. Ketika lebah mencapai BUF_LEN, sublapisan pertama dibersihkan dan langsung terisi kembali dari 2*BUF_LEN menjadi 3*BUF_LEN, dan sublapisan kedua disajikan. Dengan cara ini, kami berganti-ganti antar lapisan, tidak pernah mempertahankan objek usang, bagian penting untuk menghindari kebocoran memori.
Dalam hal mesin fisika, saya menggunakan Chipmunk karena dua alasan:
- Itu ditulis dalam Objective-C murni.
- Saya telah bekerja dengan Box2D sebelumnya, jadi saya ingin membandingkan keduanya.
Mesin fisika benar-benar hanya digunakan untuk deteksi tabrakan. Kadang-kadang, saya ditanya, "Mengapa Anda tidak menulis deteksi tabrakan Anda sendiri?". Pada kenyataannya, tidak ada banyak akal untuk itu. Mesin fisika dirancang untuk tujuan itu: mereka dapat mendeteksi tabrakan antara benda-benda dengan bentuk yang rumit dan mengoptimalkan proses itu. Misalnya, mesin fisika sering membagi dunia menjadi sel dan melakukan pemeriksaan tabrakan hanya untuk benda di sel yang sama atau berdekatan.
Mengotomatiskan pekerjaan. Gunakan alat. Jadilah keren.
Komponen kunci dari pengembangan game indie infinite runner adalah menghindari tersandung masalah kecil. Waktu adalah sumber daya penting saat mengembangkan aplikasi, dan otomatisasi bisa sangat menghemat waktu.
Namun terkadang, otomatisasi juga bisa menjadi kompromi antara perfeksionisme dan memenuhi tenggat waktu Anda. Dalam hal ini, perfeksionisme bisa menjadi pembunuh Angry Birds.
Misalnya, di game iOS lain yang sedang saya kembangkan, saya membuat kerangka kerja untuk membuat tata letak menggunakan alat khusus (tersedia di GitHub). Kerangka kerja ini memiliki keterbatasan (misalnya, tidak memiliki transisi yang bagus antar adegan), tetapi menggunakannya memungkinkan saya untuk membuat adegan dalam sepersepuluh waktu.
Jadi, meskipun Anda tidak dapat membangun superframework Anda sendiri dengan supertools khusus, Anda masih dapat dan harus mengotomatiskan tugas-tugas kecil ini sebanyak mungkin.
Dalam membangun pelari tak terbatas ini, otomatisasi adalah kuncinya sekali lagi. Misalnya, artis saya akan mengirimi saya grafik resolusi tinggi melalui folder Dropbox khusus. Untuk menghemat waktu, saya menulis beberapa skrip untuk secara otomatis membuat kumpulan file untuk berbagai resolusi target yang diperlukan oleh App Store, menambahkan -hd atau @2x juga (skrip tersebut didasarkan pada ImageMagick).
Dalam hal alat tambahan, menurut saya TexturePacker sangat berguna—ini dapat mengemas sprite ke dalam lembar sprite sehingga aplikasi Anda akan menggunakan lebih sedikit memori dan memuat lebih cepat, karena semua sprite Anda akan dibaca dari satu file. Itu juga dapat mengekspor tekstur di hampir semua format kerangka kerja yang mungkin. (Perhatikan bahwa TexturePacker bukan alat gratis, tapi menurut saya itu sepadan dengan harganya. Anda juga dapat melihat alternatif gratis seperti ShoeBox.)

Kesulitan utama yang terkait dengan fisika permainan adalah membuat poligon yang sesuai untuk setiap sprite. Dengan kata lain, menciptakan representasi poligonal dari lebah atau bunga yang bentuknya tidak jelas. Jangan coba-coba melakukannya dengan tangan—selalu gunakan aplikasi khusus, yang jumlahnya banyak. Beberapa bahkan cukup… eksotis—seperti membuat topeng vektor dengan Inkspace dan kemudian mengimpornya ke dalam game.
Untuk pengembangan game endless runner saya sendiri, saya membuat alat untuk mengotomatisasi proses ini, yang saya sebut Andengine Vertex Helper. Seperti namanya, itu awalnya dirancang untuk kerangka Andengine, meskipun akan bekerja dengan baik dengan sejumlah format hari ini.
Dalam kasus kami, kami perlu menggunakan pola plist:
<real>%.5f</real><real>%.5f</real>
Selanjutnya, kita membuat file plist dengan deskripsi objek:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>jet_ant</key> <dict> <key>vertices</key> <array> <real>-0.18262</real><real>0.08277</real> <real>-0.14786</real><real>-0.22326</real> <real>0.20242</real><real>-0.55282</real> <real>0.47047</real><real>0.41234</real> <real>0.03823</real><real>0.41234</real> </array> </dict> </dict> </plist>
Dan pemuat objek:
- (void)createBodyAtLocation:(CGPoint)location{ float mass = 1.0; body = cpBodyNew(mass, cpMomentForBox(mass, self.sprite.contentSize.width*self.sprite.scale, self.sprite.contentSize.height*self.sprite.scale)); body->p = location; cpSpaceAddBody(space, body); NSString *path =[[NSBundle mainBundle] pathForResource:@"obj _descriptions" ofType:@"plist"]; // <- load plist NSDictionary *objConfigs = [[[NSDictionary alloc] initWithContentsOfFile:path] autorelease]; NSArray *vertices = [[objConfigs objectForKey:namePrefix] objectForKey:@"vertices"]; shape = [ChipmunkUtil polyShapeWithVertArray:vertices withBody:body width:self.sprite.contentSize.width height:self.sprite.contentSize.height]; shape->e = 0.7; shape->u = 1.0; shape->collision_type = OBJ_COLLISION_TYPE; cpSpaceAddShape(space, shape); }
Untuk menguji bagaimana sprite sesuai dengan tubuh fisik mereka, lihat di sini.
Jauh lebih baik, bukan?
Singkatnya, selalu otomatisasi jika memungkinkan. Bahkan skrip sederhana dapat menghemat banyak waktu Anda. Dan yang terpenting, waktu itu bisa digunakan untuk pemrograman alih-alih mengklik mouse. (Untuk motivasi tambahan, inilah token XKCD.)
Penagihan dalam aplikasi
Blowball yang dikumpulkan dalam game bertindak sebagai mata uang dalam aplikasi, memungkinkan pengguna untuk membeli kulit baru untuk lebah mereka. Namun, mata uang ini juga dapat dibeli dengan uang sungguhan. Poin penting yang perlu diperhatikan sehubungan dengan penagihan dalam aplikasi adalah apakah Anda perlu melakukan pemeriksaan sisi server untuk validitas pembelian atau tidak. Karena semua barang yang dapat dibeli pada dasarnya sama dalam hal gameplay (hanya mengubah penampilan lebah), tidak perlu melakukan pemeriksaan server untuk validitas pembelian. Namun, dalam banyak kasus, Anda pasti harus melakukannya.
Untuk lebih lanjut, Ray Wenderlich memiliki tutorial penagihan dalam aplikasi yang sempurna.
Gameplay multipemain dengan Game Center
Dalam game seluler, bersosialisasi lebih dari sekadar menambahkan tombol 'Suka' Facebook atau menyiapkan papan peringkat. Untuk membuat game lebih seru, saya menerapkan versi multiplayer.
Bagaimana cara kerjanya? Pertama, dua pemain terhubung menggunakan pembuatan pertandingan real-time iOS Game Center. Karena para pemain benar-benar memainkan game pelari tak terbatas yang sama, hanya perlu ada satu set objek game. Itu berarti bahwa instance satu pemain perlu menghasilkan objek, dan permainan lainnya akan membacanya. Dengan kata lain, jika perangkat kedua pemain menghasilkan objek game, akan sulit untuk menyinkronkan pengalaman.
Dengan mengingat hal itu, setelah koneksi dibuat, kedua pemain saling mengirim nomor acak. Pemain dengan nomor lebih tinggi bertindak sebagai "server", membuat objek game.
Apakah Anda ingat diskusi tentang generasi dunia yang terbagi? Di mana kami memiliki dua sublapisan, satu dari 0 hingga BUF_LEN dan yang lainnya dari BUF_LEN hingga 2*BUF_LEN? Arsitektur ini tidak digunakan secara kebetulan—diperlukan untuk menyediakan grafik yang halus melalui jaringan yang tertunda. Ketika sebagian objek dihasilkan, itu akan dikemas ke dalam daftar dan dikirim ke pemain lain. Buffer cukup besar untuk membiarkan pemain kedua bermain bahkan dengan penundaan jaringan. Kedua pemain saling mengirim posisi mereka saat ini dengan jangka waktu setengah detik, juga mengirimkan gerakan naik turun mereka dengan segera. Untuk memuluskan pengalaman, posisi dan kecepatan dikoreksi setiap 0,5 detik dengan animasi yang halus, sehingga dalam praktiknya sepertinya pemain lain bergerak atau berakselerasi secara bertahap.
Tentu saja ada lebih banyak pertimbangan yang harus dibuat sehubungan dengan gameplay multiplayer tanpa akhir, tetapi semoga ini memberi Anda pemahaman tentang jenis tantangan yang terlibat.
Ruang untuk perbaikan
Permainan tidak pernah selesai. Memang, ada beberapa area yang ingin saya tingkatkan sendiri, yaitu:
- Masalah kontrol: mengetuk sering kali merupakan gerakan yang tidak intuitif bagi pemain yang lebih suka meluncur.
Lapisan dunia dipindahkan menggunakan tindakan CCMoveBy. Ini baik-baik saja ketika kecepatan lapisan dunia konstan, karena tindakan CCMoveBy didaur ulang dengan CCRepeatForever:
-(void) infiniteMove{ id actionBy = [CCMoveBy actionWithDuration: BUFFER_DURATION position: ccp(-BUFFER_LENGTH, 0)]; id actionCallFunc = [CCCallFunc actionWithTarget:self selector:@selector(requestFillingNextBuffer)]; id actionSequence = [CCSequence actions: actionBy, actionCallFunc, nil]; id repeateForever = [CCRepeatForever actionWithAction:actionSequence]; [self.bufferContainer runAction:repeateForever]; }
Tapi kemudian, saya menambahkan peningkatan kecepatan dunia untuk membuat permainan lebih sulit saat berjalan:
-(void) infiniteMoveWithAccel { float duration = BUFFER_DURATION-BUFFER_ACCEL*self.lastBufferNumber; duration = max(duration, MIN_BUFFER_DURATION); id actionBy = [CCMoveBy actionWithDuration: duration position: ccp(-BUFFER_LENGTH, 0)]; id restartMove = [CCCallFunc actionWithTarget:self selector:@selector(infiniteMoveWithAccel)]; id fillBuffer = [CCCallFunc actionWithTarget:self selector:@selector(requestFillingNextBuffer)]; id actionSequence = [CCSequence actions: actionBy, restartMove, fillBuffer, nil]; [self.bufferContainer runAction:actionSequence]; }
Perubahan ini menyebabkan animasi compang-camping pada setiap tindakan dimulai ulang. Saya mencoba untuk memperbaiki masalah, tidak berhasil. Namun, penguji beta saya tidak melihat perilaku tersebut, jadi saya menunda perbaikannya.
- Di satu sisi, tidak perlu menulis otorisasi saya sendiri untuk multipemain saat menggunakan Game Center atau menjalankan server game saya sendiri. Di sisi lain, membuat bot menjadi tidak mungkin, yang mungkin ingin saya ubah.
Kesimpulan
Membuat game pelari tak terbatas indie Anda sendiri bisa menjadi pengalaman yang luar biasa. Dan begitu Anda mencapai langkah proses penerbitan, itu bisa menjadi perasaan yang luar biasa saat Anda melepaskan kreasi Anda sendiri ke alam liar.
Proses peninjauan dapat berkisar dari beberapa hari hingga beberapa minggu. Untuk lebih lanjut, ada situs bermanfaat di sini yang menggunakan data yang bersumber dari banyak orang untuk memperkirakan waktu peninjauan saat ini.
Selain itu, saya sarankan menggunakan AppAnnie untuk memeriksa berbagai informasi tentang semua aplikasi di App Store, dan mendaftar dengan beberapa layanan analitik seperti Flurry Analytics juga dapat membantu.
Dan jika game ini membuat Anda penasaran, pastikan untuk memeriksa Bee Race di toko.