目錄
1 系統設計目的
2 系統實現功能
3 系統硬件設計
3.1系統設計框圖
3.2 液晶顯示模塊LCD12864
3.3 按鍵輸入模塊
3.4 時鐘電路和復位電路
4 系統軟件設計
4.1系統軟件流程
4.2 游戲引擎模塊程序設計
4.3?顯示模塊程序設計
4.4?輸入處理模塊程序設計
5?系統仿真結果
5.1 游戲引擎模塊的實現
5.2 顯示模塊功能的實現
5.3 輸入處理模塊的實現
1 系統設計目的
? ? ? ?設計一個基于51單片機的貪吃蛇游戲,可以幫助學習者掌握硬件控制相關知識、學習游戲開發和代碼實現,提高編程能力和解決問題的能力,同時培養團隊合作精神和創新意識。
? ? (1)掌握基于51單片機的硬件控制相關知識,包括輸入輸出口的操作、中斷處理、定時器/計數器等模塊的使用。
? ? (2)學習貪吃蛇游戲的實現方法,掌握游戲邏輯的設計和代碼實現,加深對面向對象編程的理解。
? ? (3)提高編程能力和解決問題的能力,通過設計和編寫程序,發現和解決軟件和電路方面的問題。
? ? (4)培養團隊合作精神和創新意識,多思考和嘗試不同的解決方案,共同完成項目。
? ? (5)加深對嵌入式系統和游戲開發的了解,拓展相關領域的技能。
2 系統實現功能
? ? ? ?本項目旨在設計和實現一個基于51單片機的貪吃蛇游戲,使用LCD12864顯示屏作為游戲界面,通過矩陣按鍵控制蛇的移動,并增加暫停/啟動游戲、顯示已吃的食物數量等功能。以下是該項目的詳細內容和功能要求:
? ? ? ?(1)游戲界面設計:
? ? ? ?使用LCD12864顯示屏作為游戲界面,具有較高的分辨率,能夠顯示游戲界面、蛇、食物等元素。游戲界面應具備良好的界面布局,清晰明了,易于辨認。
? ? ? ?(2)蛇的控制:
? ? ? ?運用矩陣按鍵的4個按鍵分別控制蛇的上、下、左、右移動。當蛇吃到食物時,長度增加并重新生成食物,結束時顯示已吃的食物數量。蛇的移動速度可以逐漸加快,增加游戲難度。
? ? ? ?(3)暫停/啟動游戲功能:
? ? ? ?設計一個特定的按鍵,用于暫停或啟動游戲。當按下暫停按鍵時,游戲暫停,蛇停止移動,游戲界面保持靜止。再次按下該按鍵時,游戲繼續進行,恢復蛇的移動。
? ? ? ?(3)食物顯示功能:
? ? ? ?在游戲界面中增加顯示當前已吃的食物數量的功能。每次蛇吃到食物時,更新并顯示已吃的食物數量。
3 系統硬件設計
3.1系統設計框圖
? ? ? ?本設計由控制器、液晶顯示模塊、按鍵輸入模塊、時鐘電路和復位電路等5部分組成。控制器是系統的核心,通過IO口控制液晶顯示模塊,實現游戲畫面的顯示和游戲狀態的更新,同時也可以接收按鍵輸入模塊的信號,根據使用者的操作控制蛇的移動和游戲速度等參數;液晶顯示模塊LCD12864用于顯示游戲界面和相關信息;按鍵輸入模塊用于接收用戶的操作輸入,通過按下不同的按鈕,用戶可以控制蛇的移動方向和游戲速度等。時鐘電路是為主控芯片提供穩定的時鐘信號,通過確定的振蕩頻率,主控芯片可以按照時序進行正常的工作和指令執行;復位電路可以使用復位按鈕或者上電復位電路,通過將主控芯片的復位引腳拉低一段時間來實現復位操作。系統設計框圖如圖3.1所示。
3.2 液晶顯示模塊LCD12864
? ? ? ?液晶顯示模塊用于顯示游戲界面和相關信息,一般采用16x2或128x64字符液晶顯示屏。通過控制芯片的指令和數據,可以在屏幕上繪制游戲區域、分數以及游戲狀態等內容,為用戶提供良好的視覺體驗。電路原理圖如圖3.2所示。? ?
3.3 按鍵輸入模塊
? ? ? ?按鍵輸入模塊用于接收用戶的操作輸入,通過使用矩陣式按鍵陣列,按下不同的按鈕,用戶可以控制蛇的移動方向和游戲速度等。在硬件設計中,需要考慮防抖處理,以避免因按鍵抖動產生誤操作。電路原理圖如圖3.3所示。
3.4 時鐘電路和復位電路
? ? ? ?時鐘電路是為主控芯片提供穩定的時鐘信號,常使用晶體振蕩器。通過確定的振蕩頻率,主控芯片可以按照時序進行正常的工作和指令執行。根據系統需要,可以選擇合適的晶體頻率,通常使用10MHz或者12MHz晶體。
? ? ? ?復位電路用于在系統上電或者復位時將主控芯片初始化為一個已知的狀態,以確保系統在啟動時正常工作。復位電路可以使用復位按鈕或者上電復位電路,通過將主控芯片的復位引腳拉低一段時間來實現復位操作。電路原理圖如圖3.4所示。
4 系統軟件設計
4.1系統軟件流程
? ? ? ? 基于51單片機的貪吃蛇游戲系統的軟件部分包括四個主要模塊,分別是游戲引擎模塊、顯示模塊、輸入處理模塊和計時模塊。游戲引擎模塊是貪吃蛇游戲的核心,它負責控制游戲的狀態和邏輯;顯示模塊主要負責將游戲的狀態和畫面顯示在液晶屏上;輸入處理模塊負責讀取用戶按鍵操作的輸入,并將其傳遞給游戲引擎模塊進行處理;計時模塊主要負責控制游戲的速度和幀率,為游戲加入時間的概念。基于51單片機的貪吃蛇游戲系統的軟件部分通過游戲引擎、顯示、輸入處理和計時等模塊的協同工作,為用戶提供了一個有趣、穩定和流暢的游戲體驗。總體軟件流程圖如圖4.1所示。
4.2 游戲引擎模塊程序設計
? ? ? ?游戲引擎模塊是貪吃蛇游戲的核心,它負責控制游戲的狀態和邏輯。在每一個游戲循環中,它根據當前蛇的狀態和用戶的操作來更新蛇的位置、食物的位置,檢測蛇是否撞墻或者咬到自己的身體,計算得分等等。游戲引擎模塊也負責處理游戲的初始化和結束邏輯,使得游戲在啟動和結束時能夠正常的運行和退出。游戲引擎模塊程序設計流程圖如圖4.2所示:
游戲引擎模塊部分程序如下所示:
void Game_Play() //游戲的具體過程,也是貪吃蛇算法的關鍵部分
{uchar n;InitRandom(TL0);food.yes=1; //1表示需要出現新事物,0表示已經存在食物尚未吃掉snake.life=0; //表示蛇還活著snake.direction=DOWN;snake.x[0]=6;snake.y[0]=6;snake.x[1]=3;snake.y[1]=6;snake.node=2;Lcd_Show_Score(); //顯示分數Lcd_Show_Title(); //顯示標題while(1){if(food.yes==1){while(1){food.x=Random()*85+3;food.y=Random()*55+3; //獲得隨機數while(food.x%3!=0)food.x++;while(food.y%3!=0)food.y++;for(n=0;n<snake.node;n++) //判斷產生的食物坐標是否和蛇身重合{if((food.x==snake.x[n])&&(food.y==snake.y[n]))break;}if(n==snake.node){food.yes=0;break; //產生有效的食物坐標}}}if(food.yes==0){Lcd_Rectangle(food.x,food.y,food.x+2,food.y+2,1);} if(Run==0){for(n=snake.node-1;n>0;n--){snake.x[n]=snake.x[n-1];snake.y[n]=snake.y[n-1];} switch(snake.direction){case DOWN:snake.x[0]+=3;break;case UP:snake.x[0]-=3;break;case RIGHT:snake.y[0]-=3;break;case LEFT:snake.y[0]+=3;break;default:break;}for(n=3;n<snake.node;n++) //從第三節開始判斷蛇頭是否咬到自己{if(snake.x[n]==snake.x[0]&&snake.y[n]==snake.y[0]){Game_Over();snake.life=1;break;}}}if(snake.x[0]<3||snake.x[0]>=90||snake.y[0]<3||snake.y[0]>=60)//判蛇頭是否撞到墻壁{Game_Over();snake.life=1;}if(snake.life==1)break; //蛇死,則跳出while(1)循環if(snake.x[0]==food.x&&snake.y[0]==food.y) //判蛇是否吃到食物{Lcd_Rectangle(food.x,food.y,food.x+2,food.y+2,0); //消隱食物snake.x[snake.node]=200;snake.y[snake.node]=200;//產生蛇新的節坐標先放在看不見的位置snake.node++; //蛇節數加1food.yes=1; //食物標志置1if(++Score>=PASSSCORE){Lcd_Show_Score();Game_Over();break;}Lcd_Show_Score();}for(n=0;n<snake.node;n++){Lcd_Rectangle(snake.x[n],snake.y[n],snake.x[n]+2,snake.y[n]+2,1);} //根據蛇的節數畫出蛇Delay(Speed*1000);Lcd_Rectangle(snake.x[snake.node-1],snake.y[snake.node-1],snake.x[snake.node-1]+2,snake.y[snake.node-1]+2,0);switch(KeyBuffer){case FUNC:KeyBuffer=0;if(++Speed>=10)Speed=1;Lcd_Show_Title();break;case DOWN: //下KeyBuffer=0;if(snake.direction!=UP)snake.direction=DOWN;break;case UP: //上KeyBuffer=0;if(snake.direction!=DOWN)snake.direction=UP;break;case RIGHT: //右KeyBuffer=0;if(snake.direction!=LEFT)snake.direction=RIGHT;break;case LEFT: //左KeyBuffer=0;if(snake.direction!=RIGHT)snake.direction=LEFT;break;default:break;} }
}
4.3?顯示模塊程序設計
? ? ? ?顯示模塊主要負責將游戲的狀態和畫面顯示在液晶屏上。它通過調用液晶屏的相關API,繪制出游戲區域、蛇和食物等圖形元素,根據游戲引擎模塊返回的數據更新分數和其他游戲狀態信息。液晶屏的分辨率和色彩深度等參數會影響顯示效果,所以在開發時需要根據硬件選型和資源需求進行平衡。顯示模塊程序設計流程圖如圖4.3所示:
顯示模塊部分程序如下所示:
void Lcd_Show_Board() //LCD顯示墻壁函數
{uchar n;for(n=0;n<31;n++){Lcd_Rectangle(3*n,0,3*n+2,2,1);}for(n=0;n<21;n++){Lcd_Rectangle(0,3*n,2,3*n+2,1);Lcd_Rectangle(90,3*n,92,3*n+2,1); }
}
void Lcd_Show_Title() //液晶顯示貪吃蛇漢字
{Lcd_Show_String(7,0,"貪"); //顯示漢字Lcd_Show_String(7,1,"吃");Lcd_Show_String(7,2,"蛇");
}
4.4?輸入處理模塊程序設計
? ? ? 輸入處理模塊負責讀取用戶按鍵操作的輸入,并將其傳遞給游戲引擎模塊進行處理。根據設計,它可以通過矩陣按鍵掃描,實現對四個方向的控制和其他額外的功能鍵,比如開始、暫停、調速等命令。輸入處理模塊需要防止按鍵的抖動和誤操作,因此一般采用掃描或者中斷的方式實現。顯示模塊程序設計流程圖如圖4.4所示,輸入處理模塊部分程序如下所示。
uchar KeyBoard() /* 反轉法鍵盤掃描 */
{uchar temp1,temp2,temp,a=0;P1=0xf0; /* 輸入行值(或列值) */Delay_MS(1) ; /* 延時 */temp1=P1; /* 讀列值(或行值) */P1=0xff;Delay_MS(1); /* 延時 */P1=0x0f; /* 輸入列值(或行值) */Delay_MS(1) ; /* 延時 */ temp2=P1; /* 讀行值(或列值) */P1=0xff;temp=(temp1&0xf0)|(temp2&0xf); /* 將兩次讀入數據組合 */switch(temp) /* 通過讀入數據組合判斷按鍵位置 */{ case 0xdd :a=20;break; // 按鍵上case 0x7d :a=40;break;// 按鍵下case 0xbe :a=30;break; // 按鍵左case 0xbb :a=10;break; // 按鍵右default :a=0;}return a; /* 返回按鍵值 */
}
5?系統仿真結果
5.1 游戲引擎模塊的實現
? ? ? ?游戲引擎模塊是貪吃蛇游戲的核心,它負責控制游戲的狀態和邏輯。系統開始時,進入游戲運行界面。效果如圖5.1所示:
5.2 顯示模塊功能的實現
? ? ? ?液晶顯示模塊用于顯示游戲界面和相關信息,可以在屏幕上繪制游戲區域、分數以及游戲狀態等內容,實現效果如圖5.2所示。
5.3 輸入處理模塊的實現
輸入處理模塊負責讀取用戶按鍵操作的輸入,可以通過矩陣按鍵掃描,實現對四個方向的控制和其他額外的功能鍵,比如開始、暫停等命令實現效果如圖5.3所示。
演示視頻:基于51單片機的貪吃蛇游戲設計演示視頻-CSDN直播
?完整代碼:
#include<reg51.h> //加載頭文件
#include<intrins.h>
#define uchar unsigned char //宏定義
#define uint unsigned int
#define ulong unsigned long
#define A 48271L //參數宏定義
#define M 2147483647L
#define Q (M/A)
#define R (M%A)
#define N 25
#define FUNC 1
#define UP 2
#define DOWN 3
#define LEFT 4
#define RIGHT 5
#define PASSSCORE 20 //預定義過關成績
#define SHORT_ON_DITHERING_COUNTER 3//定義短按按下去抖時間
#define SHORT_OFF_DITHERING_COUNTER 3//定義短按松開去抖時間,一般與短按按下去抖時間相同
#define LCD_DATA P0 //液晶數據口定義
sbit LCD_RS=P2^0; //液晶并行的指令/數據選擇信號, H數據, L命令
sbit LCD_RW=P2^1; //液晶并行讀寫選擇信號, H讀, L寫
sbit LCD_EN=P2^2; //液晶并行使能端, H有效, L無效
sbit KEY_UP=P3^4; //按鍵上
sbit KEY_RIGHT=P3^5; //按鍵右
sbit KEY_LEFT=P3^6; //按鍵左
sbit KEY_DOWN=P3^7; //按鍵下
static ulong Seed=1; //變量聲明
bit Run=0;
uchar Flag=0;
uchar Score=0;
uchar Speed=5;
uchar KeyBuffer=0;
code uint MaskTab[]={ //為加速邏輯運算而設置的掩碼表,這是以犧牲空間而換取時間的辦法
0x0001,0x0002,0x0004,0x0008,0x0010,0x0020,0x0040,0x0080,
0x0100,0x0200,0x0400,0x0800,0x1000,0x2000,0x4000,0x8000};
struct Food
{uchar x;uchar y;uchar yes;
}food; //食物結構體
struct Snake
{uchar x[N];uchar y[N];uchar node;uchar direction;uchar life;
}snake; //蛇結構體
double Random() //偽隨機數發生器
{long TmpSeed;TmpSeed=A*(Seed%Q)-R*(Seed/Q);if(TmpSeed>=0)Seed=TmpSeed;elseSeed=TmpSeed+M;return (double)Seed/M;
}
void InitRandom(ulong InitVal) //為偽隨機數發生器播種
{Seed=InitVal;
}
void Delay(uint t) //延時子程序
{ uint i,j;for(i=0;i<t;i++)for(j=0;j<10;j++);
}
void Delay_MS(uint z) //z*1MS延時函數
{uint x,y;for(x=z;x>0;x--)for(y=110;y>0;y--);
}
void Timer_Init() //定時器初始化
{TMOD=0x00; //定時器工作方式TH0=0; //定時器賦初值TL0=0;ET0=1; //允許定時器0中斷EA=1; //開總中斷TR0=1; //定時器0啟動
}
void Lcd_W_Com(uchar com) //液晶寫指令函數
{ LCD_RS=0;LCD_RW=0;LCD_EN=0;_nop_(); _nop_();LCD_DATA=com;_nop_(); _nop_();LCD_EN=1;_nop_(); _nop_();LCD_EN=0;
}
void Lcd_W_Dat(uchar dat) //液晶寫數據函數
{ LCD_RS=1;LCD_RW=0;LCD_EN=0;_nop_(); _nop_(); LCD_DATA=dat;LCD_EN=1;_nop_();_nop_();LCD_EN=0;
}
uchar Lcd_R_Dat() //液晶讀數據函數
{uchar Temp;LCD_DATA=0xff;LCD_RS=1;LCD_RW=1;LCD_EN=1;_nop_();Temp=LCD_DATA;LCD_EN=0;return Temp;
}
void Lcd_Show_String(uchar x,uchar y,uchar *Str) //液晶在某個位置顯示字符串函數
{if((y>3)||(x>7))return; //如果指定位置不在顯示區域內,則不做任何寫入直接返回EA=0; //關總中斷switch(y){case 0: //第一行Lcd_W_Com(0x80+x);break;case 1: //第二行Lcd_W_Com(0x90+x);break; case 2: //第三行Lcd_W_Com(0x88+x);break;case 3: //第四行Lcd_W_Com(0x98+x);break;}while(*Str>0){ Lcd_W_Dat(*Str); //寫入字符串Str++; }EA=1; //開總中斷
}
void Lcd_PutPixel(uchar x,uchar y,uchar Color) //向LCD指定坐標寫入一個象素,象素顏色有兩種,0代表白(無顯示),1代表黑(有顯示)
{uchar z,w;uint Temp;if(x>=128||y>=64)return;Color=Color%2;w=15-x%16; //確定對這個字的第多少位進行操作x=x/16; //確定為一行上的第幾字if(y<32) //如果為上頁z=0x80;else //否則如果為下頁z=0x88;y=y%32;EA=0;Lcd_W_Com(0x36);Lcd_W_Com(y+0x80); //行地址Lcd_W_Com(x+z); //列地址 Temp=Lcd_R_Dat(); //先空讀一次Temp=(uint)Lcd_R_Dat()<<8; //再讀出高8位Temp|=(uint)Lcd_R_Dat(); //再讀出低8位EA=1;if(Color==1) //如果寫入顏色為1Temp|=MaskTab[w]; //在此處查表實現加速else //如果寫入顏色為0Temp&=~MaskTab[w]; //在此處查表實現加速EA=0;Lcd_W_Com(y+0x80); //行地址Lcd_W_Com(x+z); //列地址Lcd_W_Dat(Temp>>8); //先寫入高8位,再寫入低8位Lcd_W_Dat(Temp&0x00ff);Lcd_W_Com(0x30);EA=1;
}
void Lcd_HoriLine(uchar x,uchar y,uchar Length,uchar Color) //向LCD指定位置畫一條長度為Length的指定顏色的水平線
{uchar i;if(Length==0)return;for(i=0;i<Length;i++)Lcd_PutPixel(x+i,y,Color);
}
void Lcd_VertLine(uchar x,uchar y,uchar Length,uchar Color) //向LCD指定位置畫一條長度為Length的指定顏色的垂直線
{uchar i;if(Length==0)return;for(i=0;i<Length;i++)Lcd_PutPixel(x,y+i,Color);
}
void Lcd_Rectangle(uchar x0,uchar y0,uchar x1,uchar y1,uchar Color) //向LCD指定左上角坐標和右下角坐標畫一個指定顏色的矩形
{uchar Temp;if(x0>x1){Temp=x0;x0=x1;x1=Temp;} if(y0>y1){Temp=y0;y0=y1;y1=Temp;}Lcd_VertLine(x0,y0,y1-y0+1,Color);Lcd_VertLine(x1,y0,y1-y0+1,Color);Lcd_HoriLine(x0,y0,x1-x0+1,Color);Lcd_HoriLine(x0,y1,x1-x0+1,Color);
}
void Lcd_Clear(uchar Mode) //清除Lcd全屏,如果清除模式Mode為0,則為全屏清除為顏色0(無任何顯示)
{ //否則為全屏清除為顏色1(全屏填充顯示)uchar x,y,ii;uchar Temp;if(Mode%2==0)Temp=0x00;elseTemp=0xff;Lcd_W_Com(0x36); //擴充指令 繪圖顯示for(ii=0;ii<9;ii+=8) for(y=0;y<0x20;y++) for(x=0;x<8;x++){ EA=0; //關總中斷Lcd_W_Com(y+0x80); //行地址Lcd_W_Com(x+0x80+ii); //列地址 Lcd_W_Dat(Temp); //寫數據 D15-D8 Lcd_W_Dat(Temp); //寫數據 D7-D0 EA=1; //開總中斷}Lcd_W_Com(0x30);
}
void Lcd_Init() //LCD液晶初始化
{ Lcd_W_Com(0x30); //選擇基本指令集Lcd_W_Com(0x0c); //開顯示(無游標、不反白)Lcd_W_Com(0x01); //清除顯示,并且設定地址指針為00HLcd_W_Com(0x06); //指定在資料的讀取及寫入時,設定游標的移動方向及指定顯示的移位
}//************************************************************************/
// 描述: 反轉法鍵盤掃描
//************************************************************************/
uchar KeyBoard() /* 反轉法鍵盤掃描 */
{uchar temp1,temp2,temp,a=0;P1=0xf0; /* 輸入行值(或列值) */Delay_MS(1) ; /* 延時 */temp1=P1; /* 讀列值(或行值) */P1=0xff;Delay_MS(1); /* 延時 */P1=0x0f; /* 輸入列值(或行值) */Delay_MS(1) ; /* 延時 */ temp2=P1; /* 讀行值(或列值) */P1=0xff;temp=(temp1&0xf0)|(temp2&0xf); /* 將兩次讀入數據組合 */switch(temp) /* 通過讀入數據組合判斷按鍵位置 */{ case 0xdd :a=20;break; // 按鍵上case 0x7d :a=40;break;// 按鍵下case 0xbe :a=30;break; // 按鍵左case 0xbb :a=10;break; // 按鍵右default :a=0;}return a; /* 返回按鍵值 */
}void Exint_Init()
{IT0 = 1;EX0 = 1;EA = 1;
}/*********外部中斷0服務函數***********/
void Exint0_Service() interrupt 0
{Run=~Run;
}uchar Key_Read() //讀取按鍵動作函數
{ //沒有按鍵動作,則返回0,static uchar KeyEventCnt=0; //1號按鍵動作,返回1-4static uchar KeySampleCnt=0; //2號按鍵動作,返回5-8,如此類推static uchar KeyBuffer=0; //返回1、5、9、13:確認短按按下uchar KeyTemp; //返回2、6、10、14:確認長按按下KeyTemp=KeyBoard(); //返回3、7、11、15:確認短按松開switch(KeyEventCnt) //返回4、8、12、16:確認長按松開{case 0:if(KeyTemp!=0){KeySampleCnt=0;KeyBuffer=KeyTemp;KeyEventCnt=1; }return 0; //沒有按下,return 0break;case 1:if(KeyTemp!=KeyBuffer){KeyEventCnt=0;return 0; //抖動,return 0}else{if(++KeySampleCnt>=SHORT_ON_DITHERING_COUNTER){KeySampleCnt=0;KeyEventCnt=2;return KeyBuffer;}elsereturn 0; //不確定按下,return 0 }break;case 2:if(KeyTemp!=KeyBuffer){if(++KeySampleCnt>=SHORT_OFF_DITHERING_COUNTER){KeyEventCnt=0;return KeyBuffer+3;}elsereturn 0;}else{KeySampleCnt=0;return 0;}break;default:break;}return 0;
}
void Lcd_Show_Board() //LCD顯示墻壁函數
{uchar n;for(n=0;n<31;n++){Lcd_Rectangle(3*n,0,3*n+2,2,1);Lcd_Rectangle(3*n,60,3*n+2,62,1);}for(n=0;n<21;n++){Lcd_Rectangle(0,3*n,2,3*n+2,1);Lcd_Rectangle(90,3*n,92,3*n+2,1); }
}
void Lcd_Show_Score() //液晶顯示分數函數
{uchar Str[3];Lcd_Show_String(6,1,"分"); //顯示漢字Lcd_Show_String(6,2,"數");Str[0]=(Score/10)|0x30; //十位Str[1]=(Score%10)|0x30; //個位Str[2]=0;Lcd_Show_String(6,3,Str); //液晶顯示分數
}
void Lcd_Show_Title() //液晶顯示貪吃蛇漢字
{Lcd_Show_String(7,0,"貪"); //顯示漢字Lcd_Show_String(7,1,"吃");Lcd_Show_String(7,2,"蛇");
}
void Game_Over() //游戲結束處理
{uchar n;Lcd_Rectangle(food.x,food.y,food.x+2,food.y+2,0); //消隱出食物for(n=1;n<snake.node;n++)Lcd_Rectangle(snake.x[n],snake.y[n],snake.x[n]+2,snake.y[n]+2,0);//消隱食物,蛇頭已到墻壁內,故不用消去 if(snake.life==0) //如果蛇還活著Lcd_Show_String(2,1,"過關");else //如果蛇死了Lcd_Show_String(2,1,"輸了");Lcd_Show_String(1,2,"游戲結束");
}
void Game_Play() //游戲的具體過程,也是貪吃蛇算法的關鍵部分
{uchar n;InitRandom(TL0);food.yes=1; //1表示需要出現新事物,0表示已經存在食物尚未吃掉snake.life=0; //表示蛇還活著snake.direction=DOWN;snake.x[0]=6;snake.y[0]=6;snake.x[1]=3;snake.y[1]=6;snake.node=2;Lcd_Show_Score(); //顯示分數Lcd_Show_Title(); //顯示標題while(1){if(food.yes==1){while(1){food.x=Random()*85+3;food.y=Random()*55+3; //獲得隨機數while(food.x%3!=0)food.x++;while(food.y%3!=0)food.y++;for(n=0;n<snake.node;n++) //判斷產生的食物坐標是否和蛇身重合{if((food.x==snake.x[n])&&(food.y==snake.y[n]))break;}if(n==snake.node){food.yes=0;break; //產生有效的食物坐標}}}if(food.yes==0){Lcd_Rectangle(food.x,food.y,food.x+2,food.y+2,1);} if(Run==0){for(n=snake.node-1;n>0;n--){snake.x[n]=snake.x[n-1];snake.y[n]=snake.y[n-1];} switch(snake.direction){case DOWN:snake.x[0]+=3;break;case UP:snake.x[0]-=3;break;case RIGHT:snake.y[0]-=3;break;case LEFT:snake.y[0]+=3;break;default:break;}for(n=3;n<snake.node;n++) //從第三節開始判斷蛇頭是否咬到自己{if(snake.x[n]==snake.x[0]&&snake.y[n]==snake.y[0]){Game_Over();snake.life=1;break;}}}if(snake.x[0]<3||snake.x[0]>=90||snake.y[0]<3||snake.y[0]>=60)//判蛇頭是否撞到墻壁{Game_Over();snake.life=1;}if(snake.life==1)break; //蛇死,則跳出while(1)循環if(snake.x[0]==food.x&&snake.y[0]==food.y) //判蛇是否吃到食物{Lcd_Rectangle(food.x,food.y,food.x+2,food.y+2,0); //消隱食物snake.x[snake.node]=200;snake.y[snake.node]=200;//產生蛇新的節坐標先放在看不見的位置snake.node++; //蛇節數加1food.yes=1; //食物標志置1if(++Score>=PASSSCORE){Lcd_Show_Score();Game_Over();break;}Lcd_Show_Score();}for(n=0;n<snake.node;n++){Lcd_Rectangle(snake.x[n],snake.y[n],snake.x[n]+2,snake.y[n]+2,1);} //根據蛇的節數畫出蛇Delay(Speed*1000);Lcd_Rectangle(snake.x[snake.node-1],snake.y[snake.node-1],snake.x[snake.node-1]+2,snake.y[snake.node-1]+2,0);switch(KeyBuffer){case FUNC:KeyBuffer=0;if(++Speed>=10)Speed=1;Lcd_Show_Title();break;case DOWN: //下KeyBuffer=0;if(snake.direction!=UP)snake.direction=DOWN;break;case UP: //上KeyBuffer=0;if(snake.direction!=DOWN)snake.direction=UP;break;case RIGHT: //右KeyBuffer=0;if(snake.direction!=LEFT)snake.direction=RIGHT;break;case LEFT: //左KeyBuffer=0;if(snake.direction!=RIGHT)snake.direction=LEFT;break;default:break;} }
}
void Main() //主函數
{
start:Score=0;Timer_Init(); //定時器初始化Exint_Init();Lcd_Init(); //液晶初始化Lcd_Clear(0); //液晶清屏Lcd_Show_Board(); //液晶顯示墻壁Game_Play(); //玩游戲Game_Over(); //游戲結束 Delay_MS(4000);goto start;
}
void Timer0_ISR() interrupt 1 //定時器0中斷處理函數
{switch(Key_Read()){case 90:KeyBuffer=FUNC;if(++Speed>=10)Speed=1;Flag|=1<<1; //速度變化標志置1break;case 13:KeyBuffer=DOWN;break;case 33:KeyBuffer=UP;break;case 23:KeyBuffer=RIGHT;break;case 43:KeyBuffer=LEFT;break;default:break;}
}
仿真源文件、源程序及設計報告百度網盤鏈接:https://pan.baidu.com/s/1C4da0C0fJHio73F4eFVChg?pwd=q1ef? ?提取碼: q1ef?