找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 6564|回复: 5
收起左侧

基于STM32的贪吃蛇游戏设计(程序+硬件PCB文件+视频)

  [复制链接]
ID:197252 发表于 2019-4-12 22:14 | 显示全部楼层 |阅读模式
      学习嵌入式已经有两年多了,现在准备工作了,也已经从51单片机过度到ARM的STM32单片机,就想结合自己所学的东西做一个东西出来,于是就做了这个贪吃蛇游戏。
       本贪吃蛇游戏是的硬件CPU是STM32F103C8T6、显示液晶用的是诺基亚Nokia5510的屏幕、按键、电源稳压模块。
       为什么选择STM32,因为STM32的主频时钟高达72M,说白了就是速度快,内部资源丰富和大容量的flash和动态内存。液晶不选用LCD12864,本人自己认为那个蓝光屏幕看久了会眼花而且还有点笨重(纯属个人意见)。
       怎么在程序上实现贪吃蛇游戏设计呢,在这里感谢网上的个位大佬,核心程序基本上是借鉴的过来了。核心程序解决了,怎么显示在液晶上了,第一首先写一个画点函数,就是输入坐标就可以在对应的坐标点亮。nokia5510是84*48的像素点屏幕。其他的按键驱动函数就不在描述了。主要是本作者的表达能力不是很好,不在啰嗦了,直接上程序吧!

制作出来的贪吃蛇实物图如下:

运行结果(为了防腐蚀涂上了绿油)

运行结果(为了防腐蚀涂上了绿油)

焊接好STM32最小系统

焊接好STM32最小系统

实物

实物


下面链接是演示视频:
https://v.youku.com/v_show/id_XNDEzNTI4ODk0NA==.html

Altium Designer画的PCB图如下:(51hei附件中可下载工程文件)
0.png

STM32单片机源程序如下:
  1. #include "lcd_5510.h"
  2. #include "Snake_Game.h"

  3. #define ElemType int
  4.         
  5. typedef struct node
  6. {
  7.     ElemType x,y;
  8.     struct node *prior;
  9.     struct node *next;
  10. } Node;

  11. Food food;                                        //全局变量//
  12. int add=10;                                        //总得分与每次吃食物得分//蛇长度计数;。

  13. u8 sleeptime=200;                //每次运行的时间间隔
  14. Snake snake;
  15. Node *Head;                                        //Node*food;//蛇头指针,食物指针
  16. u8 endgamestatus=0; //游戏结束的情况,1:撞到墙;2:咬到自己;3:主动退出游戏。

  17. //按键判断
  18. Fuct KEY_Test(void)
  19. {
  20.     Fuct sta=Null;
  21.     if(UP_ON)    sta = Up;
  22.     if(Down_ON)  sta =  Down;
  23.     if(Left_ON)  sta =  Left;
  24.     if(Right_ON) sta =  Right;
  25.     if(MODE_ON)         sta =  Puase;
  26.     if(RTN_ON)         sta =  Rtn;
  27.     return sta;
  28. }



  29. // 新建节点。成功,返回节点指针;否则,返回NULL。
  30. static Node* CreateNode()
  31. {
  32.     Node *newNode = NULL;
  33.     newNode = (Node*)malloc(sizeof(Node));
  34.     if (newNode == NULL)
  35.         return NULL;
  36.     // 初始化新建节点前置节点和后置节点都是自己
  37.     newNode->prior = newNode->next = newNode;
  38.     return newNode;
  39. }

  40. // 创建表头  成功,返回0;否则,返回-1。
  41. int Link_Create()
  42. {
  43.     Head = CreateNode();
  44.     if (Head == NULL)
  45.         return -1;
  46.     return 0;
  47. }

  48. // 表头插入数据
  49. Node * Link_InsertFirst(Node*head)
  50. {
  51.     Node *newNode = CreateNode();
  52.     if (newNode == NULL)
  53.         return NULL;
  54.     newNode->next = head->next;
  55.     newNode->prior = head;
  56.     head->next->prior = newNode;
  57.     head->next = newNode;
  58.     return newNode;
  59. }

  60. //删除表尾数据
  61. Node* Link_DeleteLast(Node*head)
  62. {
  63.     Node *current = head->prior;
  64.     current->next->prior = current->prior;
  65.     current->prior->next = current->next;
  66.     return current;
  67. }



  68. //销毁双向循环链表L:
  69. void DestroyList(Node* head)
  70. {
  71.         Node* q,*p=head->next; /* p指向第一个结点 */
  72.         while(p!=head) /* p没到表头 */
  73.         {
  74.                 q=p->next;
  75.                 free(p);
  76.                 p=q;
  77.         }
  78.         free(head);
  79.      head=NULL;
  80. }
  81. /*=========显示蛇身===========*/
  82. void LCD_DrawPoint_Snake(Node *head)
  83. {
  84.     Node *current=head->next;
  85.     while(current!=head)//从头到为,输出蛇身
  86.     {
  87.         LCD_DrawPoint(current->x,current->y,ON);
  88.         current=current->next;
  89.     }
  90. }

  91. /*=========判断是否撞墙=============*/
  92. void cantcrosswall(Node*head)//不能穿墙
  93. {
  94.     Node *current = head->next;
  95.     if(current->x<=min_width ||current->x>=frame_width-1 ||current->y>=frame_height-1 || current->y<=min_height)
  96.     {
  97.         endgamestatus=1;//游戏结束的情况。3种情况
  98.     }
  99. }
  100. /*=========判断是否咬到了自己=============*/

  101. void biteself(Node*head)//
  102. {
  103.     Node *current=head->next;
  104.     Node *self=current->next;
  105.     while(self!=head)
  106.     {
  107.         if(self->x==current->x && self->y==current->y)
  108.         {
  109.                                           endgamestatus=2;
  110.         }
  111.         self=self->next;
  112.     }
  113. }

  114. u8 is_alive(Node*head)//判断是否存活
  115. {
  116.   biteself(head);//判断是否咬到自己
  117.         cantcrosswall(head);//判断是否到边界
  118.         return endgamestatus;
  119. }
  120. /*=========判断是否吃到食物=============*/
  121. FlagStatus  judgeCoordInFoods(Food *foods, Node *spot)
  122. {
  123.    Node * nextHead=spot->next;
  124.          if(nextHead->x>=foods->X[0])//如果蛇头在食物坐标范围内
  125.         if(nextHead->x<=foods->X[3])
  126.             if(nextHead->y>=foods->Y[0])
  127.                 if(nextHead->y<=foods->Y[3])
  128.                     return SET;//返回吃的食物
  129.     return RESET;//没吃的到
  130. }



  131. void DrawFood(Food *foods,u8 state)//画出食物
  132. {
  133.     for(int i=0; i<Food_Size; i++)
  134.     {
  135.         LCD_DrawPoint(foods->X[i], foods->Y[i],state);//显示食物
  136.     }
  137. }



  138. /*========产生随机食物=====*/
  139. void Gave_Food(Food *foods)
  140. {
  141.     do
  142.     {
  143.         foods->X[0]=(rand() % (77))+ 5;                //产生食物的范围
  144.         foods->Y[0]=(rand() % (41))+ 5;//(x-y)Y最大值  +z 最小值  
  145.     }
  146.     while(!((foods->X[0]>5)&&(foods->X[0]<80)&&(foods->Y[0]<40)&&(foods->Y[0]>9)));                        //判断是否在范围内
  147.     foods->X[1] = foods->X[0] + 1;
  148.     foods->X[2] = foods->X[0];
  149.     foods->X[3] = foods->X[0] + 1;
  150.     foods->Y[1] = foods->Y[0] ;
  151.     foods->Y[2] = foods->Y[0] + 1;
  152.     foods->Y[3] = foods->Y[0] + 1;
  153.     DrawFood(foods,ON);
  154. }

  155. /*=======吃到食物处理=======*/
  156. void update_food()  //吃到食物处理
  157. {
  158.     static        u16 time=0;
  159.           Node * nextHead=Head->next;
  160.     time++;
  161.     if(judgeCoordInFoods(&food,Head))//吃到食物
  162.     {
  163.         time=0;//食物更新时间清0
  164.         snake.len++;//蛇身长度+1
  165.         DrawFood(&food,OFF);//清除当前食物
  166.         Gave_Food(&food);//获取新食物
  167.         snake.scroe+=((snake.len+10)/10);//分数
  168.                         
  169.                                 LCD_ShowNum(10,0,snake.len,3);                                                                                        //显示长度
  170.                                 LCD_ShowNum(3,0,snake.scroe/100,3);                                                                        //显示
  171.                                 LCD_ShowNum(3,0,snake.scroe%100,3);                                                                        //显示
  172.         LCD_DrawPoint(nextHead->x,nextHead->y,ON);                //显示蛇头
  173.     }
  174.                 else
  175.                 {
  176.         nextHead=Link_DeleteLast(Head); //删除蛇最后一个元素
  177.                                 LCD_DrawPoint(nextHead->x,nextHead->y,OFF);//关掉蛇尾最后一个像素点
  178.         free(nextHead);//释放最后一个链表
  179.     }
  180.     if(time>100)//如果跟新时间到达之前没有吃到食物
  181.     {
  182.         time=0;//更新食物
  183.         DrawFood(&food,OFF);//关掉食物
  184.         Gave_Food(&food);//获取新食物
  185.     }
  186. }


  187. /*=========游戏暂停==========*/
  188. void pause()
  189. {
  190.   //  Disp_CZhuitu(6,8,2,10,Pause_char);
  191.     while(1)
  192.     {
  193.     //    delay_ms(300);
  194.         if(MODE_ON)
  195.             break;
  196.     }
  197. //    Disp_CZhuitu(6,8,2,10,jie_char);
  198. }


  199. /*=========控制游戏==========*/
  200. void gamecircle()//控制游戏
  201. {
  202.     Fuct Sta=KEY_Test();

  203.     if(Sta==Up&& snake.dir!=Down)//如果上按键按下 且蛇运动方向不为下
  204.     {
  205.         snake.dir=Up;//蛇方向为上
  206.     }
  207.     else if(Sta==Down&& snake.dir!=Up)
  208.     {
  209.         snake.dir=Down;
  210.     }
  211.     else if(Sta==Left&& snake.dir!=Right)
  212.     {
  213.         snake.dir=Left;
  214.     }
  215.     else if( Sta==Right&& snake.dir!=Left)
  216.     {
  217.         snake.dir=Right;
  218.     }
  219.     else if(Sta==Puase)//如果暂停键按下
  220.     {
  221.         pause();
  222.     }
  223.     else if(Sta==Rtn)
  224.     {
  225.        endgamestatus=3;
  226.     }
  227. }



  228. /*=========蛇移动==========*/
  229. void move_snake()//蛇前进,上U,下D,左L,右R
  230. {   
  231.           Node*nextHead;
  232.     Node * currot=Head->next;
  233.     nextHead=Link_InsertFirst(Head);//在蛇头添加一个元素
  234.     switch(snake.dir)
  235.     {
  236.         case Up:
  237.             nextHead->x=currot->x;
  238.             nextHead->y=currot->y-1;
  239.             break;
  240.         case Down:
  241.             nextHead->x=currot->x;
  242.             nextHead->y=currot->y+1;
  243.             break;
  244.         case Left:
  245.             nextHead->x=currot->x-1;
  246.             nextHead->y=currot->y;
  247.             break;
  248.         case Right:
  249.             nextHead->x=currot->x+1;
  250.             nextHead->y=currot->y;
  251.             break;
  252.     }
  253.                 LCD_DrawPoint(nextHead->x,nextHead->y,ON);//显示添加的元素
  254. }


  255. //显示游戏界面
  256. void game_ucos()
  257. {
  258.         LCD_prints_68(0, 0, "SC:");
  259.         LCD_prints_68(7, 0,"Ln:");
  260.         LCD_DrawLine(0,8,83,8);
  261.         LCD_DrawLine(0,8,0,47);
  262.         LCD_DrawLine(0,47,83,47);
  263.         LCD_DrawLine(83,47,83,8);
  264. }



  265. /*=========初始化游戏===========*/
  266. void init_snake(u8 grade)
  267. {
  268.     int i;
  269.           Node *list=NULL;
  270.             if(grade==2)
  271.         snake.speed=120;
  272.     else if(grade==3)
  273.         snake.speed=70;
  274.                  else if(grade==4)
  275.         snake.speed=40;

  276. //=============蛇初始化==============//               
  277.                 snake.dir=Right;
  278.                 endgamestatus=0;
  279.                 snake.len=10;
  280.     Link_Create();
  281.     for(i=0; i<snake.len; i++)
  282.     {
  283.         list=Link_InsertFirst(Head);
  284.         list->x=frame_width/2+i;
  285.         list->y=frame_height/2;
  286.     }
  287.     LCD_DrawPoint_Snake(Head);
  288. //=============界面初始化==============//
  289.                 snake.scroe=0;
  290.                
  291.                 LCD_ShowNum(10,0,snake.len,3);                                //显示长度
  292.                 LCD_ShowNum(3,0,snake.scroe%1000,3);                                        //显示分数
  293.                 LCD_ShowNum(3,0,snake.scroe/100,3);                                        //显示分数
  294.                 LCD_ShowNum(3,0,snake.scroe%100,3);                                        //显示分数
  295.                 Gave_Food(&food);
  296. }

  297. void remove_snake(Node *head)
  298. {
  299.     Node *current=head->next;
  300.     while(current!=head)//从头到为,输出蛇身
  301.     {
  302.         LCD_DrawPoint(current->x,current->y,OFF);
  303.         current=current->next;
  304.     }
  305. }


  306. int Play(u8 grade)
  307. {
  308.    init_snake(grade);
  309.                 game_ucos();
  310.         while(1)
  311.     {
  312.         gamecircle();        //游戏控制
  313.         move_snake(); //移动蛇身
  314.                           update_food(); //是否产生食物
  315.         delay_ms(snake.speed); //移动速度snake.speed
  316.         if(is_alive(Head)) //蛇的死活(撞墙或自食)
  317.         {  
  318.                                           DestroyList(Head);
  319.                                                 DrawFood(&food,OFF);//清除当前食物
  320.                                                 LCD_Clear_Cache();
  321.                                         while(KEY_Test()==0);
  322.                                         RLCD_vertical_vrintch(game_icon);
  323.                                         LCD_clr_scr();
  324.                                         interface();
  325.                                         return 0;
  326.         }
  327.     }
  328. }


  329. //进入难度选择
  330. void Start_game()
  331. {
  332.         int key,num=2;
  333.         interface();
  334.         while(1)
  335.         {
  336.                 LCD_prints_68(3, 0,"Difficult");
  337.                 LCD_ShowNum(5,2,1,1);                                        //显示分数
  338.                 LCD_ShowNum(5,3,2,1);                                        //显示分数
  339.                 LCD_ShowNum(5,4,3,1);                                        //显示分数
  340.                 LCD_printc_68(3, num, '>');
  341.                
  342.                 key=KEY_Test();

  343.                 switch (key)
  344.                 {
  345.                         case Up:
  346.                                                         LCD_printc_68(3, num, ' ');
  347.                                                         num ++;
  348.                                                         if(num >=5) num =2;
  349.                                                         LCD_printc_68(3, num, '>');
  350.                                                         delay_ms(100) ;break ;
  351.                         case Down:         
  352.                                                                 LCD_printc_68(3, num, ' ');
  353.                                                                 num --;
  354.                                                                 if(num <=1) num =4;
  355.                                                                 LCD_printc_68(3, num, '>');
  356.                                                                 delay_ms(100);break ;
  357.                         case Puase:
  358.                                                                         LCD_clr_scr();
  359.                                                                         Play(num);break ;
  360.                         
  361.                         default : break;
  362.                 }
  363.         }
  364. }

复制代码


0.png
附件有源程序和PCB文件
全部资料51hei下载地址:
nokia5510.zip (1.5 MB, 下载次数: 73)

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

ID:336842 发表于 2019-4-13 20:23 | 显示全部楼层
您好,请问能上传仿真图吗
回复

使用道具 举报

ID:197252 发表于 2019-4-14 16:46 | 显示全部楼层
一只猫的过客 发表于 2019-4-13 20:23
您好,请问能上传仿真图吗

没有仿真的文件,直接做出硬件在线调试的。
回复

使用道具 举报

ID:446097 发表于 2019-6-10 21:49 | 显示全部楼层
13321764316 发表于 2019-4-14 16:46
没有仿真的文件,直接做出硬件在线调试的。

楼主有硬件清单吗?
回复

使用道具 举报

ID:785431 发表于 2020-6-21 14:37 | 显示全部楼层
矩阵是什么?

回复

使用道具 举报

ID:691090 发表于 2020-7-15 16:06 | 显示全部楼层
请问LCD1602显示屏可以做不?
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表