找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 4795|回复: 2
收起左侧

51单片机LCD12864液晶贪吃蛇游戏Proteus仿真源程序制作

  [复制链接]
ID:22266 发表于 2021-5-16 04:50 | 显示全部楼层 |阅读模式
源码+需要用到的辅助软件分享
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
51hei.png 2.png 3.png
我是一个普通二本学校电气的学生。今年大三。
这个东西是我大二的时候做的玩的。

这里面有我做51单片机+12864液晶的贪吃蛇游戏机的详细资料。仿真实物我都做过,效果我自己比较满意。花的精力也比较多,代码也全都是自己一行一行的写下来。所以做完之后想把这些东西都整理一下,然后放在上面,方便后面要做这些东西的人。
open精神在国内是很重要的说···0 0、

环境: keil V4.02    protues version 7.5
   
我先说下我这个东西最主要的几块。

1.单片机用的是stc的,51内核,你首先要会这个···

2.用状态机实现按键。这个里面有别人写的技术帖子··

3.液晶是12864,2种控制器都做过,一个控制器是ST7920带字库的,一个控制器是KS0108不带字库的。2个的英文资料我都放在里面了。首先得弄懂液晶要怎么样和单片机连接。
要在液晶上面做游戏,推荐先看英文资料,一定要看英文,研究液晶读写时序和初始化方法,然后找别人的程序,实现液晶的基本操作,画点,写汉字什么的,读液晶···然后自己写程序实现液晶的所有基本操作。这样才能对液晶有充分的认识,才能开始在上面做游戏。有了这个基础,以后学别的液晶也会快的多。

4.菜单,游戏少不了要用到菜单,比如选择难度,选择地图什么的。我里面放了一个别人的文档,看了之后应该就会自己写了。(注意文档里面讲的是用函数指针实现菜单,但是KEIL这个环境下用51函数指针会很困难,推荐像我那样直接传菜单号)

5.贪吃蛇游戏,推荐先不要看别人的程序,自己在电脑上面下个贪吃蛇游戏,玩的时候就观察贪吃蛇游戏的特征···比如蛇在怎么动,蛇要怎么描述,是每个点都描述还是只要蛇头蛇尾···蛇转弯的时候怎么写?  对于上面的问题,可以参考别人的程序,然后就开始动手画图什么的表示出来这些过程,一个功能一个功能逐一实现。(推荐看带字库的那个程序,那个程序代码是最终优化版本。思路很清晰···)

6.液晶的动画,动画实际上就是图片一张一张的显示,我在里面放了一个把彩色照片黑白二值化的工具,可以找到自己喜欢的GIF动画,用提取工具把照片一张一张提取出来,然后黑白二值化,然后取模工具取模,然后实现···虽然过程蛮枯燥,但是效果还不过,也可以学到不少东西。

最后希望大家在看这个的时候,特别是贪吃蛇游戏实现的时候,哪一个方面有更好的办法一定通知我,因为我觉得我实现贪吃蛇游戏的方法已经很不错了。如果能有更好的,我会非常希望学习下····

单片机源程序如下:
  1. #include <st7920.h>
  2. #include <tanchishe_resource.h>
  3. #define num 15

  4. //用来描述蛇在液晶上面一个点的结构体
  5. struct she
  6. {
  7.         unsigned char x,y;
  8.         unsigned int value;
  9.         unsigned char direction;        
  10. };

  11. struct loop_queue  //利用数组实现循环队列,拐点不会超过15个。所以大小选15足以。
  12. {   
  13.         unsigned int front;
  14.         unsigned int rear;
  15.         unsigned int count;
  16.         struct she dat[num];
  17. };
  18. //不熟悉数组实现循环队列的可以在纸上画一下操作过程

  19. struct she tou,wei,food; //蛇的头尾节点  和食物点
  20. struct loop_queue queue;//循环队列  蛇转弯则形成拐点 将拐点入队列  尾巴到达拐点则拐点出队列

  21. unsigned char flag_exit_game; //返回标志  为1时  从贪吃蛇游戏返回菜单
  22. unsigned char flag_game_over; //结束标志  为1时  显示游戏结束画面 同时使返回标志为1
  23. unsigned char tcs_key_flag;  //贪吃蛇在一次移动中   只允许捕获一次按键  也就是只能产生一个拐点
  24. unsigned char tcs_key_state; //用于按键状态机的实现
  25. unsigned int  tcs_df;  //贪吃蛇得分
  26. unsigned char tcs_stop;//贪吃蛇暂定功能的实现

  27. unsigned char tcs_nd;  //贪吃蛇难度的选择
  28. unsigned char tcs_dt;  //贪吃蛇地图的选择 这2个是全局变量  在menu里面要用到


  29. void tcs_bianjie(unsigned int he);//贪吃蛇游戏地图的实现和边界
  30. void timer0_initial();
  31. void timer1_initial();

  32. //循环队列的基本操作
  33. void queue_initial(struct loop_queue *q);  
  34. void queue_in(struct loop_queue *q,struct she h)  ;
  35. struct she queue_out(struct loop_queue *q)  ;

  36. void tcs_game_initial();
  37. void food_produce();

  38. void tcs_game()
  39. {
  40.         struct she check;  //用与暂存蛇即将移动的点  从而进行判断
  41.         unsigned int he;
  42.         unsigned char flag;//flag作为吃到食物的标志

  43.         flag_exit_game=0;
  44.         flag_game_over=0;
  45.         tcs_key_state=0;
  46.         tcs_df=0;
  47.         tcs_stop=1;

  48. //        draw_lcd_picture(&tcs_picture[0]); //贪吃蛇游戏画面
  49. //        delay_ms(2000);

  50.         tcs_bianjie(tcs_dt); //贪吃蛇游戏边界
  51.         tcs_game_initial();//贪吃蛇初始化  主要是设定蛇头蛇尾  并显示蛇
  52.         timer0_initial(); //定时器0工作在方式2  8位自动装初值  只计数不中断 用来产生随机数
  53.         timer1_initial(); //定时器1工作在方式1        16位     10ms中断一次扫描键盘

  54.         do
  55.         {
  56.                 food_produce();//产生一个随机数 即随即产生x y value3个值确定一个点为食物
  57.                 he=test_point_exist(food.x,food.y,food.value);//检测产生的点的地方是否已经有点存在
  58.         }
  59.         while(he==food.value);//如果随机数产生在蛇的身体以及边界上 则重新再产生一个  
  60.         draw_lcd_point(food.x,food.y,food.value);//显示食物 先读取这个点所在xy的情况 以免破坏现场
  61.         
  62.         queue_initial(&queue);//初始化循环队列  使队列空  front rear 即头尾指针为0

  63.         while(1)
  64.         {
  65.                 if(flag_exit_game==1)//如果游戏过程中按下返回键  则立即结束游戏  返回菜单
  66.                         break;
  67.                 if(flag_game_over==1)//如果游戏过程中蛇死了  则立即结束游戏 显示结束动画 按键后返回菜单
  68.                         break;
  69.                
  70.                 ET1=1;   //允许扫描按键          贪吃蛇游戏难度决定蛇2次移动之间的间隔时间  也就是检测按键的时间
  71.                 tcs_key_flag=0; //允许捕获按键  在检测按键时间内  只允许生产一个拐点  即捕获到一个有效的
  72.                                                 //按键以后  就置一这个变量 不允许再增加拐点
  73.                 while(tcs_stop==0);//暂定键按下会取反tcs_stop的值  从而实现暂停功能
  74.                 switch(tcs_nd)
  75.                 {
  76.                         case 0:  {delay_ms(10); break;}//0代表变态难度  蛇的2次移动之间只有10ms的机会检测键盘按下
  77.                         case 1:  {delay_ms(50); break;}//难度减小  2次移动间隔增加
  78.                         case 2:  {delay_ms(100); break;}
  79.                 }
  80.                 ET1=0;

  81.                 switch(tou.direction)//这个switch用来根据蛇头的x y value值以及当前蛇头移动的方向
  82.                                                          //取出蛇头要到达的下一个点  存入check中  来判断是否撞墙 吃到食物 什么的···
  83.                 {        
  84.                         case 0: {         
  85.                                                 check.y=tou.y;
  86.                                                 if(tou.value==0x0001)//x的值要发生改变
  87.                                                 {
  88.                                                         if(tou.x==3)//蛇碰到上屏幕的右边界  则回到左边界
  89.                                                                 check.x=0;
  90.                                                         else if(tou.x==11)//蛇碰到下屏幕的右边界  则回到左边界
  91.                                                                 check.x=8;
  92.                                                         else
  93.                                                                 check.x=tou.x+1;
  94.                                                         check.value=0x8000;
  95.                                                 }
  96.                                                 else
  97.                                                 {
  98.                                                         check.x=tou.x;
  99.                                                         check.value=tou.value>>1;
  100.                                                 }
  101.                                                  break ;
  102.                                         }//->向右y不变

  103.                         case 1: {//向下value值不变                                          
  104.                                                 check.value=tou.value;
  105.                                                 if(tou.y==31) //蛇头从在上下屏之间移动
  106.                                                 {
  107.                                                         if(tou.x<=3)//蛇头从上屏移动到下屏
  108.                                                                 check.x=tou.x+8;
  109.                                                         else                 //蛇头从下屏移动到上屏
  110.                                                                 check.x=tou.x-8;
  111.                                                         check.y=0;
  112.                                                 }
  113.                                                 else
  114.                                                 {
  115.                                                         check.y=tou.y+1;
  116.                                                         check.x=tou.x;  //蛇头的x值不变
  117.                                             }
  118.                                                 break ;
  119.                                         }

  120.                         case 2: {//<-向左y不变         
  121.                                                 check.y=tou.y;
  122.                                                 if(tou.value==0x8000)//x的值要发生改变
  123.                                                 {
  124.                                                         if(tou.x==0)//蛇碰到上屏幕的左边界  则回到右边界
  125.                                                                 check.x=3;
  126.                                                         else if(tou.x==8)//蛇碰到下屏幕的左边界  则回到右边界
  127.                                                                 check.x=11;
  128.                                                         else
  129.                                                                 check.x=tou.x-1;
  130.                                                         check.value=0x0001;
  131.                                                 }
  132.                                                 else
  133.                                                 {
  134.                                                         check.x=tou.x;
  135.                                                         check.value=tou.value<<1;
  136.                                                 }
  137.                                                  break ;
  138.                                         }

  139.                         case 3: { //↑         向上value不变
  140.                                                 check.value=tou.value;
  141.                                                 if(tou.y==0) //蛇头从在上下屏之间移动
  142.                                                 {
  143.                                                         if(tou.x<=3)//蛇头从上屏移动到下屏
  144.                                                                 check.x=tou.x+8;
  145.                                                         else                 //蛇头从下屏移动到上屏
  146.                                                                 check.x=tou.x-8;
  147.                                                         check.y=31;
  148.                                                 }
  149.                                                 else
  150.                                                 {
  151.                                                         check.y=tou.y-1;
  152.                                                         check.x=tou.x;  //蛇头的x值不变
  153.                                             }
  154.                                                 break ;
  155.                                         }        
  156.                 }                        
  157.                 flag=0;//初始化吃到食物标志  表示没有吃到        
  158.                 he=test_point_exist(check.x,check.y,check.value); // 检测蛇头移动的下一个点是否几经有点存在
  159.                 if(he==check.value) //如果前面的点已经存在        则可以是食物 或者是墙和蛇身
  160.                 {
  161.                         if(        (check.x==food.x) && (check.y==food.y) && (check.value==food.value) )//吃到食物
  162.                         {
  163.                                 flag=1;//吃到食物
  164.                                 tcs_df++;
  165.                                 write_lcd_shuzi(5,16,tcs_shuzi[tcs_df/100]       );                         //显示分数的百位
  166.                                 write_lcd_shuzi(6,16,tcs_shuzi[ (tcs_df%100)/10] );                                //                        十
  167.                                 write_lcd_shuzi(7,16,tcs_shuzi[tcs_df%10]        );                                //          个
  168.                         
  169.                                 food.direction=tou.direction;//食物本没有方向 但是为了下面的语句
  170.                                 tou=food; //食物就成了蛇头···蛇头的方向依然不变
  171.                                  
  172.                                 do
  173.                                 {
  174.                                         food_produce();
  175.                                         he=test_point_exist(food.x,food.y,food.value);
  176.                                 }
  177.                                 while(he==food.value);  
  178.                                 draw_lcd_point(food.x,food.y,food.value);//上面有说过  这个是产生并显示食物
  179.                         }
  180.                         else//撞到身体或者墙
  181.                         {
  182.                                 flag_game_over=1; // 游戏结束
  183.                         }
  184.                 }
  185.                 else  //什么都么有遇到 继续前进
  186.                 {
  187.                         check.direction=tou.direction;//这么做的理由同上 为了保持蛇头当前的移动方向
  188.                         tou=check;
  189.                         draw_lcd_point(tou.x,tou.y,tou.value);//画出此时的蛇头
  190.                 }

  191.                 //下面是对尾巴的操作               
  192.                
  193.                 if(flag==1)//如果吃到食物 直接结束此轮循环 尾巴不做任何操作
  194.                 {
  195.                
  196.                 }
  197.                 else
  198.                 {
  199.                         if (  (wei.x == queue.dat[queue.front].x) && ( wei.y==queue.dat[queue.front].y ) && ( wei.value== queue.dat[queue.front].value)  ) //如果尾巴到达拐点
  200.                         {
  201.                                  check=queue_out(&queue);         
  202.                                 wei.direction=check.direction; //尾巴按照拐点指示的方向走
  203.                         }
  204.                         else //如果没有到达拐点 尾巴移动的方向不变
  205.                         {
  206.                                        
  207.                         }                                                
  208.                                                                         
  209.                         switch( wei.direction ) //这个是尾巴的移动  以及尾巴节点数据的修改
  210.                         {
  211.                                 case 0:        {//->向右y不变        
  212.                                                         clear_lcd_point(wei.x,wei.y,wei.value);
  213.                                                         if(wei.value==0x0001)//x的值要发生改变
  214.                                                         {
  215.                                                                 if(wei.x==3)//蛇碰到上屏幕的右边界  则回到左边界
  216.                                                                         wei.x=0;
  217.                                                                 else if(wei.x==11)//蛇碰到下屏幕的右边界  则回到左边界
  218.                                                                         wei.x=8;
  219.                                                                 else
  220.                                                                         wei.x=wei.x+1;
  221.                                                                 wei.value=0x8000;
  222.                                                         }
  223.                                                         else
  224.                                                         {
  225.                                                                 wei.value=wei.value>>1;
  226.                                                         }
  227.                                                         break;
  228.                                                 }
  229.                                 case 1:        {//向下value值不变        
  230.                                                         clear_lcd_point(wei.x,wei.y,wei.value);
  231.                                                         if(wei.y==31) //蛇从在上下屏之间移动
  232.                                                         {
  233.                                                                 if(wei.x<=3)//蛇从上屏移动到下屏
  234.                                                                         wei.x=wei.x+8;
  235.                                                                 else                 //蛇从下屏移动到上屏
  236.                                                                         wei.x=wei.x-8;
  237.                                                                 wei.y=0;
  238.                                                         }
  239.                                                         else
  240.                                                         {
  241.                                                                 wei.y=wei.y+1;
  242.                                                     }
  243.                                                         break ;
  244.                                                         
  245.                                                     }                                                                                
  246.                                 case 2:        {//<-向左y不变   
  247.                                                         clear_lcd_point(wei.x,wei.y,wei.value);
  248.                                                         if(wei.value==0x8000)//x的值要发生改变
  249.                                                         {
  250.                                                                 if(wei.x==0)//蛇碰到上屏幕的左边界  则回到右边界
  251.                                                                         wei.x=3;
  252.                                                                 else if(wei.x==8)//蛇碰到下屏幕的左边界  则回到右边界
  253.                                                                         wei.x=11;
  254.                                                                 else
  255.                                                                         wei.x=wei.x-1;
  256.                                                                 wei.value=0x0001;
  257.                                                         }
  258.                                                         else
  259.                                                         {
  260.                                                                 wei.value=wei.value<<1;
  261.                                                         }
  262.                                                         break;
  263.                                                  }
  264.                                 case 3:        {//↑         向上value不变  
  265.                                                         clear_lcd_point(wei.x,wei.y,wei.value);
  266.                                                         if(wei.y==0) //蛇从在上下屏之间移动
  267.                                                         {
  268.                                                                 if(wei.x<=3)//蛇从上屏移动到下屏
  269.                                                                         wei.x=wei.x+8;
  270.                                                                 else                 //蛇从下屏移动到上屏
  271.                                                                         wei.x=wei.x-8;
  272.                                                                 wei.y=31;
  273.                                                         }
  274.                                                         else
  275.                                                         {
  276.                                                                 wei.y=wei.y-1;
  277.                                                             }
  278.                                                         break;
  279.                                                  }        
  280.                           }
  281.                 }                                                        
  282.         }        
  283.         TR0=0;
  284.         TR1=0;
  285.         ET1=0;  //退出游戏时 关闭游戏过程中用到的定时器0和1  然后定时器中断也要关闭


  286. }

  287. //状态机实现按键   参考里面资料别人的技术贴
  288. void timer1() interrupt 3
  289. {        
  290.         unsigned char tou_last_direction; //记录蛇头原来的移动方向
  291.         
  292.         TR1=0;
  293.         TH1=0xDC;               
  294.         TL1=0x00;

  295.         switch (tcs_key_state)
  296.         {
  297.                 case 0:        {                // 按键初始态
  298.                                         if ( (P2 & 0xfe) != 0xfe )   tcs_key_state = 1;        // 键被按下,状态转换到键确认态
  299.                                                 else          tcs_key_state = 0;
  300.                                         break;
  301.                                 }
  302.         
  303.                 case 1:        {                        // 按键确认态
  304.                                         if ( (P2 & 0xfe) != 0xfe)
  305.                                         {
  306.                                                 // 按键仍按下,此时确定记录一次有效按键
  307.                                                         tcs_key_state = 2;        // 状态转换到键释放态
  308.                                                         if(tcs_key_flag==0)         //如果此时允许捕获按键
  309.                                                         {
  310.                                                                 tou_last_direction=tou.direction;
  311.                                                                 switch(P2 & 0xfe)
  312.                                                                 {        
  313.                                                                         case 0xfc:        break; // 确定键  不进行任何操作
  314.                                                                         case 0xfa:        {  flag_exit_game=1;   break; }//返回键           结束游戏 返回菜单
  315.                                                                         case 0xbe:        {  tou.direction=3;break; }//↑
  316.                                                                         case 0xee:        {  tou.direction=2;break; }//←
  317.                                                                         case 0xde:        {  tou.direction=1;break; }//↓
  318.                                                                         case 0x7e:        {  tou.direction=0;break; }//->         
  319.                                                                         case 0xf6:  {  if(tcs_stop==0) tcs_stop=1; else tcs_stop=0; break;}//暂停键
  320.                                                                 }
  321.                                                 
  322.                                                                 if( (tou.direction==tou_last_direction) || (tou.direction+tou_last_direction==2) || (tou.direction+tou_last_direction==4) )
  323.                                                                 //如果本来就是按此方向移动   或者按键给的方向与蛇移动方向相反 则不做任何操作 此次按键无效
  324.                                                                 {
  325.                                                                         tou.direction=tou_last_direction; //   还原蛇头的移动方向
  326.                                                                 }
  327.                                                                 else //否则记录此头结点成为拐点           
  328.                                                                 {                        
  329.                                                                         queue_in(&queue,tou);
  330.                                                                         tcs_key_flag=1; //已经捕获到一次按键信息 这次移动拐点已经产生 关闭捕获键盘
  331.                                                                
  332. ……………………

  333. …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码

所有资料51hei提供下载:
51单片机 12864液晶贪吃蛇游戏机(完美版).7z (6.69 MB, 下载次数: 180)

评分

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

查看全部评分

回复

使用道具 举报

ID:981320 发表于 2021-11-15 22:12 | 显示全部楼层
本帖最后由 Shrink001 于 2021-11-15 22:24 编辑

你好,关于你的单片机实现的贪吃蛇游戏的程序,我现在想降低程序在data中的内存,将一部分存储到xdata中,但是尝试了很久都没有成功,会出现各种报错,请问您有没有什么好的想法呢?求您解答一下我现在是把tcs_game.c文件中21,22行的两个结构体添加了xdata的前缀,data是降下来了程序也可以编译通过,但是在proteus仿真时会出现黄色的短路电平,不知道这个该怎么办。。。
回复

使用道具 举报

ID:981320 发表于 2021-11-15 22:19 | 显示全部楼层
求助!关于你的单片机实现的贪吃蛇游戏的程序,我现在想降低程序在data中的内存,将一部分存储到xdata中,但是尝试了很久都没有成功,会出现各种报错,请问您有没有什么好的想法呢?求您解答一下
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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