Ini adalah versi kecil dari video game breakout klasik untuk Arduino UNO dan layar LCD TFT (240x320 piksel), driver ILI9341 dengan komunikasi paralel 8-bit.
Breakout ini memiliki beberapa layar dengan baris dan kolom bata yang dapat dikonfigurasi berbeda, hingga delapan baris, dengan masing-masing dua baris warna yang berbeda, yang dapat diprogram atau dimatikan dengan pola yang berbeda. Menggunakan satu bola, menggunakan panel sentuh, pemain harus merobohkan batu bata sebanyak mungkin dengan menggunakan dinding dan/atau dayung di bawah untuk memantulkan bola ke batu bata dan menghilangkannya. Jika dayung pemain meleset dari pantulan bola, mereka akan kehilangan giliran.
Setiap baris bata mendapatkan poin yang berbeda.
Setiap level dapat mengonfigurasi ukuran dayung dan ukuran bola. Kecepatan bola meningkat pada setiap pukulan, Anda dapat mengonfigurasi kecepatan awal untuk setiap layar. Tergantung pada titik dayung yang mengenai bola, kecepatan horizontal juga berubah
Anda dapat mendefinisikan sebagai layar baru dengan pola dinding yang berbeda:
Pegang perangkat dengan tangan Anda dan gunakan ibu jari Anda di atas layar untuk menggerakkan dayung ke kiri atau ke kanan.
ej.
Proyek ini menggunakan layar LCD TFT AZ-Delivery 2,4 inci dengan layar sentuh 4-kawat resistif dan pembaca kartu SD terintegrasi. Layar LCD TFT 2,4 inci AZ-Delivery.
Lebih lanjut tentang perisai ini di artikel saya "Pengujian jalan AZ-Delivery 2, 4 TFT LCD Touch Display"
Anda hanya perlu memasang pelindung di atas Aduino.
Anda harus mengkalibrasi layar agar informasi posisi benar saat Anda menyentuh layar. Pustaka MCUFriend_kbv memberikan contoh dengan nama "TouchScreen_Calibr_native". Contoh mengirimkan hasil ke port serial. Mulai monitor serial Arduino IDE sehingga Anda dapat menyalin kode yang dihasilkan oleh contoh.
Ikuti petunjuk pada layar sentuh, tekan dan tahan penanda posisi yang ditampilkan, yang disorot dalam warna putih. Setelah Anda menyelesaikan semua tanda posisi, kalibrasi tampilan ditampilkan kepada Anda pada layar sentuh dan melalui port serial.
Untuk proyek ini, Anda memerlukan data untuk "kalibrasi potret".
Untuk memindahkan gambar melintasi layar dari waktu ke waktu adalah dengan menggunakan kecepatan statis dan menerapkannya ke posisi gambar setiap langkah waktu.
Resolusi ILI9341 adalah 240 x 320 sehingga kita membutuhkan dua bilangan bulat 9 bit untuk mereferensikan piksel di layar. Menggunakan bilangan bulat 16 bit, mari kita bebaskan 6 bit untuk mewakili bagian desimal.
Kami menyebut angka ini, 6, eksponen biner. Dan kita dapat menggunakan enam bit ini untuk memiliki bagian desimal mulai dari 0,000 hingga 0,63. Jadi kita bisa menggunakan matematika bilangan bulat untuk menghindari aritmatika titik mengambang.
Untuk mendapatkan bagian bilangan bulat dari bilangan tersebut, kita lakukan pergeseran aritmatika yang benar.
Batalkan komentar pada Arahan Definisi dan dayung akan mengikuti bola seperti yang terlihat dalam video demo:
Kode
Pecahan ArduinoArduino
/* Arduino Touch TFT Breakout Game breakout klasik Bagian yang dibutuhkan:Ardunio UNO AZ-Delivery 2.4 TFT LCD Touch Display Arduino Shield atau yang kompatibel Kode contoh ini ada di domain publik. Dimodifikasi 07 11 2020 Oleh Enrique Albertos*/// #define DEMO_MODE#include // Core graphics library#include #include #define BLACK 0x0000#define BLUE 0x001F#define RED 0xF800 # mendefinisikan GREEN 0x07E0 # mendefinisikan CYAN 0x07FF # mendefinisikan MAGENTA 0xF81F # mendefinisikan YELLOW 0xFFE0 # mendefinisikan PUTIH 0xFFFF # mendefinisikan PRIMARY_COLOR 0x4A11 # mendefinisikan PRIMARY_LIGHT_COLOR 0x7A17 # mendefinisikan PRIMARY_DARK_COLOR 0x4016 # mendefinisikan PRIMARY_TEXT_COLOR 0x7FFF # mendefinisikan LCD_CS A3 // Chip Pilih pergi ke Analog 3 #define LCD_CD A2 // Command/Data masuk ke Analog 2#define LCD_WR A1 // LCD Write ke Analog 1#define LCD_RD A0 // LCD Read masuk ke Analog 0#define LCD_RESET A4 // Bisa bergantian sambungkan ke reset Arduino pinAdafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);#define LOWFLASH (defined(__AVR_ATmega328P__) &&didefinisikan(MCUFRIEND_KBV_H_))// Ambang batas kalibrasi layar sentuh#define MINPRESSURE 40#define XP 8, XM =A2, YP =A3, YM =9; //240x320 ID=0x9341konst int16_t TS_LEFT =122, TS_RT =929, TS_TOP =77, TS_BOT =884;const Layar Sentuh ts =Layar Sentuh(XP, YP, XM, YM, 300);#definisi SCORE_SIZE 30char format skor[] ="% 04d";typedef struct gameSize_type { int16_t x, y, lebar, tinggi;} gameSize_type;gameSize_type gameSize;uint16_t backgroundColor =BLACK;int level;const uint8_t BIT_MASK[] ={0x01, 0x02, 0x04, 0x08, 0x40, 0x08, 0x , 0x80};uint8_t poinForRow[] ={7, 7, 5, 5, 3, 3 , 1, 1};#define GAMES_NUMBER 16typedef struct game_type { int ballsize; int lebar pemain; int tinggi pemain; int eksponen; int atas; int baris; kolom int; int brickGap; kehidupan int; int dinding[GAMES_NUMBER]; int initVelx; int initVely;} game_type;game_type games[GAMES_NUMBER] =// ukuran bola, lebar pemain, tinggi pemain, eksponen, atas, baris, kolom, brickGap, hidup, dinding[8], initVelx, initVely{ { 10, 60, 8, 6, 40 , 8, 8, 3, 3, {0x18, 0x66, 0xFF, 0xDB, 0xFF, 0x7E, 0x24, 0x3C} , 28, -28}, { 10, 50, 8, 6, 40 , 8, 8, 3 , 3, {0xFF, 0x99, 0xFF, 0xE7, 0xBD, 0xDB, 0xE7, 0xFF} , 28, -28}, { 10, 50, 8, 6, 40 , 8, 8, 3, 3, {0xAA, 0x55 , 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55} , 28, -28}, { 8, 50, 8, 6, 40 , 8, 8, 3, 3, {0xFF, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xFF} , 34, -34}, { 10, 40, 8, 6, 40 , 8, 8, 3, 3, {0xFF, 0xAA, 0xAA, 0xFF, 0xFF, 0xAA, 0xAA, 0xFF} , 28, -28}, { 10, 40, 8, 6, 40 , 8, 8, 3, 3, {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA} , 28, -28}, { 12, 64, 8, 6, 60, 4, 2, 3, 4, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} , 20, -20}, { 12, 60, 8, 6 , 60 , 5, 3, 3, 4, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} , 22, -22}, { 10, 56, 8, 6, 30 , 6, 4, 3, 4, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} , 24, -24}, { 10, 52, 8, 6, 30 , 7, 5, 3, 4, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} , 26, -26 }, { 8, 48, 8, 6, 30 , 8, 6, 3, 3, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} , 28, -28}, { 8, 44, 8, 6, 30 , 8, 7, 3, 3, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} , 30, -30}, { 8, 40, 8, 6, 30 , 8 , 8, 3, 3, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} , 32, -32}, { 8, 36, 8, 6, 40 , 8, 8, 3, 3, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} , 34, -34}, { 8, 36, 8, 6, 40 , 8, 8, 3, 3, {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA} , 34, -34}};game_type* game;typedef struct game_state_type { uint16_t ballx; uint16_t bally; uint16_t ballxold; uint16_t ballyold; int kecepatan; int vely; int pemainx; int pemainxold; int wallState[8]; skor int; int sisaHidup; int atas; int bawah; int dinding; int dinding bawah; int bata; int brickwidth;};game_state_type state;/////////////////////////////////////////////////////////// ////////////////////// ARDUINO SETUP/////////////////////////////////////////// ///////////////////////////////////pengaturan batal(){ initTft(tft); ukuran game ={0, 0, tft.width(), tft.height()}; newGame(&games[0], &state, tft);}////////////////////////////////////////////////////// /////////////////////////// ARDUINO LOOP//////////////////// ////////////////////////////////////////int seleksi =-1;void loop( void){ seleksi =readUiSelection(permainan, &status, pilihan); drawPlayer(permainan, &status); // simpan posisi lama untuk menghapus piksel lama state.playerxold =state.playerx; // hitung posisi bola baru x1 =x0 + vx * dt // periksa kecepatan maksimum jika (abs( state.vely)> ((1 <exponent) - 1)) { state.vely =((1 <eksponen) - 1) * ((state.vely> 0) - (state.vely <0)); } if (abs( state.velx)> ((1 <exponent) - 1)) { state.velx =((1 <exponent) - 1) * ((state.velx> 0 ) - (keadaan.velx <0)); } state.ballx +=state.velx; state.bally +=state.vely; // periksa tabrakan bola dan keluar dari checkBallCollisions(game, &state, state.ballx>> game->exponent, state.bally>> game->exponent); checkBallExit(game, &state, state.ballx>> game->exponent, state.bally>> game->exponent); // menarik bola di posisi baru drawBall(state.ballx>> game->exponent, state.bally>> game->exponent, state.ballxold>> game->exponent, state.ballyold>> game->exponent, game ->ukuran bola); // simpan posisi lama untuk menghapus piksel lama state.ballxold =state.ballx; state.ballyold =status.bally; // kenaikan kecepatan state.velx =(20 + (state.score>> 3 )) * ( (state.velx> 0) - (state.velx <0)); state.vely =(20 + (state.score>> 3 )) * ( (state.vely> 0) - (state.vely <0)); // jika tidak ada batu bata, lanjutkan ke level berikutnya if (noBricks(game, &state) &&level lives, state->remainingLives); updateScore(status->skor); setupWall(permainan, status); sentuhUntukMulai(); clearDialog(Ukuran game); updateLives(game->lives, state->remainingLives); updateScore(status->skor); setupWall(game, state);}void setupStateSizes(game_type* game, game_state_type * state, Adafruit_TFTLCD &tft) { state->bottom =tft.height() - 30; state->brickwidth =tft.width() / game->columns; state->brickheight =tft.height() / 24;}void setupState(game_type* game, game_state_type * state, Adafruit_TFTLCD &tft) { setupStateSizes(game, state, tft); for (int i =0; i rows; i ++) { state->wallState[i] =0; } state->playerx =tft.width() / 2 - game->playerwidth / 2; state->remainingLives =game->lives; state->bally =state->bottom <eksponen; state->ballyold =state->bottom <eksponen; negara->velx =permainan->initVelx; state->vely =game->initVely;}void updateLives(int hidup, int sisaLives) { for (int i =0; i walltop =permainan->atas + 40; state->wallbottom =state->walltop + game->rows * state->brickheight; for (int i =0; i baris; i++) { for (int j =0; j kolom; j++) { if (isBrickIn(game->wall, j, i)) { setBrick (negara->wallState, j, i); drawBrick(status, j, i, warna[i]); } } }}void drawBrick(game_state_type * state, int xBrick, int yBrickRow, uint16_t backgroundColor) { tft.fillRect((state->brickwidth * xBrick) + game->brickGap, state->walltop + (state->brickheight * yBrickRow) + game->brickGap , state->brickwidth - game->brickGap * 2, state->brickheight - game->brickGap * 2, backgroundColor);}boolean noBricks(game_type * game, game_state_type * state) { for ( int i =0; i rows; i++) { if (state->wallState[i]) mengembalikan false; } return true;}void drawPlayer(game_type * game, game_state_type * state) { // paint tft.fillRect(state->playerx, state->bottom, game->playerwidth, game->playerheight, KUNING); if (state->playerx !=state->playerxold) { // menghapus piksel lama if (state->playerx playerxold) { tft.fillRect(state->playerx + game->playerwidth, state->bottom , abs(state->playerx - state->playerxold), game->playerheight, backgroundColor); } else { tft.fillRect(state->playerxold, state->bottom, abs(state->playerx - state->playerxold), game->playerheight, backgroundColor); } }}void drawBall(int x, int y, int xold, int yold, int ballsize) { // hapus piksel lama //if (xold !=x &&yold !=y) { if (xold <=x &&yold <=y) { tft.fillRect(xold , yold, ukuran bola, y - yold, HITAM); tft.fillRect(xold , yold, x - xold, ballsize, HITAM); } else if (xold>=x &&yold>=y) { tft.fillRect(x + ballsize , yold, xold - x, ballsize, HITAM); tft.fillRect(xold , y + ukuran bola, ukuran bola, kuning - y, HITAM); } else if (xold <=x &&yold>=y) { tft.fillRect(xold , yold, x - xold, ballsize, HITAM); tft.fillRect(xold , y + ukuran bola, ukuran bola, kuning - y, HITAM); } else if (xold>=x &&yold <=y) { tft.fillRect(xold , yold, ballsize, y - yold, HITAM); tft.fillRect(x + ukuran bola, yold, xold - x, ukuran bola, HITAM); } // cat bola baru tft.fillRect(x , y, ukuran bola, ukuran bola, KUNING); // }}void touchToStart() { drawBoxedString(0, 200, " BREAKOUT", 3, KUNING, HITAM); drawBoxedString(0, 240, "SENTUH UNTUK MULAI", 2, MERAH, HITAM); while (waitForTouch() <0) {}}void gameOverTouchToStart() { drawBoxedString(0, 180, " GAME OVER", 3, KUNING, HITAM); drawBoxedString(0, 220, "SENTUH UNTUK MULAI", 2, MERAH, HITAM); while (waitForTouch() <0) {}}void updateScore (int score) { char buffer[5]; snprintf(penyangga, ukuran(penyangga), format skor, skor); drawBoxedString(tft.width() - 50, 6, buffer, 2, KUNING, PRIMARY_DARK_COLOR);}void checkBrickCollision(game_type* game, game_state_type * state, uint16_t x, uint16_t y) { int x1 =x + game->ballsize; int y1 =y + permainan->ukuran bola; int tabrakan =0; tabrakan +=checkCornerCollision(permainan, status, x, y); tabrakan +=checkCornerCollision(permainan, status, x1, y1); tabrakan +=checkCornerCollision(permainan, status, x, y1); tabrakan +=checkCornerCollision(permainan, status, x1, y); if (tabrakan> 0 ) { state->vely =(-1 * state->vely); if (((((x % state->brickwidth) ==0) &&( state->velx <0 )) || ((((x + game->ballsize) % state->brickwidth) ==0) &&( state->velx> 0 )) ) { state->velx =(-1 * state->velx); } }}int checkCornerCollision(game_type * game, game_state_type * state, uint16_t x, uint16_t y) { if ((y> state->walltop) &&(y wallbottom)) { int yBrickRow =( y - state->walltop) / state->brickheight; int xBrickColumn =(x / state->brickwidth); if (isBrickIn(state->wallState, xBrickColumn, yBrickRow) ) { hitBrick(state, xBrickColumn, yBrickRow); kembali 1; } } return 0;}void hitBrick(game_state_type * state, int xBrick, int yBrickRow) { state->score +=pointsForRow[yBrickRow]; drawBrick(status, xBrick, yBrickRow, WHITE); penundaan (16); drawBrick(status, xBrick, yBrickRow, BIRU); penundaan (8); drawBrick(status, xBrick, yBrickRow, backgroundColor); unsetBrick(negara->wallState, xBrick, yBrickRow); updateScore(state->score);}void checkBorderCollision(game_type * game, game_state_type * state, uint16_t x, uint16_t y) { // periksa tabrakan dinding if (x + game->ballsize>=tft.width()) { status ->velx =-abs(keadaan->velx); } if (x <=0 ) { keadaan->velx =abs(keadaan->velx); } if (y <=SCORE_SIZE ) { state->vely =abs(state->vely); } if (((y + game->ballsize)>=state->bottom) &&((y + game->ballsize) <=(state->bottom + game->playerheight)) &&(x>=state->playerx) &&(x <=(state->playerx + game->playerwidth))) { // mengubah vel x di dekat batas pemain if (x> (state->playerx + game->playerwidth - 6)) { state ->velx =keadaan->velx - 1; } else if (x playerx + 6) { state->velx =state->velx + 1; } state->vely =-abs(state->vely); }}void checkBallCollisions(game_type * game, game_state_type * state, uint16_t x, uint16_t y) { checkBrickCollision(game, state, x, y); checkBorderCollision(game, state, x, y);}void checkBallExit(game_type * game, game_state_type * state, uint16_t x, uint16_t y) { if (((y + game->ballsize)>=tft.height())) { state->remainingLives--; updateLives(game->lives, state->remainingLives); penundaan (500); state->vely =-abs(state->vely); }}void setBrick(int wall[], uint8_t x, uint8_t y) { wall[y] =wall[y] | BIT_MASK[x];}void unsetBrick(int wall[], uint8_t x, uint8_t y) { wall[y] =wall[y] &~BIT_MASK[x];}boolean isBrickIn(int wall[], uint8_t x, uint8_t y) { dinding balik[y] &BIT_MASK[x];}/////////////////////////////////////////////////// //////////////////////////// PENGATURAN TFT////////////////// ////////////////////////////////////////void initTft(Adafruit_TFTLCD &tft) { tft.reset(); uint16_t ID =tft.readID(); tft.begin(ID); tft.setRotation(0);}////////////////////////////////////////////////////////// ////////////////////// Metode Lukisan Layar///////////////////////////////////// ////////////////////////////////////** Mencetak teks dalam warna depan di atas kotak yang diisi dengan warna latar belakang. Ukuran persegi dihitung untuk memasukkan seluruh teks tanpa margin @param x koordinat horizontal dalam poin sudut kiri atas @param y koordinat vertikal dalam poin sudut kiri atas @param ukuran font ukuran font teks yang akan dicetak @param foreColor forecolor dari teks yang akan dicetak @param backgroundWarna warna dari rect yang terisi @return void*/void drawBoxedString(const uint16_t x, const uint16_t y, const char* string, const uint16_t fontsize, const uint16_t foreColor, const uint16_t backgroundColor) { tft.settext); int16_t x1, y1; uint16_t w, j; tft.getTextBounds(string, x, y, &x1, &y1, &w, &h); tft.fillRect(x, y, w, h, backgroundColor); tft.setCursor(x, y); tft.setTextColor(Warna Depan); tft.print(string);}/** Kosongkan layar ke latar belakang default @param void @return void*/void clearDialog(gameSize_type gameSize) { tft.fillRect(gameSize.x, gameSize.y, gameSize.width, gameSize .height, backgroundColor); tft.fillRect(gameSize.x, gameSize.y, gameSize.width, SCORE_SIZE, PRIMARY_DARK_COLOR);}////////////////////////// ////////////////////////////////// BACA PILIHAN UI/////////// ////////////////////////////////////////////////////////////////// //* Memeriksa apakah pengguna memilih salah satu elemen ui yang diaktifkan yang terlihat Callback onTap dari elemen yang dipilih dipanggil dan disetel sebagai ditekan @param lastSelected the last selection @return the new selection*/int readUiSelection(game_type * game, game_state_type * state, const int16_t lastSelected ) { int16_t xpos, ypos; //koordinat layar TSPoint tp =ts.getPoint(); //tp.x, tp.y adalah nilai ADC // jika berbagi pin, Anda harus memperbaiki arah pin layar sentuh pinMode(XM, OUTPUT); pinMode(YP, OUTPUT); // kami memiliki beberapa tekanan minimum yang kami anggap 'valid' // tekanan 0 berarti tidak ada tekanan! if (tp.z> MINPRESSURE &&tp.z tft.width() / 2) { state->playerx +=2; } else { status->playerx -=2; } if (state->playerx>=tft.width() - game->playerwidth) state->playerx =tft.width() - game->playerwidth; if (state->playerx <0) state->playerx =0; kembali 1; }#ifdef DEMO_MODE state->playerx =(state->ballx>> game->exponent) - game->playerwidth / 2; if (state->playerx>=tft.width() - game->playerwidth) state->playerx =tft.width() - game->playerwidth; if (state->playerx <0) state->playerx =0;#endif return -1;}int waitForTouch() { int16_t xpos, ypos; //koordinat layar TSPoint tp =ts.getPoint(); //tp.x, tp.y adalah nilai ADC // jika berbagi pin, Anda harus memperbaiki arah pin layar sentuh pinMode(XM, OUTPUT); pinMode(YP, OUTPUT); // kami memiliki beberapa tekanan minimum yang kami anggap 'valid' // tekanan 0 berarti tidak ada tekanan! if (tp.z> MINPRESSURE &&tp.z Skema