1. 程式人生 > >用arduino編寫貪吃蛇小遊戲(完整程式碼)

用arduino編寫貪吃蛇小遊戲(完整程式碼)

用arduino編寫貪吃蛇小遊戲

裡面有大量的程式碼註釋,希望能真的幫助到電子愛好者。

工具 : 硬體:arduino UNO 、搖桿模組(控制貪吃蛇前後左右移動、 OLED 螢幕 軟體:arduino IDE1.8.2版本(基本上大於1.6版本,就能使用) 咱們閒話不多說,直接上程式碼

程式碼部分: #include “U8glib.h”//引用U8G標頭檔案 U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE);//設定裝置名稱:I2C-SSD1306-128*64(OLED) // #define UP 15 #define DOWN 16 #define LEFT 14 #define RIGHT 17 #define analogRead_num 950 #define delay_time 300 /

************************/ int box_x = 0;//定義變數 int box_y = 0;//定義變數 int box_x_length = 126;//x方向 int box_y_length = 62;//y方向 int snake_max_length = 100; //蛇身體最大長度 int snake_x[100];//蛇身x座標 int snake_y[100];//蛇身y座標 int snake_body_width = 3; //蛇身方塊寬度(正方形) int food_x;//食物位置座標x int food_y;//食物位置座標y int snake_length = 3; //定義初始化蛇身長度 unsigned int game_speed = 90;//設定遊戲速度(數值越大,貪吃蛇移動的越慢)

const uint8_t ji[] PROGMEM = { //封裝漢子“機”的陣列; 0x08,0x00,0x88,0x0F,0x88,0x08,0x88,0x08,0xBF,0x08,0x88,0x08,0x8C,0x08,0x9C,0x08,0xAA,0x08,0xAA,0x08,0x89,0x08,0x88,0x48,0x88,0x48,0x48,0x48,0x48,0x70,0x28,0x00 }; const uint8_t qi[] PROGMEM = { //封裝漢子“器”的陣列; 0x00,0x00,0x7C,0x3E,0x44,0x22,0x44,0x22,0x7C,0x3E,0x80,0x04,0x80,0x08,0xFF,0x7F,0x40,0x01,0x30,0x06,0x0C,0x18,0x03,0x60,0x7C,0x3E,0x44,0x22,0x44,0x22,0x7C,0x3E }; const uint8_t dong[] PROGMEM = { //封裝漢子“動”的陣列; 0x00,0x02,0x00,0x02,0x3E,0x02,0x00,0x02,0x80,0x3F,0x00,0x22,0x7F,0x22,0x04,0x22,0x04,0x22,0x04,0x21,0x12,0x21,0x22,0x21,0xBF,0x20,0xA2,0x20,0x40,0x14,0x20,0x08 }; const uint8_t li[] PROGMEM = { //封裝漢子“力”的陣列; 0x40,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0xFE,0x1F,0x40,0x10,0x40,0x10,0x40,0x10,0x40,0x10,0x20,0x10,0x20,0x10,0x10,0x10,0x10,0x10,0x08,0x11,0x04,0x0A,0x02,0x04 }; /***********************************************************************/ void setup(void) { Serial.begin(9600); //開啟串列埠; pinMode(UP, INPUT_PULLUP); //定義控制貪吃蛇上下的感測器引腳為輸入模式,這裡的INPUT_PULLUP意思是將該引腳的電平拉高; pinMode(LEFT, INPUT_PULLUP);//定義控制貪吃蛇左右的感測器引腳為輸入模式,這裡的INPUT_PULLUP意思是將該引腳的電平拉高; welcome(); //歡迎介面; delay(1000); }

void show_init_one_one() // { String shangbiao = “JiQiDongLi”;

for(int i=0;i<40;i++) { u8g.firstPage(); do{ u8g.setPrintPos(15+i, 55); u8g.print(shangbiao); }while (u8g.nextPage()); } }

//程式初始化需要顯示的內容============================= void show_init_one() // { // for(int i=0;i<4;i++) // { // u8g.firstPage(); //開啟載入介面; // do{ // u8g.setFont(u8g_font_gdr14r);u8g.setPrintPos(0, 20);u8g.print(“Snake Game”); //在OLED 螢幕上顯示”Snake Game”;【u8g.setFont(字型);表示要顯示的是哪種字型, // //u8g.setPrintPos(x,y);表示要顯示的位置,u8g.print(“內容”);表示在OLED 螢幕上顯示“內容”;】 // u8g.setFont(u8g_font_9x18);u8g.setPrintPos(15, 55); u8g.print(“JiQiDongLi”); //用u8g_font_9x18的字型在OLED 螢幕上的(15,55)顯示"JiQiDongLi". // } while (u8g.nextPage()); //畫面載入完畢; (這裡使用了do…while()語句,先執行do中的語句,再判斷while()中的語句是否符合條件,如果符合,跳出該迴圈,否則,繼續執行迴圈) // delay(delay_time); // } // // for(int i=0;i<100;i++) // { // u8g.firstPage(); // do { // u8g.setFont(u8g_font_gdr14r);u8g.setPrintPos(0, 20);u8g.print(“Snake Game”); //在OLED 螢幕上顯示”Snake Game”;【u8g.setFont(字型);表示要顯示的是哪種字型, // u8g.drawXBMP( 25+i, 45, 16, 16, ji); //在OLED 螢幕顯示“機”; // u8g.drawXBMP( 45+i, 45, 16, 16, qi); //在OLED 螢幕顯示“器”; // u8g.drawXBMP( 65+i, 45, 16, 16, dong); //在OLED 螢幕顯示“動”; // u8g.drawXBMP( 85+i, 45, 16, 16, li); //在OLED 螢幕顯示“力”; // } while( u8g.nextPage() ); // } // } // // void show_init_two() // { // for(int i=0;i<2;i++){ //顯示"Ready." // u8g.firstPage(); // do{ // u8g.setFont(u8g_font_9x18); u8g.setPrintPos(25, 42); u8g.print(“Ready”); //用u8g_font_9x18的字型在OLED 螢幕上的(25,42)顯示"Ready". // u8g.drawBox(70,42,3,3); //緊接著在(70,42)顯示寬和高都為3個畫素的實心矩形;顯示的 // //整體效果為“Ready." // } while (u8g.nextPage()); // delay(delay_time); // } // for(int i=0;i<2;i++){ //顯示“Ready…" // u8g.firstPage(); // do{ // u8g.setFont(u8g_font_9x18);u8g.setPrintPos(25, 42);u8g.print(“Ready”);//用u8g_font_9x18的字型在OLED 螢幕上的(15,55)顯示"Ready". // u8g.drawBox(70,42,3,3); u8g.drawBox(74,42,3,3); //緊接著在(70,42)和(74,42)顯示寬和高都為3個畫素的兩個 // //實心矩形;顯示的整體效果為“Ready…” // }while (u8g.nextPage()); // delay(delay_time); // } // for(int i=0;i<2;i++){ //顯示"Ready…" // u8g.firstPage(); // do{ // u8g.setFont(u8g_font_9x18); u8g.setPrintPos(25, 42);u8g.print(“Ready”); // u8g.drawBox(70,42,3,3); u8g.drawBox(74,42,3,3); u8g.drawBox(78,42,3,3);//同理,顯示的整體效果為“Ready…” // }while (u8g.nextPage()); // delay(delay_time); // } // } // // void show_init_three() //初始化顯示的第三部分(該部分主要是顯示“GO!”) // { // for(int i=0;i<2;i++){ // u8g.firstPage(); // do{ // u8g.setFont(u8g_font_9x18); u8g.setPrintPos(50, 42); u8g.print(“GO!”); // } while (u8g.nextPage()); // delay(delay_time); // } // } // //============================

/************************************************ 程式剛開始要顯示的部分 ************************************************/ void welcome() //開機需要執行的程式; { // while(digitalRead(UP)) {show_init_one();} //載入開機的第一個介面; show_init_one(); // show_init_one_one(); show_init_two(); //顯示"Ready." 顯示"Ready…" 顯示"Ready…" show_init_three(); //顯示"GO!" }

/************************************************ 讀取感測器的狀態,並返回相應的數值 ************************************************/ int read_key() { int key_temp; if (digitalRead(UP) == LOW) { key_temp = UP; return key_temp; } if (analogRead(UP) >= analogRead_num) { key_temp = DOWN;return key_temp; } if (digitalRead(LEFT) == LOW) { key_temp = LEFT;return key_temp; } if (analogRead(LEFT) >= analogRead_num) { key_temp = RIGHT; return key_temp; } return 0; }

/************************************************ | GAME OVER!(對遊戲結束進行介紹) | ************************************************/ void game_over()//對遊戲結束進行定義 { for(int i=0;i<2;i++){ u8g.firstPage(); do{ u8g.setFont(u8g_font_gdr14r);u8g.setPrintPos(0, 40);u8g.print(" GAME OVER!"); } //在OLED 螢幕上的(0,40)的位置顯示“GAME OVER!” while (u8g.nextPage()); delay(500); } snake_length=3; //============================= snake_x[0] = 15; snake_y[0] = 15;//snake起始座標 //這幾步代表的意思是恢復貪吃蛇 snake_x[1] = snake_x[0] - 1; snake_y[1] = snake_y[0];//snake起始座標 //初始的狀態(包括長度和位置) snake_x[2] = snake_x[1] - 1; snake_y[2] = snake_y[1];//snake起始座標 //============================= }

/************************************************ 對貪吃蛇引數進行定義 ************************************************/ void snake() { int flag = 1; //標誌 snake_x[0] = 15; snake_y[0] = 15;//snake起始座標 //====================================== snake_x[1] = snake_x[0] - 1; snake_y[1] = snake_y[0];//snake起始座標 //定義貪吃蛇的初始的位置以及長度; snake_x[2] = snake_x[1] - 1; snake_y[2] = snake_y[1];//snake起始座標 //====================================== int snake_dir=RIGHT; //初始方向 right game_speed=90; int food_flag=1; Serial.println(“snake!”); food(); while (flag) { snake_frame(); //將此時的貪吃蛇的狀態顯示在OLED 螢幕上; delay(game_speed); //這裡是貪吃蛇調節速度的另外一個方法; switch(read_key()) //此時讀取搖桿模組的擺動方位的狀態(分為上下左右四個擺動方位); { case UP: if(snake_dir!=DOWN) { snake_dir=UP; break; } //================================================================ case DOWN:if(snake_dir!=UP) { snake_dir=DOWN;break; } //這四行程式碼表示:搖桿擺動到不同位置時,變數snake_dir所對應的數值; case LEFT: if(snake_dir!=RIGHT) { snake_dir=LEFT; break; } //如果搖桿向右擺動,那麼此時snake_dir=RIGHT(17); case RIGHT:if(snake_dir!=LEFT) { snake_dir=RIGHT; break; } default:break; } if(snake_eat_food(snake_dir)1) { food(); if(snake_length<10){game_speed=90;} if(snake_length<30&&snake_length>=10){game_speed=80;} if(snake_length<50&&snake_length>=30){game_speed=50;} if(snake_length>=50){game_speed=0;} } else { if(snake_knock_wall(snake_dir1)){ game_over(); flag=0; } else { if(snake_eat_body(snake_dir)1) //=============================================================== { //判斷貪吃蛇是否吃到自己的身體, game_over(); //如果吃到,遊戲結束; flag=0; //================================================================= } else { //================================================================= change_con(snake_dir); //如果貪吃蛇沒有吃到食物,沒有撞牆,沒有吃到自己的身體,那麼貪吃蛇 } //執行change_con(snake_dir);函式; } //================================================================= } } }

/************************************************ 顯示部分 **********************************************/ void snake_frame() { u8g.firstPage(); do { u8g.drawFrame(box_x, box_y, 126, 62); //顯示寬為126,高為62的空心矩形框 u8g.drawFrame(food_xsnake_body_width+1, food_ysnake_body_width+1, snake_body_width, snake_body_width);//顯示食物 for (int i = 0; i < snake_length; i++) //顯示貪吃蛇的身體函式; { if(i==0){ u8g.drawFrame(snake_x[i]*snake_body_width+1, snake_y[i]*snake_body_width+1, snake_body_width, snake_body_width); } else{ u8g.drawBox(snake_x[i]*snake_body_width+1, snake_y[i]*snake_body_width+1, snake_body_width, snake_body_width); } } } while (u8g.nextPage()); }

/************************************************ 對實物進行定義 ************************************************/ void food() { int flag = 1; while (flag) { food_x = random(0,(box_x_length-2)/3); food_y = random(0,(box_y_length-2)/3); //這裡用到了random()函式,會隨機顯示食物 for (int i = 0; i < snake_length; i++) { if((food_xsnake_x[i])&&(food_ysnake_y[i])) //該語句的作用主要是避免食物出現的位置與貪吃蛇身體的某一部分重合 //也就是防止食物出現在貪吃蛇身上。 { break; } flag=0; } } }

/************************************************ 移動座標函式 ************************************************/ void change_con(int DIR) //如果貪吃蛇沒有吃到食物時,貪吃蛇移時的函式封裝; { int temp_x[snake_length+2]; //定義一個臨時儲存貪吃蛇整個身體橫座標的陣列; int temp_y[snake_length+2]; //定義一個臨時儲存貪吃蛇整個身體縱座標的陣列; for(int i=0;i<snake_length-1;i++)//將舊座標資料存放到temp陣列 最後一位資料無效; { temp_x[i]=snake_x[i]; // temp_y[i]=snake_y[i]; //這兩個步驟是將貪吃蛇整個身體的橫縱座標儲存到臨時定義的兩個陣列中; } switch(DIR) //判斷貪吃蛇處於搖桿的哪個位置; { case RIGHT: {snake_x[0]+=1;break;} case LEFT: {snake_x[0]-=1;break;} case UP: {snake_y[0]-=1;break;} case DOWN: {snake_y[0]+=1;break;} } for(int i=1;i<snake_length;i++) { snake_x[i]=temp_x[i-1]; snake_y[i]=temp_y[i-1]; //所以,貪吃蛇身體橫縱座標依次為臨時陣列中儲存的貪吃蛇身體的橫縱座標; } }

/************************************************ 吃到東西后移動函式 ************************************************/ void change_pos() { int temp_x[snake_length+2]; int temp_y[snake_length+2];//因為貪吃蛇不僅僅有橫著的狀態,還有豎著的狀態,所以,貪吃蛇這裡的Y座標也需要設定成貪吃蛇的總長度 for(int i=0;i<snake_length-1;i++)//將舊座標資料存放到temp陣列 最後一位資料無效 { temp_x[i]=snake_x[i]; //將貪吃蛇的X座標存入新定義的temp_x[]陣列中; temp_y[i]=snake_y[i]; //將貪吃蛇的Y座標存入新定義的temp_y[]陣列中; } snake_x[0]=food_x; snake_y[0]=food_y; //如果貪吃蛇吃到食物後,需要將食物的縱座標儲存在貪吃蛇的頭部的座標 for(int i=1;i<snake_length;i++) { snake_x[i]=temp_x[i-1]; snake_y[i]=temp_y[i-1]; } }

/************************************************ 判斷是否吃到食物 0-沒有吃到 1-吃到 ************************************************/ int snake_eat_food(int dir) { int x_temp=snake_x[0]; //將此時貪吃蛇的頭的橫座標儲存給變數x_temp; int y_temp=snake_y[0]; //將此時貪吃蛇的頭的縱座標儲存給變數y_temp; switch(dir) //判斷搖桿上一次觸發時所在的狀態; { case UP :y_temp-=1;break; case DOWN :y_temp+=1;break; case LEFT :x_temp-=1;break; case RIGHT :x_temp+=1;break; } if((x_tempfood_x)&&(y_tempfood_y)) { snake_length+=1; //如果座標相等,貪吃蛇的長度增加1; change_pos(); //進入吃到東西移動的函式; return 1; } else { return 0; //否則,沒有吃到食物; } }

/************************************************ 判斷是否撞牆 0-沒有撞到 1-撞到 ************************************************/ int snake_knock_wall(int dir) { int x_temp=snake_x[0]; int y_temp=snake_y[0]; switch(dir) { case UP :y_temp-=1;break; case DOWN :y_temp+=1;break; case LEFT :x_temp-=1;break; case RIGHT :x_temp+=1;break; } if(x_temp<0||x_temp>42||y_temp<0||y_temp>19) { return 1; } else{ return 0; } }

/************************************************ 判斷是否吃到自己 0-沒有吃到 1-吃到 ************************************************/ int snake_eat_body(int dir) { int x_temp=snake_x[0]; int y_temp=snake_y[0]; switch(dir) { case UP :y_temp-=1;break; case DOWN :y_temp+=1;break; case LEFT :x_temp-=1;break; case RIGHT :x_temp+=1;break; } for(int i=1;i<snake_length;i++) { if((snake_x[0]==snake_x[i])&&(snake_y[0]==snake_y[i])) { return 1; } } return 0; }

/************************************************ 主迴圈函式 ************************************************/ void loop(void) { while (1) { snake(); } }