目录

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>…