基于51單片機的貪吃蛇游戲Protues仿真設計

目錄

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?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/88946.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/88946.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/88946.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

HTML+CSS

一、HTML相關內容- <img> 標簽&#xff1a;- 用于在網頁中嵌入圖像&#xff0c; src 屬性指定圖像的路徑&#xff0c;可以是絕對路徑&#xff08;如 D:\Git\java115_java116\課堂代碼\前端代碼\pic\cat.jpg &#xff09;、相對路徑&#xff08;如 ./pic/cat.jpg &#x…

基于 Gitlab、Jenkins與Jenkins分布式、SonarQube 、Nexus 的 CiCd 全流程打造

前言 在當今數字化飛速發展的時代&#xff0c;軟件開發與交付的效率和質量成為了企業競爭的關鍵要素。為了滿足市場對軟件快速迭代和高質量交付的需求&#xff0c;越來越多的企業開始探索和實踐持續集成與持續交付&#xff08;CI/CD&#xff09;的開發模式。而 GitLab、Jenkin…

[密碼學實戰]密評相關題庫解析

[密碼學實戰]密評相關題庫解析 一、背景 依據《密碼法》第二十二條&#xff0c;關鍵信息基礎設施&#xff08;關基&#xff09;運營者必須開展商用密碼應用安全性評估&#xff0c;且需定期進行&#xff08;不少于每年一次&#xff09;。 二、核心解析 2.1 測評標準框架&#x…

谷歌開源庫gflags詳細說明

目錄 一.gflags 介紹 二.gflags安裝 三.gflags使用 1.包含頭文件 2.定義參數 3.訪問參數 4.不同文件訪問參數 5.初始化所有參數 6.運行參數設置 7.配置文件的使用 8.特殊參數標識 四.總結 一.gflags 介紹 gflags 是 Google 開發的一個開源庫&#xff0c;用于 C 應用…

Python爬蟲實戰:研究XlsxWriter 庫相關技術

1. 研究背景與意義 1.1 網絡爬蟲技術價值 網絡爬蟲作為數據采集的核心工具,在金融、醫療、教育等領域發揮關鍵作用。據 Statista 數據顯示,2025 年全球大數據市場規模預計達 3250 億美元,高效的數據獲取能力成為企業核心競爭力。Python 以其 80% 的市場占有率成為爬蟲開發首…

ThreadLocal內部結構深度解析(Ⅰ)

目錄 使用ThreadLocal 例子 內部結構分析 源碼解析 圖示詳解 ThreadLocal是Java中一個非常重要且常用的線程局部變量工具類&#xff0c;它使得每個線程可以獨立地持有自己的變量副本&#xff0c;而不是共享變量&#xff0c;解決了多線程環境下變量共享的線程安全問題。下面我…

Python 數據挖掘之數據探索

在數據挖掘的流程中&#xff0c;數據探索是非常關鍵的第一步&#xff0c;它能幫助我們深入了解數據的特點&#xff0c;為后續的預處理和模型構建打下堅實的基礎。我們主要圍繞四個方面展開&#xff1a;數據對象與特征、數據統計描述、數據可視化以及相關性和相似性度量。一、數…

高并發點贊場景Synchronized、AtomicLong、LongAdder 和 LongAccumulator性能分析

在高并發點贊場景中&#xff0c;我們需要一個高效、線程安全的計數器來記錄點贊數。synchronized、AtomicLong、LongAdder 和 LongAccumulator 都是 Java 中用于實現原子操作的類&#xff0c;但它們的性能在高并發下差異顯著。性能主要取決于線程競爭程度&#xff1a;競爭越高&…

postgreSQL的sql語句

目錄 一&#xff1a;前提準備1.postgreSQL的安裝可以參考我下面一片文章&#xff1a; 二&#xff1a;SQL語句 1.相同點&#xff1a;支持標準sql類型 2.參考詳細學習地址&#xff1a; 3.postgresql與mysql的不同點 一&#xff1a;前提準備 1.postgreSQL的安裝可以參考我下面…

vue3 JavaScript 數據累加 reduce

在Vue 3中&#xff0c;你可以使用JavaScript的reduce方法來處理數據累加。reduce方法通常用在數組上&#xff0c;它將數組中的每個元素通過一個累加器函數&#xff08;accumulator&#xff09;從左到右累積&#xff0c;最終生成一個單一的值。這在計算總和、累加值等場景中非常…

史上最清楚!讀者,寫者問題(操作系統os)

讀者-寫者問題是另一個里程碑式的同步互斥問題。它比生產者-消費者更復雜&#xff0c;因為它引入了不對稱的訪問權限&#xff1a;讀者和讀者之間是共享的&#xff0c;但寫者和任何人&#xff08;包括讀者和其他寫者&#xff09;之間都是互斥的。我們用一個生動的比喻來解析這個…

使用Starrocks替換Clickhouse的理由

背景 Starrocks和clickhouse都是非常優秀的OLAP數據庫&#xff0c;那么什么情況下使用clickhouse&#xff0c;什么場景下使用starrocks呢&#xff0c;本文就簡單列舉一下他們的優缺點 理由 首先兩者都是列存儲&#xff0c;并且都實現了列壓縮&#xff0c;所以從存儲中兩者的壓縮…

Mybatis 兩級緩存可能導致的問題

Mybatis 兩級緩存可能導致的問題兩級緩存簡介一級緩存 localCache效果開關二級緩存兩級緩存可能導致的問題分布式環境下查詢到過期數據事務隔離級別失效讀已提交失效讀未提交失效總結兩級緩存簡介 一級緩存 localCache 效果 一級緩存是 session 或者說事務級別的&#xff0c…

vue3+uniapp 使用vue-plugin-hiprint中實現打印效果

前言&#xff1a; vue3uniapp 使用vue-plugin-hiprint中實現打印效果 官網地址&#xff1a;gitee https://gitee.com/ccsimple/vue-plugin-hiprinthttps://gitee.com/ccsimple/vue-plugin-hiprint 實現效果&#xff1a; 預覽打印內容&#xff1a; 實現步驟&#xff1a; 1、安…

【elementUI踩坑記錄】解決 el-table 固定列 el-table__fixed 導致部分滾動條無法拖動的問題

目錄一、問題背景二、 問題現象三、核心原因四、解決辦法增強方案&#x1f680;寫在最后一、問題背景 在使用 Element UI 的 el-table 組件時&#xff0c;固定列功能雖然實用&#xff0c;但會引發滾動條交互問題&#xff1a; 固定列區域懸浮顯示滾動條但無法正常拖動滾動條 …

【機器人編程基礎】python文件的打開和關閉

文件的打開和關閉 在Python中,文件操作是一項基本而重要的任務,涉及到打開、讀取、寫入、關閉文件等操作。正確地管理文件對于數據持久化、輸入輸出處理等至關重要。下面將詳細解釋如何在Python中打開和關閉文件,并提供相應的代碼示例。 文件打開 在Python中,可以使用內…

ShenYu實戰、問題記錄

概述 一款高性能的國產的Apache開源API網關&#xff0c;官方文檔。 在ShenYu v2.6.1, ShenYu注冊中心只支持http類型&#xff0c;中間件注冊類型已經被移除。 所以&#xff0c;請使用http注冊類型來注冊你的服務。不是微服務注冊中心&#xff0c;它只是將元數據、選擇器數據、…

走近科學IT版:EasyTire設置了ip,但是一閃之后就變回到原來的dhcp獲得的地址

EasyTier 是一款簡單、安全、去中心化的內網穿透和異地組網工具&#xff0c;適合遠程辦公、異地訪問、游戲加速等多種場景。無需公網 IP&#xff0c;無需復雜配置&#xff0c;輕松實現不同地點設備間的安全互聯。 上次實踐的記錄&#xff1a;適合遠程辦公、異地訪問的EasyTier…

rk3588平臺USB 3.0 -OAK深度相機適配方法

目錄 文件更改記錄表 1、usb規則添加 2、拉取相關依賴 3、安裝python3、安裝pip 4、安裝依賴 5、安裝ffmeg 6、攝像頭功能測試 7、將視頻拷貝到U盤查看 1、usb規則添加 由于OAK是USB設備,因此為了在使用 udev 工具的系統上與之通信, 您需要添加udev規則以使…

工廠模式總結

工廠模式1. 簡單工廠模式&#xff08;Simple Factory&#xff09; 核心思想 定義一個工廠類&#xff0c;根據輸入參數創建不同的具體對象。客戶端不直接調用具體類的構造函數&#xff0c;而是通過工廠類獲取對象。 示例代碼 #include <iostream> #include <memory>…