【使用51单片机驱动1.54英寸墨水屏做一个简洁的贪吃蛇游戏】
【使用51单片机驱动1.54英寸墨水屏做一个简洁的贪吃蛇游戏】
使用51单片机驱动1.54英寸墨水屏做一个简洁的贪吃蛇游戏
- 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)。
3.程序流程
贪吃蛇游戏涉及到蛇的移动、食物生成、碰撞检测、方向控制和界面输出。所以整个程序的设计可以按照以下步骤:
1.游戏初始化
2.生成食物坐标
3.获取前进方向
4.蛇的坐标更新
5.碰撞检测(墙壁,蛇身和实物)
6.输出界面
大致的程序流程:
3.1 游戏初始化
游戏一开始我设计的是蛇的长度为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 <stdlib.h>
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<snake_len;i++)if(food_xy[0] == snake_xy[i][0])ture = 0;}ture = 0;while(!ture){ture = 1;food_xy[1] = (rand()%22)+1; //1-23for(i=0;i<snake_len;i++)if(food_xy[1] == snake_xy[i][1])ture = 0;}eat = 0;}
}
3.3 获取前进方向
在主函数中一直循环获取按键键值,更新前进方向:
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次,多次和长短按键
3.4 蛇的坐标更新
先保存下蛇尾的坐标,后面碰撞检测的时候如果吃到食物需要用于增加蛇身长度。
然后,从后往前更新蛇身坐标。
最后,按照前进方向更新蛇头坐标。
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<snake_len;i++)
// if((snake_xy[0][0]==snake_xy[i][0]) && (snake_xy[0][1]==snake_xy[i][1])) //蛇头碰撞蛇身
// gemeover = 1;if(snake_xy[0][0]==food_xy[0] && snake_xy[0][1]==food_xy[1]) //吃到食物{eat = 1;snake_len++;snake_xy[snake_len-1][0] = sanke_tempxy[0]; //尾巴增加snake_xy[snake_len-1][1] = sanke_tempxy[1];if(snake_len==20)gemeover = 2; //通关}
}
3.6 输出界面
通过游戏坐标,这边调用的是墨水屏的显示函数,游戏用在其他地方的话可以忽略这节。
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<snake_len;i++) //显示蛇身EPD_Dis_Part(snake_xy[i][0]*8,snake_xy[i][1]*8,gImage_main_1,8,8,OFF); EPD_Dis_Part(food_xy[0]*8,food_xy[1]*8,gImage_main_1,8,8,OFF); //显示食物}EPD_Part_Update();
}
3.7 主函数
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倍的速度。本文如果对你有帮助的话就点个赞吧👍。