Proyek Game Arduino – Replika Flappy Bird untuk Arduino
Dalam Proyek Arduino ini kami akan membuat Game Arduino yang keren, sebenarnya merupakan replika dari game Flappy Bird yang populer untuk smartphone, menggunakan Arduino dan Layar Sentuh TFT. Anda dapat mempelajari cara kerjanya dengan menonton video berikut atau membaca teks tertulis di bawah ini.
Ringkasan
Gim ini cukup sederhana namun menarik dan membuat ketagihan. Menggunakan layar sentuh kami mengontrol burung dan mencoba menghindari pilar bergerak yang kecepatannya meningkat saat kami maju. Gim ini juga dapat menyimpan skor tertinggi Anda bahkan jika Anda mencabut kabel listrik.[/column]
Dalam tutorial sebelumnya (Tutorial TFT Arduino) kami mempelajari secara detail cara menggunakan Layar Sentuh TFT dengan Arduino dan kami meninggalkan contoh permainan untuk dijelaskan dalam tutorial ini. Jadi sekarang, seperti pada tutorial sebelumnya, kami akan menjelaskan langkah demi langkah kode di balik Game Arduino ini.
Kode sumber
Karena kodenya sedikit lebih panjang dan untuk pemahaman yang lebih baik, saya akan memposting kode sumber program di bagian dengan deskripsi untuk setiap bagian. Dan di akhir artikel ini saya akan memposting source code lengkapnya.
Kami akan menggunakan perpustakaan UTFT dan URTouch yang dibuat oleh Henning Karlsen. Anda dapat mengunduh perpustakaan ini dari situs webnya, www.RinkyDinkElectronics.com. Kami juga akan menggunakan perpustakaan EEPROM untuk menyimpan skor tertinggi di EEPROM. EEPROM adalah memori yang dapat menyimpan data bahkan ketika board dimatikan.
Setelah kami menyertakan perpustakaan, kami perlu membuat objek UTFT dan URTouch serta menentukan variabel yang diperlukan untuk permainan. Di bagian penyiapan kita perlu memulai tampilan dan sentuhan, membaca skor tertinggi dari EEPROM dan memulai permainan menggunakan fungsi kustom inisiasiGame().
#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 inisiasiGame() kita akan menggambar status awal game dan inilah cara kita melakukannya. Pertama kita perlu membersihkan layar, lalu menggambar latar belakang biru, menggambar bagian bawah, menambahkan teks dan memanggil fungsi kustom drawBird() untuk menggambar burung. Setelah ini kita membutuhkan loop sementara yang akan mencegah permainan dimulai sampai kita mengetuk layar. Jadi saat kita dalam keadaan ini, jika kita menekan sudut kanan atas kita dapat mengatur ulang skor tertinggi ke nol dan jika kita menekan di tempat lain di layar kita akan keluar dari loop sementara dan masuk ke loop utama dari kode yang akan memulai permainan.
// ===== 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 pilar, serta variabel yP. Pada awalnya variabel xP bernilai 319 sebagai ukuran layar dan variabel yP bernilai 100 yang merupakan tinggi tiang pertama. Setiap iterasi nilai variabel xP dikurangi dengan nilai variabel movingRate yang awalnya bernilai 3 dan seiring berjalannya permainan, nilai tersebut 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 prinsip kerja permainan:kami memiliki pilar lebar 50 piksel yang bergerak dari kanan ke kiri dan setiap pilar berikutnya memiliki ketinggian acak yang berbeda. Untuk membuatnya bergerak, secara logis, setelah setiap iterasi kita perlu membersihkan layar dan menggambar ulang grafik dengan pilar di posisi barunya. Namun, kami tidak dapat melakukannya karena kecepatan refresh layar yang rendah, yang akan menyebabkan grafik berkedip. Untuk mengaktifkan semua pikselnya, layar memerlukan sedikit lebih banyak waktu sehingga kita harus berimprovisasi dan menggambar ulang hanya hal-hal yang bergerak.
Jadi mari kita lihat bagaimana fungsi kustom drawPilars() akan melakukannya. Dibutuhkan variabel xP dan yP dan menggunakannya dan fungsi fillRect() itu akan menggambar pilar. Jadi setiap iterasi itu menggambar pilar di lokasi barunya dengan persegi panjang biru tambahan dari sisi kiri dan kanannya yang menghapus pilar yang digambar sebelumnya dan dengan cara itu kita benar-benar melakukan improvisasi hanya dengan menggambar ulang pilar yang bergerak. Pernyataan if di sini adalah improvisasi tambahan karena untuk beberapa alasan fungsi fillRect() tidak berfungsi jika parameter 'x2' memiliki nilai di luar ukuran layar. Selain itu, di akhir fungsi kustom ini kita perlu mencetak nilai skor yang dicapai.
// ===== 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 merupakan posisi y burung dan itu tergantung pada tingkat jatuh yang setelah setiap iterasi meningkat dan dengan cara itu kita mendapatkan efek percepatan atau gravitasi. Selain itu, di sini kami memeriksa tabrakan dan menggunakan pernyataan if untuk mengurangi burung sehingga jika mengenai bagian atas, tanah atau pilar, permainan akan berakhir.
Berikutnya adalah fungsi kustom drawBird() dan mari kita lihat cara kerjanya. Burung itu sebenarnya adalah foto yang diubah menjadi bitmap menggunakan alat ImageConverter565 buatan Henning Karlsen. File ".c" yang dibuat menggunakan alat ini perlu dimasukkan ke dalam direktori agar dapat dimuat saat meluncurkan sketsa. Kita juga harus mendefinisikan bitmap seperti ini dan menggunakan fungsi drawBitmap() kita akan menggambar foto di layar. Burung tersebut memiliki koordinat X – tetap dan variabel yB sebagai koordinat Y. Mirip dengan pilar, kita akan menghapus status burung sebelumnya 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 ke loop kita dapat melihat bahwa setelah pilar melewati layar, variabel xP akan direset menjadi 319, yP akan mendapatkan nilai acak baru dari 20 menjadi 100 untuk ketinggian pilar dan skor akan bertambah satu. Dengan pernyataan if berikutnya kita mengontrol burung. Jika kita mengetuk layar kita akan mengatur tingkat jatuh ke negatif apa yang akan membuat burung melompat dan pernyataan lain jika tidak akan memungkinkan terjadi jika kita hanya memegang layar. Pernyataan if terakhir adalah untuk tingkat kesulitan permainan dan meningkatkan laju pergerakan pilar setelah setiap poin bagus.
Ok yang tersisa sekarang adalah melihat bagaimana fungsi kustom gameOver() bekerja. Setelah penundaan satu detik itu akan menghapus layar, mencetak skor dan beberapa teks, jika skor lebih tinggi dari skor tertinggi itu akan menuliskannya ke dalam EEPROM, itu akan mengatur ulang semua variabel ke nilai posisi awal dan di akhir itu akan memanggil fungsi kustom inisiasiGame() 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 kodenya cukup jelas. Jika Anda memiliki pertanyaan, jangan ragu untuk bertanya di bagian komentar di bawah.
Ini kode lengkap gamenya:
/* 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 download dengan sketsa Arduino, gambar burung, dan file bitmap untuk burung tersebut.