1.硬件信息2.界面设计3.程序流程
3.1 游戏初始化3.2 生成食物坐标3.3 获取前进方向3.4 蛇的坐标更新3.5 碰撞检测3.6 输出界面3.7 主函数 4 结果展示 1.硬件信息
本文使用的是STC15W408AS型号的51单片机,自己做的一个带按键的小系统,显示屏使用的是某宝上买的1.54英寸的墨水屏,一共有200 * 200个像素点。所以整个游戏界面的坐标按照200 * 200进行设计。
2.界面设计 既然是简洁版的贪吃蛇,那么显示界面就一切从简了,如下图所示。只要搞懂了基本的设计思路在此基础上还是可以搞复杂的。(0_0)
首先是游戏场地,在四周是宽度为8个像素的墙壁,如果蛇头碰撞到墙壁那么游戏结束,也就是说蛇头的坐标超出规定范围就游戏结束。
为方便计算,在原本200 * 200像素的基础上,我把8 * 8作为一个游戏像素,也就是映射为25 * 25个像素。把8 * 8像素的左上角作为坐标的话,蛇的活动范围就在(1,1)到(23,23)。
贪吃蛇游戏涉及到蛇的移动、食物生成、碰撞检测、方向控制和界面输出。所以整个程序的设计可以按照以下步骤:
1.游戏初始化
2.生成食物坐标
3.获取前进方向
4.蛇的坐标更新
5.碰撞检测(墙壁,蛇身和实物)
6.输出界面
大致的程序流程:
游戏一开始我设计的是蛇的长度为3,初始方向向右。由于51单片机不好实现链表,这里直接用数值存储蛇的每个游戏像素坐标。本文设计最大长度为20,超过20游戏结束。以下是相关的变量初始化。
u8 snake_len=3;//蛇的长度u8 snake_xy[20][2]={{5,3},{4,3},{3,3}};//初始蛇坐标u8 sanke_tempxy[2]={5,3};//保存尾坐标char direction_xy[2]={1,0};//前进方向u8 food_xy[2]={4,5};//食物坐标u8 gemeover=0;//0位继续 1死亡结束 2通过结束 bit eat=0;//是否吃到食物
其中前进方向用在后面的蛇头坐标计算中。
3.2 生成食物坐标 使用rand()函数生成1到23的随机数作为食物的生成坐标,且生成的坐标不能和蛇身重合。
需要包含头文件 #include
void Food_GetXY(void){u8 ture=0; //生成的值不能和蛇身坐标重合u8 i=0;if(eat){while(!ture){ture = 1;food_xy[0] = (rand()%22)+1;//1-23for(i=0;i
在主函数中一直循环获取按键键值,更新前进方向:
key_get = Key_Scan2();if(key_get){switch(key_get){case 1:{direction_xy[0] = 0;direction_xy[1] = 1;break;}case 2:{direction_xy[0] = 1;direction_xy[1] = 0;break;}case 4:{direction_xy[0] = -1;direction_xy[1] = 0;break;}case 8:{direction_xy[0] = 0;direction_xy[1] = -1;break;}}key_get = 0;}
其中的按键获取函数是无去抖等待的,需要的可以参考:
STM32定时器实现不加延时的三种独立按键获取函数——返回1次,多次和长短按键
先保存下蛇尾的坐标,后面碰撞检测的时候如果吃到食物需要用于增加蛇身长度。
然后,从后往前更新蛇身坐标。
最后,按照前进方向更新蛇头坐标。
void Snake_UpdateXY(void){u8 i=0;sanke_tempxy[0] = snake_xy[snake_len-1][0];//保存尾坐标sanke_tempxy[1] = snake_xy[snake_len-1][1];for(i=snake_len;i>0;i--)//从后往前更新蛇身坐标{snake_xy[i][0] = snake_xy[i-1][0];snake_xy[i][1] = snake_xy[i-1][1];}snake_xy[0][0] += direction_xy[0];//按照前进方向更新蛇头坐标snake_xy[0][1] += direction_xy[1];}
3.5 碰撞检测超出游戏界面范围的话游戏结束,蛇身超过20游戏通过。
void Snake_Crash(void)//碰撞判断{u8 i=0;if((snake_xy[0][0]>23 || snake_xy[0][0]<1)||(snake_xy[0][1]>23 || snake_xy[0][1]<1))//蛇头碰到边框gemeover = 1;//for(i=1;i
通过游戏坐标,这边调用的是墨水屏的显示函数,游戏用在其他地方的话可以忽略这节。
void Show_Updaet(void){u8 i=0;EPD_Dis_Part(0,0,gImage_main_1,200,8,OFF); //边框EPD_Dis_Part(192,8,gImage_main_1,8,192,OFF); EPD_Dis_Part(0,8,gImage_main_1,8,192,OFF); EPD_Dis_Part(8,192,gImage_main_1,184,8,OFF); if(gemeover==1){EPD_Dis_Part(110,72,gImage_num[4],35,56,NEG); //失败界面EPD_Dis_Part(150,72,gImage_num[4],35,56,NEG); }else if(gemeover==2){EPD_Dis_Part(110,72,gImage_num[6],35,56,NEG); //通关界面EPD_Dis_Part(150,72,gImage_num[6],35,56,NEG); }else{for(i=0;i
int main(){//uchar i;u8 key_get=0;system_init();while(1){if(gemeover)//游戏结束{return 0;}key_get = Key_Scan2();//获取按键if(key_get){switch(key_get){//获取前进方向case 1:{direction_xy[0] = 0;direction_xy[1] = 1;break;}case 2:{direction_xy[0] = 1;direction_xy[1] = 0;break;}case 4:{direction_xy[0] = -1;direction_xy[1] = 0;break;}case 8:{direction_xy[0] = 0;direction_xy[1] = -1;break;}}key_get = 0;}if(system_ctr&0x01 && !isEPD_W21_BUSY)//每隔500ms进一次进行游戏更新{Show_clear_snake();//清屏Food_GetXY();//生成食物坐标Snake_UpdateXY();//蛇的坐标更新Snake_Crash();//碰撞检测Show_Updaet();//输出界面system_ctr &= ~0x01;}}return 0;}
4 结果展示 最后来看下效果吧,这个墨水屏更新显示太慢了,导致游戏不能快点刷新,体验极差(0-0),显示的动图这是快了4倍的速度。本文如果对你有帮助的话就点个赞吧。