Membuat Klon Flappy Bird Arduino: Proyek Game Langkah demi Langkah dengan Layar Sentuh TFT
Dalam Proyek Arduino ini kita akan membuat Game Arduino yang keren, sebenarnya replika dari game Flappy Bird populer untuk smartphone, menggunakan Arduino dan Layar Sentuh TFT. Anda dapat mempelajari cara kerjanya dengan menonton video di bawah ini atau membaca teks tulis di bawahnya.
Ringkasan
Game ini cukup sederhana namun menarik dan membuat ketagihan. Menggunakan layar sentuh kita mengontrol burung dan mencoba menghindari tiang bergerak yang kecepatannya meningkat seiring kemajuan. Juga game dapat menyimpan skor tertinggi Anda bahkan jika Anda mencabut daya.[/column]
Dalam tutorial sebelumnya (Tutorial Arduino TFT) kami mempelajari secara detail cara menggunakan Layar Sentuh TFT dengan Arduino dan kami meninggalkan contoh game untuk dijelaskan dalam tutorial ini. Jadi sekarang, sama seperti dalam tutorial sebelumnya, kami akan langkah demi langkah menjelaskan kode di balik Game Arduino ini.
Kode Sumber
Karena kode cukup panjang dan untuk pemahaman yang lebih baik saya akan memposting kode sumber program dalam bagian-bagian dengan deskripsi untuk setiap bagian. Dan di akhir artikel ini saya akan memposting kode sumber lengkap.
Kita akan menggunakan pustaka UTFT dan URTouch yang dibuat oleh Henning Karlsen. Anda dapat mengunduh pustaka ini dari situs webnya, www.RinkyDinkElectronics.com. Juga kita akan menggunakan pustaka EEPROM untuk menyimpan skor tertinggi di EEPROM. EEPROM adalah memori yang dapat menyimpan data bahkan ketika papan dimatikan.
Setelah kita menyertakan pustaka kita perlu membuat objek UTFT dan URTouch serta mendefinisikan variabel yang diperlukan untuk game. Di bagian setup kita perlu menginisialisasi layar dan sentuh, membaca skor tertinggi dari EEPROM dan memulai game menggunakan fungsi kustom initiateGame().
#include <UTFT.h> #include <URTouch.h> #include <EEPROM.h> //==== Creating Objects UTFT myGLCD(SSD1289,38,39,40,41); //Parameters should be adjusted to your Display/Schield model URTouch myTouch( 6, 5, 4, 3, 2); //==== Defining Fonts extern uint8_t SmallFont[]; extern uint8_t BigFont[]; extern uint8_t SevenSegNumFont[]; extern unsigned int bird01[0x41A]; // Bird Bitmap int x, y; // Variables for the coordinates where the display has been pressed // Floppy Bird int xP = 319; int yP = 100; int yB = 50; int movingRate = 3; int fallRateInt = 0; float fallRate = 0; int score = 0; int lastSpeedUpScore = 0; int highestScore; boolean screenPressed = false; boolean gameStarted = false; void setup() { // Initiate display myGLCD.InitLCD(); myGLCD.clrScr(); myTouch.InitTouch(); myTouch.setPrecision(PREC_MEDIUM); highestScore = EEPROM.read(0); // Read the highest score from the EEPROM initiateGame(); // Initiate the game }Code language: Arduino (arduino)
Jadi dengan fungsi kustom initiateGame() kita akan menggambar status awal game dan berikut adalah bagaimana kita akan melakukannya. Pertama kita perlu membersihkan layar, kemudian menggambar latar belakang biru, menggambar bagian bawah, menambahkan teks dan memanggil fungsi kustom drawBird() untuk menggambar burung. Setelah ini kita perlu perulangan while yang akan mencegah game memulai sampai kita mengetuk layar. Jadi saat kita berada di status ini, jika kita menekan sudut kanan atas kita dapat mengatur ulang skor tertinggi ke nol dan jika kita menekan di mana pun di layar kita akan keluar dari perulangan while dan masuk ke loop utama kode yang akan memulai game.
// ===== initiateGame - Custom Function void initiateGame() { myGLCD.clrScr(); // Blue background myGLCD.setColor(114, 198, 206); myGLCD.fillRect(0,0,319,239); // Ground myGLCD.setColor(221,216,148); myGLCD.fillRect(0, 215, 319, 239); myGLCD.setColor(47,175,68); myGLCD.fillRect(0, 205, 319, 214); // Text myGLCD.setColor(0, 0, 0); myGLCD.setBackColor(221, 216, 148); myGLCD.setFont(BigFont); myGLCD.print("Score:",5,220); myGLCD.setFont(SmallFont); myGLCD.print("HowToMechatronics.com", 140, 220); myGLCD.setColor(0, 0, 0); myGLCD.setBackColor(114, 198, 206); myGLCD.print("Highest Score: ",5,5); myGLCD.printNumI(highestScore, 120, 6); myGLCD.print(">RESET<",255,5); myGLCD.drawLine(0,23,319,23); myGLCD.print("TAP TO START",CENTER,100); drawBird(yB); // Draws the bird // Wait until we tap the sreen while (!gameStarted) { if (myTouch.dataAvailable()) { myTouch.read(); x=myTouch.getX(); y=myTouch.getY(); // Reset higest score if ((x>=250) && (x<=319) &&(y>=0) && (y<=28)) { highestScore = 0; myGLCD.setColor(114, 198, 206); myGLCD.fillRect(120, 0, 150, 22); myGLCD.setColor(0, 0, 0); myGLCD.printNumI(highestScore, 120, 5); } if ((x>=0) && (x<=319) &&(y>=30) && (y<=239)) { gameStarted = true; myGLCD.setColor(114, 198, 206); myGLCD.fillRect(0, 0, 319, 32); } } } // Clears the text "TAP TO START" before the game start myGLCD.setColor(114, 198, 206); myGLCD.fillRect(85, 100, 235, 116); }Code language: Arduino (arduino)
Di bagian loop utama kita memiliki variabel xP yang digunakan untuk menggambar tiang, serta variabel yP. Di awal, variabel xP memiliki nilai 319 sebagai ukuran layar dan variabel yP memiliki nilai 100 yang adalah tinggi tiang pertama. Setiap iterasi nilai variabel xP dikurangi dengan nilai variabel movingRate yang di awal memiliki nilai 3 dan seiring kemajuan game itu meningkat.
// The Main Loop Section void loop() { xP=xP-movingRate; // xP - x coordinate of the pilars; range: 319 - (-51) drawPilars(xP, yP); // Draws the pillars // yB - y coordinate of the bird which depends on value of the fallingRate variable yB+=fallRateInt; fallRate=fallRate+0.4; // Each inetration the fall rate increase so that we can the effect of acceleration/ gravity fallRateInt= int(fallRate); // Checks for collision if(yB>=180 || yB<=0){ // top and bottom gameOver(); } if((xP<=85) && (xP>=5) && (yB<=yP-2)){ // upper pillar gameOver(); } if((xP<=85) && (xP>=5) && (yB>=yP+60)){ // lower pillar gameOver(); } // Draws the bird drawBird(yB); // After the pillar has passed through the screen if (xP<=-51){ xP=319; // Resets xP to 319 yP = rand() % 100+20; // Random number for the pillars height score++; // Increase score by one } //==== Controlling the bird if (myTouch.dataAvailable()&& !screenPressed) { fallRate=-6; // Setting the fallRate negative will make the bird jump screenPressed = true; } // Doesn't allow holding the screen / you must tap it else if ( !myTouch.dataAvailable() && screenPressed){ screenPressed = false; } // After each five points, increases the moving rate of the pillars if ((score - lastSpeedUpScore) == 5) { lastSpeedUpScore = score; movingRate++; } }Code language: Arduino (arduino)
Berikut adalah prinsip kerja game: kita memiliki tiang lebar 50 piksel yang bergerak dari kanan ke kiri dan setiap tiang berikutnya memiliki tinggi acak yang berbeda. Untuk membuatnya bergerak, secara logis, setelah setiap iterasi kita perlu membersihkan layar dan menggambar ulang grafis dengan tiang di posisi baru mereka. Namun, kita tidak dapat melakukannya karena laju segarkan layar yang rendah, yang akan menyebabkan berkedip grafis. Untuk mengaktifkan semua piksel layar membutuhkan waktu lebih lama jadi oleh karena itu kita harus mengimprovisasi dan hanya menggambar ulang hal-hal yang bergerak.
Jadi mari kita lihat bagaimana fungsi kustom drawPilars() akan melakukannya. Fungsi ini mengambil variabel xP dan yP dan menggunakan mereka serta fungsi fillRect() ia akan menggambar tiang. Jadi setiap iterasi ia menggambar tiang di lokasi baru mereka dengan persegi panjang biru tambahan dari sisi kiri dan kanan tiang yang membersihkan tiang yang digambar sebelumnya dan dengan cara itu kita sebenarnya melakukan improvisasi dengan hanya menggambar ulang tiang yang bergerak. Pernyataan if di sini adalah improvisasi tambahan karena karena alasan tertentu fungsi fillRect() tidak berfungsi jika parameter ‘x2’-nya memiliki nilai di luar ukuran layar. Juga, di akhir fungsi kustom ini kita perlu mencetak nilai skor yang tercapai.
// ===== drawPlillars - Custom Function void drawPilars(int x, int y) { if (x>=270){ myGLCD.setColor(0, 200, 20); myGLCD.fillRect(318, 0, x, y-1); myGLCD.setColor(0, 0, 0); myGLCD.drawRect(319, 0, x-1, y); myGLCD.setColor(0, 200, 20); myGLCD.fillRect(318, y+81, x, 203); myGLCD.setColor(0, 0, 0); myGLCD.drawRect(319, y+80, x-1, 204); } else if( x<=268) { // Draws blue rectangle right of the pillar myGLCD.setColor(114, 198, 206); myGLCD.fillRect(x+51, 0, x+60, y); // Draws the pillar myGLCD.setColor(0, 200, 20); myGLCD.fillRect(x+49, 1, x+1, y-1); // Draws the black frame of the pillar myGLCD.setColor(0, 0, 0); myGLCD.drawRect(x+50, 0, x, y); // Draws the blue rectangle left of the pillar myGLCD.setColor(114, 198, 206); myGLCD.fillRect(x-1, 0, x-3, y); // The bottom pillar myGLCD.setColor(114, 198, 206); myGLCD.fillRect(x+51, y+80, x+60, 204); myGLCD.setColor(0, 200, 20); myGLCD.fillRect(x+49, y+81, x+1, 203); myGLCD.setColor(0, 0, 0); myGLCD.drawRect(x+50, y+80, x, 204); myGLCD.setColor(114, 198, 206); myGLCD.fillRect(x-1, y+80, x-3, 204); } // Draws the score myGLCD.setColor(0, 0, 0); myGLCD.setBackColor(221, 216, 148); myGLCD.setFont(BigFont); myGLCD.printNumI(score, 100, 220); }Code language: Arduino (arduino)
Kembali di bagian loop kita memiliki variabel yB yang adalah posisi y burung dan bergantung pada laju jatuh yang setelah setiap iterasi ditingkatkan dan dengan cara itu kita mendapatkan efek percepatan atau gravitasi. Juga, di sini kita memeriksa tabrakan dan menggunakan pernyataan if untuk membatasi burung sehingga jika ia mengenai atas, tanah atau tiang game akan berakhir.
Selanjutnya adalah fungsi kustom drawBird() dan mari kita lihat bagaimana cara kerjanya. Burung sebenarnya adalah foto yang dikonversi menjadi bitmap menggunakan alat ImageConverter565 yang dibuat oleh Henning Karlsen. File “.c” yang dibuat menggunakan alat perlu disertakan di direktori sehingga dimuat saat meluncurkan sketsa. Juga kita harus mendefinisikan bitmap seperti ini dan menggunakan fungsi drawBitmap() kita akan menggambar foto di layar. Burung memiliki koordinat X tetap dan variabel yB sebagai koordinat Y. Sama seperti tiang kita akan membersihkan status sebelumnya burung dengan menggambar dua persegi panjang biru di atas dan di bawah burung.
//====== drawBird() - Custom Function void drawBird(int y) { // Draws the bird - bitmap myGLCD.drawBitmap (50, y, 35, 30, bird01); // Draws blue rectangles above and below the bird in order to clear its previus state myGLCD.setColor(114, 198, 206); myGLCD.fillRoundRect(50,y,85,y-6); myGLCD.fillRoundRect(50,y+30,85,y+36); }Code language: Arduino (arduino)
Kembali di loop kita dapat melihat bahwa setelah tiang telah melewati layar variabel xP akan diatur ulang ke 319, yP akan mendapatkan nilai acak baru dari 20 hingga 100 untuk tinggi tiang dan skor akan bertambah satu. Dengan pernyataan if berikutnya kita mengontrol burung. Jika kita mengetuk layar kita akan mengatur laju jatuh ke negatif yang akan membuat burung melompat dan pernyataan else if tidak akan mengizinkan hal itu jika kita hanya menahan layar. Pernyataan if terakhir adalah untuk kesulitan game dan itu meningkatkan kecepatan gerakan tiang setelah setiap lima poin.
Ok yang tersisa sekarang adalah melihat bagaimana fungsi kustom gameOver() bekerja. Setelah penundaan satu detik ia akan membersihkan layar, mencetak skor dan beberapa teks, jika skor lebih tinggi dari skor tertinggi ia akan menuliskannya ke EEPROM, ia akan mengatur ulang semua variabel ke nilai posisi awal mereka dan di akhir ia akan memanggil fungsi kustom initiateGame() untuk memulai ulang game.
//======== gameOver() - Custom Function void gameOver() { delay(1000); // 1 second // Clears the screen and prints the text myGLCD.clrScr(); myGLCD.setColor(255, 255, 255); myGLCD.setBackColor(0, 0, 0); myGLCD.setFont(BigFont); myGLCD.print("GAME OVER", CENTER, 40); myGLCD.print("Score:", 100, 80); myGLCD.printNumI(score,200, 80); myGLCD.print("Restarting...", CENTER, 120); myGLCD.setFont(SevenSegNumFont); myGLCD.printNumI(2,CENTER, 150); delay(1000); myGLCD.printNumI(1,CENTER, 150); delay(1000); // Writes the highest score in the EEPROM if (score > highestScore) { highestScore = score; EEPROM.write(0,highestScore); } // Resets the variables to start position values xP=319; yB=50; fallRate=0; score = 0; lastSpeedUpScore = 0; movingRate = 3; gameStarted = false; // Restart game initiateGame(); }Code language: Arduino (arduino)
Itu saja dan saya harap penjelasan kode cukup jelas. Jika Anda memiliki pertanyaan, jangan ragu untuk bertanya di bagian komentar di bawah.
Berikut adalah kode lengkap game:
/* Arduino Game Proejct * Program made by Dejan Nedelkovski, * www.HowToMechatronics.com */ /* This program uses the UTFT and URTouch libraries * made by Henning Karlsen. * You can find and download them at: * www.RinkyDinkElectronics.com */ #include <UTFT.h> #include <URTouch.h> #include <EEPROM.h> //==== Creating Objects UTFT myGLCD(SSD1289,38,39,40,41); //Parameters should be adjusted to your Display/Schield model URTouch myTouch( 6, 5, 4, 3, 2); //==== Defining Fonts extern uint8_t SmallFont[]; extern uint8_t BigFont[]; extern uint8_t SevenSegNumFont[]; extern unsigned int bird01[0x41A]; // Bird Bitmap int x, y; // Variables for the coordinates where the display has been pressed // Floppy Bird int xP = 319; int yP = 100; int yB = 50; int movingRate = 3; int fallRateInt = 0; float fallRate = 0; int score = 0; int lastSpeedUpScore = 0; int highestScore; boolean screenPressed = false; boolean gameStarted = false; void setup() { // Initiate display myGLCD.InitLCD(); myGLCD.clrScr(); myTouch.InitTouch(); myTouch.setPrecision(PREC_MEDIUM); highestScore = EEPROM.read(0); // Read the highest score from the EEPROM initiateGame(); // Initiate the game } void loop() { xP=xP-movingRate; // xP - x coordinate of the pilars; range: 319 - (-51) drawPilars(xP, yP); // Draws the pillars // yB - y coordinate of the bird which depends on value of the fallingRate variable yB+=fallRateInt; fallRate=fallRate+0.4; // Each inetration the fall rate increase so that we can the effect of acceleration/ gravity fallRateInt= int(fallRate); // Checks for collision if(yB>=180 || yB<=0){ // top and bottom gameOver(); } if((xP<=85) && (xP>=5) && (yB<=yP-2)){ // upper pillar gameOver(); } if((xP<=85) && (xP>=5) && (yB>=yP+60)){ // lower pillar gameOver(); } // Draws the bird drawBird(yB); // After the pillar has passed through the screen if (xP<=-51){ xP=319; // Resets xP to 319 yP = rand() % 100+20; // Random number for the pillars height score++; // Increase score by one } //==== Controlling the bird if (myTouch.dataAvailable()&& !screenPressed) { fallRate=-6; // Setting the fallRate negative will make the bird jump screenPressed = true; } // Doesn't allow holding the screen / you must tap it else if ( !myTouch.dataAvailable() && screenPressed){ screenPressed = false; } // After each five points, increases the moving rate of the pillars if ((score - lastSpeedUpScore) == 5) { lastSpeedUpScore = score; movingRate++; } } // ===== initiateGame - Custom Function void initiateGame() { myGLCD.clrScr(); // Blue background myGLCD.setColor(114, 198, 206); myGLCD.fillRect(0,0,319,239); // Ground myGLCD.setColor(221,216,148); myGLCD.fillRect(0, 215, 319, 239); myGLCD.setColor(47,175,68); myGLCD.fillRect(0, 205, 319, 214); // Text myGLCD.setColor(0, 0, 0); myGLCD.setBackColor(221, 216, 148); myGLCD.setFont(BigFont); myGLCD.print("Score:",5,220); myGLCD.setFont(SmallFont); myGLCD.print("HowToMechatronics.com", 140, 220); myGLCD.setColor(0, 0, 0); myGLCD.setBackColor(114, 198, 206); myGLCD.print("Highest Score: ",5,5); myGLCD.printNumI(highestScore, 120, 6); myGLCD.print(">RESET<",255,5); myGLCD.drawLine(0,23,319,23); myGLCD.print("TAP TO START",CENTER,100); drawBird(yB); // Draws the bird // Wait until we tap the sreen while (!gameStarted) { if (myTouch.dataAvailable()) { myTouch.read(); x=myTouch.getX(); y=myTouch.getY(); // Reset higest score if ((x>=250) && (x<=319) &&(y>=0) && (y<=28)) { highestScore = 0; myGLCD.setColor(114, 198, 206); myGLCD.fillRect(120, 0, 150, 22); myGLCD.setColor(0, 0, 0); myGLCD.printNumI(highestScore, 120, 5); } if ((x>=0) && (x<=319) &&(y>=30) && (y<=239)) { gameStarted = true; myGLCD.setColor(114, 198, 206); myGLCD.fillRect(0, 0, 319, 32); } } } // Clears the text "TAP TO START" before the game start myGLCD.setColor(114, 198, 206); myGLCD.fillRect(85, 100, 235, 116); } // ===== drawPlillars - Custom Function void drawPilars(int x, int y) { if (x>=270){ myGLCD.setColor(0, 200, 20); myGLCD.fillRect(318, 0, x, y-1); myGLCD.setColor(0, 0, 0); myGLCD.drawRect(319, 0, x-1, y); myGLCD.setColor(0, 200, 20); myGLCD.fillRect(318, y+81, x, 203); myGLCD.setColor(0, 0, 0); myGLCD.drawRect(319, y+80, x-1, 204); } else if( x<=268) { // Draws blue rectangle right of the pillar myGLCD.setColor(114, 198, 206); myGLCD.fillRect(x+51, 0, x+60, y); // Draws the pillar myGLCD.setColor(0, 200, 20); myGLCD.fillRect(x+49, 1, x+1, y-1); // Draws the black frame of the pillar myGLCD.setColor(0, 0, 0); myGLCD.drawRect(x+50, 0, x, y); // Draws the blue rectangle left of the pillar myGLCD.setColor(114, 198, 206); myGLCD.fillRect(x-1, 0, x-3, y); // The bottom pillar myGLCD.setColor(114, 198, 206); myGLCD.fillRect(x+51, y+80, x+60, 204); myGLCD.setColor(0, 200, 20); myGLCD.fillRect(x+49, y+81, x+1, 203); myGLCD.setColor(0, 0, 0); myGLCD.drawRect(x+50, y+80, x, 204); myGLCD.setColor(114, 198, 206); myGLCD.fillRect(x-1, y+80, x-3, 204); } // Draws the score myGLCD.setColor(0, 0, 0); myGLCD.setBackColor(221, 216, 148); myGLCD.setFont(BigFont); myGLCD.printNumI(score, 100, 220); } //====== drawBird() - Custom Function void drawBird(int y) { // Draws the bird - bitmap myGLCD.drawBitmap (50, y, 35, 30, bird01); // Draws blue rectangles above and below the bird in order to clear its previus state myGLCD.setColor(114, 198, 206); myGLCD.fillRoundRect(50,y,85,y-6); myGLCD.fillRoundRect(50,y+30,85,y+36); } //======== gameOver() - Custom Function void gameOver() { delay(3000); // 1 second // Clears the screen and prints the text myGLCD.clrScr(); myGLCD.setColor(255, 255, 255); myGLCD.setBackColor(0, 0, 0); myGLCD.setFont(BigFont); myGLCD.print("GAME OVER", CENTER, 40); myGLCD.print("Score:", 100, 80); myGLCD.printNumI(score,200, 80); myGLCD.print("Restarting...", CENTER, 120); myGLCD.setFont(SevenSegNumFont); myGLCD.printNumI(2,CENTER, 150); delay(1000); myGLCD.printNumI(1,CENTER, 150); delay(1000); // Writes the highest score in the EEPROM if (score > highestScore) { highestScore = score; EEPROM.write(0,highestScore); } // Resets the variables to start position values xP=319; yB=50; fallRate=0; score = 0; lastSpeedUpScore = 0; movingRate = 3; gameStarted = false; // Restart game initiateGame(); }Code language: Arduino (arduino)
Berikut adalah file unduhan dengan sketsa Arduino, gambar burung dan file bitmap untuk burung.