Tutorial Api Komprehensif (atau Cara Membuat Permainan Dengan Flutter)

Pengenalan

Hai semua! Saya Luan, dan selamat datang ke tutorial Flame komprehensif pertama ini.

Flame adalah enjin permainan Flutter minimalis yang menyediakan beberapa modul untuk membuat permainan berasaskan kanvas.

Dalam tutorial ini, kami akan membuat permainan yang sangat mudah, di mana kotak akan jatuh dan matlamatnya adalah untuk memusnahkan mereka sebelum mereka melanda bahagian bawah skrin.

Ini adalah bagaimana permainan akan kelihatan seperti

Anda boleh menyemak sendiri permainan untuk melihat apa yang kami terpasang untuk memasang APK ini, atau memasangnya dari Gedung Play.

Ini akan membolehkan kita menampung semua ciri yang disediakan oleh rangka kerja, dan menunjukkan bagaimana untuk melaksanakan operasi yang paling asas: rendering, sprite, audio, teks, animasi, dan banyak lagi.

Mengapa kami memilih permainan ini walaupun? Selain daripada menjadi sangat mudah tetapi lengkap untuk penerokaan rangka kerja, satu faktor yang baik ialah kita mempunyai sumber untuk melakukannya!

Untuk permainan ini, kami akan menggunakan sumber-sumber berikut: satu kepingan peti, satu letupan letupan (dengan animasi), bunyi letupan, bunyi 'terlepas' (apabila kotak tidak terkena), muzik latar belakang dan juga fon cantik untuk memberikan markah.

Sukar untuk mencari sumber yang boleh didapati secara komersil di Internet, tetapi kami mendapati segala-galanya (kecuali font) di laman web yang hebat ini. Mereka benar-benar hebat, sangat disyorkan.

Buat masa ini, lembaran helai tidak disokong, jadi, pertama, kita perlu menukar semua sumber kepada format yang diperuntukkan. Juga, kita perlu menukar semua audio untuk MP3 (OGG juga disokong; WAV tidak). Itu semua dilakukan, anda boleh memuat turun satu bundle dengan semua yang anda perlukan sumber bijak di sini.

Juga bernilai menyebutkan, semua yang diterangkan di sini adalah komited, sebagai versi lengkap sepenuhnya berfungsi, di GitHub. Anda sentiasa boleh melihat apabila ragu-ragu. Juga, contoh itu dibuat mengikut langkah-langkah yang tepat ini, dan mengambil kerap membuat komik. Sepanjang tutorial, saya akan menghubungkan komit khusus yang memajukan repositori ke tahap yang dipersoalkan. Ini akan membolehkan anda membuat pemeriksaan dan melayari kod lama untuk melihat apa-apa yang tidak jelas.

Satu perkara terakhir; anda boleh menyemak dokumentasi penuh untuk Flame di sini. Jika anda mempunyai sebarang pertanyaan, cadangan, pepijat, berasa bebas untuk membuka isu atau hubungi saya.

Selain itu, anda juga perlu memasang Flutter dan Dart. Jika ia sesuai dengan anda, anda juga boleh mempunyai IntelliJ IDEA, yang merupakan tempat yang sangat baik untuk menulis kod anda. Untuk memasang barangan ini, anda boleh menyemak sejumlah besar tutorial di luar sana, seperti ini.

Asas-asas

Jadi, buat masa ini, saya akan menganggap anda mempunyai segala-galanya sedia. Jadi, jalankan yang berikut dan buka!

Perkara pertama yang perlu anda lakukan ialah menambah kebergantungan api. Pergi ke fail pubspec.yaml anda dan pastikan senarai kekunci utama bergantung kepada:

Di sini kita menggunakan versi terkini, 0.5.0, tetapi anda juga boleh memilih yang baru jika tersedia.

Sekarang, fail utama anda mempunyai banyak perkara: kaedah 'utama' yang perlu disimpan; dan panggilan seterusnya untuk kaedah runApp. Kaedah ini mengambil Widget dan komponen Flutter lain yang digunakan untuk membuat skrin aplikasinya. Oleh kerana kita membuat permainan, kita akan menggambar segala-galanya di kanvas, dan tidak akan menggunakan komponen ini; jadi hapus semua itu.

Kaedah utama kami sekarang kosong, dan kami akan menambah dua perkara; pertama, beberapa konfigurasi:

Import flame.dart memberikan akses kepada kelas statik Flame, yang hanya pemegang untuk beberapa kelas lain yang berguna. Kami akan menggunakan lebih banyak lagi nanti. Buat masa ini, kami memanggil dua kaedah dalam kelas Flame ini.

Yang terakhir adalah penjelasan, ia melumpuhkan beberapa pembalakan dari pemidang audioplayers. Sekarang, tidak lama lagi kami akan menambah audio kepada permainan, dan jika ia tidak berfungsi, itu adalah di mana anda perlu memberi komen untuk menyelesaikan masalah. Tetapi kita akan sampai ke sana akhirnya.

Baris pertama adalah lebih kompleks. Pada asasnya, beberapa fungsi penting dari Flutter tidak hadir kerana kami tidak menggunakan kaedah runApp. Panggilan Aktifkan ini membolehkan sedikit penyelesaian untuk mendapatkan apa yang penting untuk setiap aplikasi, tanpa memerlukan menggunakan Widget.

Akhirnya, kita perlu memulakan permainan kami. Untuk melakukan itu, kami akan menambah satu kelas lagi ke senarai import, kelas Permainan. Kelas ini menyediakan abstraksi yang diperlukan untuk membuat sebarang permainan: gelung permainan. Ia mestilah subclassed supaya anda dikehendaki untuk melaksanakan asas mana-mana permainan: kaedah kemas kini, yang dipanggil setiap kali mudah dan mengambil jumlah masa yang berlalu sejak kemas kini terakhir, dan kaedah render, yang perlu tahu bagaimana untuk menarik keadaan semasa permainan. Kerja dalam gelung dibiarkan untuk kelas Permainan untuk menyelesaikan (anda boleh lihat, tentu saja, itu sangat mudah), dan anda hanya perlu memanggil permulaan, dengan baik, mulakan.

Pemeriksaan: 599f809

Buat masa ini, render tidak melakukan apa-apa, maka, jika anda boot itu, ia harus dijalankan, tetapi memberikan anda skrin hitam. Manis! Jadi kami mendapat aplikasi berfungsi tanpa sebarang widget dan mana-mana, dan kanvas kosong untuk mula melukis aplikasi kami.

Rendering Shapes

Dan bagaimanakah lukisan selesai? Mari kita buat persegi panjang mudah untuk melihatnya dalam tindakan. Tambah yang berikut ke kaedah penyampaian anda:

Di sini, seperti yang anda lihat, kami menentukan segi empat tepat, berdasarkan kedudukan skrin. Imej berikut menunjukkan bagaimana aturan berorientasikan. Pada dasarnya, asal berada di sudut kiri atas, dan paksi meningkat ke kanan dan ke bawah.

Juga, ambil perhatian bahawa kebanyakan kaedah lukisan mengambil Cat. Cat bukan hanya satu warna, tetapi boleh menjadi Degradè atau beberapa tekstur lain. Biasanya, anda akan mahu warna pepejal atau langsung ke Sprite. Jadi kami hanya menetapkan warna di dalam cat kepada contoh Warna.

Warna mewakili satu warna ARGB; anda menciptanya dengan integer yang anda boleh tulis di hex untuk bacaan yang lebih mudah; ia dalam format A (alfa, ketelusan, biasanya 0xFF), dan kemudian dua digit untuk R, G dan B dalam perintah itu.

Terdapat juga koleksi warna yang dinamakan; ia adalah di dalam pakej bahan walaupun. Hanya berhati-hati untuk mengimport hanya modul Warna, kerana tidak sengaja menggunakan sesuatu yang lain dari pakej material.

Jadi, hebat, sekarang kita mempunyai persegi!

Checkpoint: 4eff3bf

Kita juga tahu bagaimana penguasa bekerja, tetapi kita tidak tahu dimensi skrin! Bagaimanakah kita akan menarik sesuatu di tiga penjuru yang lain tanpa maklumat ini? Jangan takut, kerana Flame mempunyai kaedah untuk mengambil dimensi sebenar skrin (itu kerana terdapat isu yang didokumenkan di sekelilingnya.

Pada asasnya, kaedah async

Perhatikan bahawa kata kunci menunggu, sama seperti dalam JavaScript, hanya boleh digunakan dalam fungsi async, jadi pastikan untuk membuat async utama anda (Flutter tidak peduli).

Pemeriksaan seterusnya akan mengambil dimensi sekali dalam kaedah utama dan menyimpannya di dalam kelas Permainan kami, kerana kami akan memerlukannya berulang kali.

Pemeriksaan: a1f9df3

Rendering Sprites

Akhir sekali, kita tahu bagaimana untuk menarik apa-apa bentuk, di mana sahaja di skrin. Tetapi kita mahu sprite! Pemeriksaan seterusnya menambahkan beberapa aset yang akan kita gunakan dalam folder aset yang sesuai:

Checkpoint: 92ebfd9

Dan yang seterusnya lakukan satu perkara penting yang anda tidak boleh lupa: tambah segala-galanya untuk fail pubsepc.yaml anda. Apabila kod anda sedang dibina, Dart hanya akan membekalkan sumber yang anda tentukan di sana.

Checkpoint cf5975f

Akhirnya, kita sudah bersedia untuk menarik perhatian kita. Cara asas Flame membolehkan anda melakukan itu adalah untuk mendedahkan kaedah Flame.images.load ('path dari dalam folder imej') yang mengembalikan janji untuk imej yang dimuatkan, yang kemudiannya boleh ditarik dengan kaedah kanvas.drawImage.

Walau bagaimanapun, dalam hal menarik peti, sangat mudah dilakukan, kerana kita boleh menggunakan kelas SpriteComponent, seperti:

Kelas Komponen abstrak adalah antara muka dengan dua kaedah, membuat dan mengemaskini, seperti permainan kami. Ideanya ialah Permainan boleh terdiri daripada Komponen yang mempunyai kaedah render dan kemas kini yang dipanggil dalam kaedah Permainan. SpriteComponent adalah suatu pelaksanaan yang membuat sprite, diberi nama dan saiznya (persegi atau segi empat tepat), kedudukan (x, y) dan sudut putaran. Ia akan menyusut atau mengembangkan imej supaya sesuai dengan saiz yang dikehendaki.

Dalam kes ini, kita memuat fail 'crate.png', yang mesti berada dalam folder aset / imej, dan mempunyai kelas Crate yang menarik kotak sebanyak 128x128 piksel, dengan sudut putaran 0.

Kami kemudian menambah harta Crate kepada Permainan, memaparkannya di bahagian atas skrin, berpusat secara mendatar, dan menjadikannya dalam gelang permainan kami:

Ini akan menjadikan Crate kita! Awesome! Kod ini agak ringkas dan mudah dibaca juga.

Checkpoint 7603ca4

Mengemaskini Negeri dalam Gelung Permainan

Peti kami semua tetapi berhenti di udara. Kami mahu memindahkannya! Setiap peti akan jatuh dengan kelajuan berterusan, ke bawah. Kita perlu melakukan itu dalam kaedah kemas kini kami; hanya ubah kedudukan Y dari Crate tunggal yang kita ada:

Kaedah ini mengambil masa (dalam saat) yang diambil dari kemas kini terakhir. Biasanya ini akan menjadi sangat kecil (perintah 10 ms). Jadi SPEED adalah tetap dalam unit-unit tersebut; dalam kes kami, SPEED = 100 piksel / saat.

Pemeriksaan: 452dc40

Pengendalian Input

Hurray! Peti itu jatuh dan hilang, tetapi anda tidak boleh berinteraksi dengan mereka. Mari tambahkan kaedah untuk memusnahkan peti yang kami sentuh. Untuk itu, kami akan menggunakan acara tetingkap. Objek tetingkap boleh didapati di setiap projek Flutter di seluruh dunia, dan ia mempunyai beberapa ciri berguna. Kami akan mendaftar pada kaedah utama acara onPointerDataPacket, iaitu, apabila pengguna mengetuk skrin:

Kami hanya mengeluarkan koordinat (x, y) klik dan lulus terus ke Permainan kami; dengan cara itu, Game boleh mengendalikan klik tanpa perlu risau tentang perincian peristiwa.

Untuk menjadikan sesuatu yang lebih menarik, mari juga refactor kelas Permainan untuk mempunyai Senarai Krat, bukannya satu pun. Selepas itu, inilah yang kami mahukan. Kami menggantikan kaedah membuat dan mengemaskini dengan aE untuk lebih Krat, dan kaedah input baharu menjadi:

Checkpoint: 364a6c2

Menjana Sprites Pelbagai

Terdapat satu perkara yang penting untuk dinyatakan di sini, dan ia berkaitan dengan kaedah membuat. Apabila kita membuat Peti, keadaan Kanvas diterjemahkan dan berputar sewenang-wenangnya, untuk membolehkan lukisan. Oleh kerana kita akan menarik beberapa peti, kita perlu menetapkan semula Kanvas antara setiap yang disediakan. Itu dibuat dengan kaedah menyelamatkan, yang menyelamatkan keadaan semasa, dan memulihkan, yang mengembalikan keadaan yang telah disimpan sebelumnya, memadamkannya.

Ini adalah ucapan penting, kerana ia adalah sumber banyak bug aneh. Mungkin kita sepatutnya melakukannya secara automatik dalam setiap penyebabnya? Saya tidak tahu, apa pendapat anda?

Sekarang kita mahu lebih banyak Crates! Bagaimana untuk melakukannya? Nah, kaedah kemas kini boleh menjadi pemasa kami. Jadi kami mahu Crate baru ditambah ke dalam senarai (melahirkan) setiap saat. Oleh itu, kami mencipta pembolehubah lain dalam kelas Permainan, untuk mengumpulkan masa delta (t) dari setiap panggilan kemas kini. Apabila ia mendapat lebih dari 1, ia ditetapkan semula dan peti baru menghasilkan:

Jangan lupa untuk menyimpan kemas kini sebelumnya, jadi peti tidak berhenti jatuh. Juga, kita menukar kelajuan menjadi 250 piksel / saat, untuk menjadikan perkara lebih menarik.

Checkpoint: 3932372

Animasi rendering

Ini harus menjadi GIF, bukan? Kami sedang berusaha untuk membuat persediaan untuk tangkapan skrin dan GIF yang lebih baik untuk tutorial ini!

Sekarang kita tahu asas-asas Sprite Handling dan Rendering. Mari kita bergerak ke langkah seterusnya: Letupan! Apa permainan yang baik tanpa mereka? Letupan itu adalah binatang yang berlainan, kerana ia mempunyai animasi. Animasi dalam Flame dilakukan hanya dengan membuat perkara-perkara yang berbeza untuk dipaparkan mengikut tanda semasa. Dengan cara yang sama kami menambah pemasa buatan tangan untuk menelurkan kotak, kami akan menambah harta benda LifeTime untuk setiap Letupan. Juga, letupan tidak akan mewarisi dari SpriteComponent, kerana yang terakhir hanya boleh mempunyai satu Sprite. Kami akan memperluaskan superclass, PositionComponent, dan melaksanakan rendering dengan Flame.image.load.

Oleh kerana setiap letupan mempunyai banyak bingkai, dan mereka perlu ditarik secara responsif, kita akan pra-beban setiap bingkai sekali, dan menyimpan dalam pembolehubah statik dalam kelas Letupan; seperti itu:

Perhatikan bahawa kami memuatkan setiap bingkai animasi 7 kami, mengikut susunan. Kemudian, dalam kaedah membuat, kami membuat logik mudah untuk menentukan bingkai untuk menarik:

Perhatikan bahawa kita sedang melukis 'dengan tangan', menggunakan drawImageRect, seperti yang dijelaskan sebelumnya. Kod ini sama dengan apa yang dilakukan oleh SpriteComponent di bawah hud. Juga ambil perhatian bahawa, jika imej tidak dalam array, tiada apa yang ditarik - jadi selepas TIME saat (kami menetapkannya kepada 0.75, atau 750 ms), tiada apa yang diberikan.

Itu baik dan baik, tetapi kami tidak mahu terus mencemarkan pelbagai letupan kami dengan ledakan meletup, jadi kami juga menambah kaedah pemusnahan () yang kembali, berdasarkan kehidupanTime, sama ada kita harus memusnahkan objek letupan.

Akhir sekali, kami mengemas kini Permainan kami, menambah Senarai Letupan, memberikannya pada kaedah penyampaian, dan mengemas kini kemudian pada kaedah kemas kini. Mereka perlu dikemas kini untuk meningkatkan kehidupan mereka. Kami juga mengambil masa ini untuk refactor apa yang sebelum ini dalam kaedah Game.update, iaitu, membuat kotak jatuh, berada di dalam kaedah Crate.update, kerana itu adalah tanggungjawab Crate. Sekarang kemas kini permainan hanya mewakilkan kepada orang lain. Akhirnya, dalam kemas kini, kita perlu mengeluarkan dari senarai apa yang telah dimusnahkan. Untuk itu, Senarai menyediakan kaedah yang sangat berguna, alih keluarDi sini:

Kami telah menggunakannya pada kaedah input, untuk mengeluarkan kotak yang disentuh dari array. Terdapat juga di mana kita akan mewujudkan letupan.

Lihatlah pusat pemeriksaan untuk maklumat lanjut.

Checkpoint: d8c30ad

Bermain Audio

Dalam komit seterusnya, kami akhirnya akan memainkan beberapa audio! Untuk melakukannya, anda perlu menambah fail ke folder aset, di dalam aset / audio /. Ia mestilah MP3 atau fail OGG. Kemudian, di mana saja dalam kod anda, jalankan:

Di mana filename.mp3 adalah nama fail di dalamnya. Dalam kes kita, kita akan memainkan letupan.mp3 bunyi apabila kita klik di dalam kotak.

Lebih-lebih lagi, mari mulai memberi tanda baca. Kami menambah pemboleh ubah mata untuk memegang jumlah mata semasa. Ia bermula dengan sifar; kami mendapat 10 mata setiap kotak yang diklik, dan kehilangan 20 apabila kotak melanda tanah.

Kami kini mempunyai kewajipan untuk menangani kotak pelarian. Berdasarkan apa yang kami lakukan pada kelas Letupan, kami menambah kaedah memusnahkan Crate, yang akan kembali sama ada mereka berada di luar skrin. Ini mula menjadi corak! Sekiranya dimusnahkan, kita hapus dari array dan menyesuaikan titik.

Buat masa ini, pemarkahan sedang bekerja, tetapi ia tidak ditunjukkan di mana-mana sahaja; yang akan datang tidak lama lagi.

Audio tidak akan berfungsi di pusat pemeriksaan seterusnya kerana saya terlupa untuk menambah fail dan meletakkannya dalam pubspec.yaml; itu dilakukan dalam komitmen berikut.

Checkpoint: 43a7570

Sekarang kita mahu lebih banyak bunyi! Pemutar audio (minda s) yang menggunakan Flame membolehkan anda memainkan berbunyi banyak sekali sekaligus, kerana anda mungkin sudah menyedari jika anda pergi mengkilap klik, tetapi kini mari menggunakannya untuk kelebihan kami, dengan memainkan bunyi yang tidak dikenali, apabila kotak hits tanah (kaedah pemusnahan Crate), dan muzik latar belakang.

Untuk memainkan muzik latar belakang pada gelung, gunakan kaedah gelung, yang berfungsi seperti sebelumnya:

Dalam komit ini, kami juga menetapkan keadaan memusnahkan Krat, yang kami terlepas dalam komitmen sebelumnya (kerana tidak ada cara untuk mengetahui, sekarang ada bunyi).

Checkpoint: f575150

Teks Rendering

Sekarang kita mempunyai semua audio yang kita mahu (latar belakang, muzik, kesan bunyi, MP3, OGG, gelung, serentak), mari kita masuk ke rendering teks. Kita perlu melihat skor itu, selepas semua. Cara ini dilakukan 'dengan tangan' adalah untuk membuat objek Perenggan dan menggunakan drawParagraph of the Canvas. Ia memerlukan banyak konfigurasi dan ia API agak mengelirukan, tetapi boleh dicapai seperti itu:

Checkpoint: e09221e

Ini ditakrifkan dalam fon lalai, dan anda boleh menggunakan fontFamily property untuk menentukan fon sistem umum yang berbeza; walaupun, mungkin, dalam permainan anda, anda akan mahu menambah yang diperibadikan.

Jadi saya menuju ke 1001fonts.com dan mendapat font percuma Halo percuma komersial ini sebagai TTF. Sekali lagi, jatuhkan fail dalam aset / fon, tetapi kini ia mesti diimport secara berbeza dalam fail pubspec.yaml. Alih-alih menambah satu lagi aset, terdapat tag fon yang berdedikasi, yang dikomitkan secara lalai dengan arahan lengkap tentang bagaimana untuk menambah fon. Oleh itu, beri nama dan buat sesuatu seperti:

Lapisan abstraksi tambahan ini adalah dari Flutter sendiri dan membolehkan anda menambah beberapa fail ke font yang sama (untuk menentukan saiz berani, lebih besar, dan lain-lain). Sekarang, kembali kepada Perenggan kami, kami hanya menambah font propertyFamily: 'Halo' kepada pembangun TextStyle.

Jalankan dan anda akan melihat fon cantik Halo!

Checkpoint: 3155bda

Kaedah yang diterangkan ini akan memberi anda lebih banyak kawalan, jika anda mahu berbilang gaya dalam perenggan yang sama, sebagai contoh. Tetapi jika anda mahu, seperti dalam kes ini, satu perenggan mudah tersusun, hanya gunakan pembantu Flame.util.text untuk menciptanya:

Baris tunggal ini menggantikan yang sebelumnya 4, dan mendedahkan ciri-ciri yang paling penting. Teks (hujah pertama) diperlukan, dan semua yang lain adalah pilihan, dengan lalai yang masuk akal.

Untuk warna, sekali lagi kami menggunakan Pembantu Colors.white, tetapi kami juga boleh menggunakan Warna baharu (0xFFFFFFFF) jika anda mahu warna tertentu.

Checkpoint: 952a9df

Dan di sana anda memilikinya! Permainan lengkap dengan rendering sprite, rendering teks, audio, gelung permainan, acara dan pengurusan negeri.

Siaran

Adakah permainan anda bersedia untuk dibebaskan?

Ikuti beberapa langkah mudah ini dari tutorial Flutter.

Mereka semua cukup mudah, seperti yang anda dapat lihat di pusat pemeriksaan terakhir ini, kecuali bahagian Ikon, yang mungkin menyebabkan sedikit sakit kepala. Cadangan saya adalah untuk membuat versi ikon besar (512 atau 1024 px), dan gunakan laman web Make App Icon untuk menghasilkan zip dengan semua yang anda perlukan (iOS dan Android).

Checkpoint: 2974f29

Apa lagi?

Adakah anda menikmati Flame? Jika anda mempunyai apa-apa cadangan, pepijat, soalan, permintaan ciri, atau apa-apa, sila hubungi saya!

Mahu memperbaiki permainan anda dan ketahui lebih lanjut? Bagaimana dengan menambah Server dengan Firebase dan Google Masuk? Bagaimana pula dengan meletakkan iklan? Bagaimana pula dengan menubuhkan Menu Utama dan pelbagai skrin?

Ada banyak untuk memperbaiki, tentu saja - ini hanyalah permainan contoh. Tetapi ia sepatutnya memberikan idea asas mengenai konsep utama pembangunan permainan dengan Flutter (dengan atau tanpa Flame).

Semoga semua orang menikmatinya!

Originally diterbitkan di GitHub.