文章目录

  • 0. 效果演示
  • 1. 开发环境
  • 2. 项目地址
  • 3. 项目目录
  • 4. 设计与开发
    • 4.1 整体原理图
    • 4.2 方向键模块
    • 4.3 点阵模块
    • 4.4 整体逻辑说明
      • 4.4.1 点阵怎么刷新
      • 4.4.2 按键在哪里检测
      • 4.4.3 蛇怎么移动
      • 4.4.4 游戏规则
    • 4.5 main.c
  • 5. 不足与展望

0. 效果演示

  • 视频演示:C51 单片机贪吃蛇 基于Proteus

1. 开发环境

  • 系统:window10 专业版。
  • 开发软件:Keil5
  • 仿真软件:Proteus

PS:软件下载地址 Proteus电路仿真及应用(51单片机系列)


2. 项目地址

  • https://gitee.com/silver-blood-inn/c51-sankegame-base-on-proteus

3. 项目目录

├─code        # 项目文件
│  ├─bsp      # 外设模块封装目录,比如点阵 matrix.c、方向按键 dir_key.c 等
│  ├─output   # 编译后的hex文件输出位置
│  ├─project  # 存放keil的project目录
│  └─user     # main.c 和 pbdata.c共享模块
└─proteus     # 仿真画图存放位置

PS : 关于项目为什么结构为什么这样划分,可以看此视频 51单片机入门学习—以最通俗易懂的语言讲解! 中P25 - P28 关于模块化编程的讲解。


4. 设计与开发

  • 代码如果看着比较凌乱,可以使用代码 配合下方各个模块的思路进行理解就可以啦 :)。

4.1 整体原理图

4.2 方向键模块

对于方向键我使用的是矩阵键盘的方式完成各个按键的检测(如果不了解矩阵键盘怎么用请看此视频 P10 矩阵键盘)。

  • 封装模块:将四个按键和其检测代码封装到 dir_key.c 中,检测那个按键按下就修改dir_key.c 中的 dir 这个变量。外部使用这个模块的时候 extern unsigned char dir; 即可引入这个变量来判断此时的方向,具体代码如下:
#include "PBDATA.H"unsigned char dir = 0; // 默认向上void DirKeyScan()
{unsigned char temp0 = 0, temp1 = 0, temp2 = 0;P1 = 0xFC;  // 1111 1100if(P1 != 0xFC) {  // 检测矩阵按键那行按下delay(20);temp1 = P1;// 列P1 = 0xF3; // 1111 0011if(P1 != 0xF3) {  // 检测矩阵按键那列按下temp2 = P1;}}temp0 = temp1 | temp2;if(temp0 == 0xFA) {  // 1111 1010 上dir = (dir == 2) ? dir : 0;} else if(temp0 == 0xF6) {  // 1111 0110 下dir = (dir == 0) ? dir : 2;} else if(temp0 == 0xF9) {  // 1111 1001 左dir = (dir == 1) ? dir : 3;} else if(temp0 == 0xF5) { // 1111 0101 右dir = (dir == 3) ? dir : 1;}
}

4.3 点阵模块

  • 如果不了解点阵怎么使用可以参考此视频 : P17 8X8 点阵
  • 封装模块: 将点阵显示的相关函数封装到 matrix.c 文件当中,具体代码如下:
#include "PBDATA.H"static uchar code indexs[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};  // 行位码static uchar tab[8] = {0};   // 列位码// 清屏函数
void Matrix_Clear()
{COLP = 0x00;
}// 生成列码
void Matrix_GenerateTab(uchar rowIndexs[], uchar colIndexs[], uchar length)
{uchar i = 0;// 清掉上次形成的列码for(i = 0; i < 8; i++) {tab[i] = 0;}//形成列位码for(i = 0; i < length; i++) {// rowIndexs[i] 行显示的列追加 tab[rowIndexs[i]];tab[rowIndexs[i]] += indexs[colIndexs[i]];}
}// 使用编码点亮点阵
void Matrix_ShowPointByCode()
{uchar i = 0;// 逐行显示 for(i = 0; i < 8; i++) {Matrix_Clear();ROWP = ~indexs[i];COLP = tab[i];  }
}void Matrix_ShowAll()
{uchar i = 0;// 逐行显示 for(i = 0; i < 8; i++) {Matrix_Clear();ROWP = ~indexs[i];COLP = 0xFF;  }
}

4.4 整体逻辑说明

4.4.1 点阵怎么刷新

  • 点阵刷新我放在 main() 函数当中死循环中,这样能最大程度保证点阵屏幕的刷新速度够快,视觉暂留效果达到最佳。
  • 具体代码看下方 main.c

4.4.2 按键在哪里检测

  • 按键不需要很高频率去检测,50ms 检测一次完全够用,且如果跟点刷新放在一条主线上会影响点阵的显示效果,因此我将其放在了定时器0 中,每隔50ms 检测下即可,并未出现什么问题。
  • 具体代码看下方 main.c

4.4.3 蛇怎么移动

  • 我将蛇移动也放到定时器0中,不过移动频率使用全局变量 speed控制,也就是隔 speed * 50ms 移动一次,可以自行调整,并且增加后续功能也可以吃的食物越多,调整 speed 变量的值。
  • 如果大家不知道蛇具体移动怎么实现,这里坐下简单说明,如下代码为蛇的一次移动的核心逻辑。
//1. 使用两个数组存储身体行和列 (uchar 即 unsigned char);
//   如下则表示蛇头到蛇尾分别在0行0列,0行1列,0行2列。
uchar bodyRow[16] = {0, 0, 0};
uchar bodyCol[16] = {0, 1, 2};// 2. 移动身体 : 将前一个位置的行列赋值给后一个位置即完成蛇身体向头部的一次移动
for(i = snakeBodyLength - 1; i > 0; i--) {bodyRow[i] = bodyRow[i - 1];bodyCol[i] = bodyCol[i - 1];
}// 生成新的蛇头
bodyRow[0] = bodyRow[0] + dirRow[dir];
bodyCol[0] = bodyCol[0] + dirCol[dir];
  • 具体代码看下方 main.c

4.4.4 游戏规则

  • 蛇死亡时候点阵会全亮。
  • 蛇死亡的条件为:撞到自身或边界。

4.5 main.c

#include "PBDATA.H"/****************************************蛇体 和 食物 相关的数据 和 函数
*****************************************/
uchar bodyRow[16], bodyCol[16];  // 蛇身的数组uchar code dirRow[4] = {-1, 0, 1, 0};  // 上右下左方向X增量
uchar code dirCol[4] = {0, 1, 0, -1};  // 上右下左方向Y增量
extern uchar dir;
uchar maxRow = 8, maxCol = 8;  // 行的范围 [0, maxRow), 列范围同理
uchar speed = 5;  // 蛇的速度,单位是50ms
uchar snakeBodyLength = 0;  // 蛇身体的长度
uchar isDead = 0;  // 表示蛇是否死亡uchar foodRow = 0, foodCol = 0, needCreate = 1;  // 食物坐标和食物是否被吃标志void InitSnake()
{bodyRow[0] = 7;bodyCol[0] = 2;bodyRow[1] = 7;bodyCol[1] = 1;snakeBodyLength = 2;dir = 1;// 生成蛇打印的内容Matrix_GenerateTab(bodyRow, bodyCol, snakeBodyLength);// 设置时间种子srand(0);
}void GenerateFood()
{uchar i = 0;while(needCreate) {// 随机生成 Row 和 ColfoodRow = rand() % maxRow;foodCol = rand() % maxCol;// 判断食物是否和当前蛇身体冲突 for(i = 0; i < snakeBodyLength; i++) {if(bodyRow[i] == foodRow && bodyCol[i] == foodCol) {break;}}if(i == snakeBodyLength) {needCreate = 0;bodyRow[snakeBodyLength] = foodRow;bodyCol[snakeBodyLength] = foodCol;} }
}/****************************************定时器相关的代码
*****************************************/uchar count;  // time = count * 50msvoid InitTimer()
{TMOD = 0x01;// 初始值 : 50msTH0 = (65536 - 50000) / 256;  // 初始值取高八位TL0 = (65536 - 50000) % 256;  // 初始值取低八位// 中断开启ET0 = 1;  // 开启定时器0的中断EA = 1;   // 开启总的中断// 配置TCON// TR0 : 1, 启动定时器0TR0 = 1;
}void TimerIsr() interrupt 1
{uchar nextHeadRow = 0, nextHeadCol = 0, i = 0;// 重新装填// 初始值 : 50msTH0 = (65536 - 50000) / 256;  // 初始值取高八位TL0 = (65536 - 50000) % 256;  // 初始值取低八位if(count == speed && !isDead) {  // count * 50ms 触发一次count = 0;// 生成食物GenerateFood();// 预测蛇头nextHeadRow = bodyRow[0] + dirRow[dir];nextHeadCol = bodyCol[0] + dirCol[dir];if(nextHeadRow >= maxRow || nextHeadRow < 0 || nextHeadCol >= maxCol || nextHeadCol < 0) {isDead = 1;return;}// 身体撞击for(i = 0; i < snakeBodyLength; i++) {if(nextHeadRow == bodyRow[i] && nextHeadCol == bodyCol[i]) {isDead = 1;return;}}// 吃到食物与否if(nextHeadRow == foodRow && nextHeadCol == foodCol) {snakeBodyLength += 1;needCreate = 1;// 再生成新的食物GenerateFood();}// 蛇身体移动for(i = snakeBodyLength - 1; i > 0; i--) {bodyRow[i] = bodyRow[i - 1];bodyCol[i] = bodyCol[i - 1];}// 新的蛇头bodyRow[0] = nextHeadRow;bodyCol[0] = nextHeadCol;// 生成需要显示的图形Matrix_GenerateTab(bodyRow, bodyCol, snakeBodyLength + 1);}count++; // 每50ms进行一次按键扫描DirKeyScan();
}void main()
{uchar i = 0, a = 0;count = speed;InitSnake();InitTimer();while(1){if(isDead) {Matrix_ShowAll();} else {// 打印蛇的身体Matrix_ShowPointByCode();       }}
}

5. 不足与展望

  • 游戏功能较为简单,仅仅实现了贪吃蛇的核心功能,其实还可以加入 LCD1602等模块显示游戏信息,如分数、蛇长度这样。
  • 在仿真当中可以加上复位电路,这样不用每次死亡要点击仿真重新运行。

C51 贪吃蛇 基于 Proteus V1.0相关推荐

  1. 贪吃蛇大作战html5,贪吃蛇大作战5.0.1

    贪吃蛇大作战5.0.1版本是一款用心制作的休闲多人竞技手游,贪吃蛇大作战5.0.1版本游戏吃掉无数对手和食物,各种混乱任务奖励,精致的皮肤效果最为华丽,解锁新地图场景.新颖有趣的通关方式实现了您的所有 ...

  2. C语言实现贪吃蛇小游戏1.0

    C语言实现贪吃蛇小游戏1.0 贪吃蛇游戏要有三个东西:边框.蛇.食物 还有两个灵魂的东西:光标的移动与按键监控 一.光标的移动 在我看来在控制台上移动光标画图是实现这个小游戏的灵魂了,在这之前我一直以 ...

  3. c语言vc6.0编写贪吃蛇,基于VC6.0的贪吃蛇游戏源码

    //******************************************************* //**************************************** ...

  4. 【Android 2d游戏开发(3)】——贪吃蛇(基于surfaceview框架,精讲)

    前两个游戏是基于View游戏框架的,View游戏框架只适合做静止的,异步触发的游戏,如果做一直在动的游戏,View的效率就不高了,我们需要一种同步触发的游戏框架,也就是surfaceview游戏框架, ...

  5. 贪吃蛇python零基础教程_自学python-tkinter项目-贪吃蛇的程序(0基础入门学习)...

    python学习了2个月有点想放弃了(本人英文基本为0,之前也根本没接触过IT行业) 不过贪吃蛇的程序做成,给了继续下去的理由和信心 下面是代码: import tkinter import rand ...

  6. C语言七彩贪吃蛇源代码---AI自动寻径,分数排行,数据保存,背景音乐难度设置等 控制台小游戏

    ------------------------------------------------------------------------------------------- 七彩贪吃蛇小游戏 ...

  7. 重力感应贪吃蛇(C51 MPU6050 8*8LED点阵)

    重力感应贪吃蛇(C51 MPU6050 8*8LED点阵) 本文讲述如何从无到有用C51做一个重力感应的贪吃蛇,包括元件选型.原理图PCB绘制和代码编写. 重力感应贪吃蛇(C51 MPU6050 8* ...

  8. 用pygame做一个简单的python小游戏---贪吃蛇

    用pygame做一个简单的python小游戏-贪吃蛇 贪吃蛇游戏博客链接:(方法一样,语言不一样) c++贪吃蛇:https://blog.csdn.net/weixin_46791942/artic ...

  9. 手把手教你做一个Java贪吃蛇小游戏

    大家好,我是孙不坚1208,这篇博客给大家分享一下:如何做一个贪吃蛇小游戏(Java版)的exe应用程序,希望能给需要帮助的朋友带来方便. 手把手教你做一个Java贪吃蛇小游戏的exe应用程序 一.J ...

最新文章

  1. excel处置4000行数据卡_【Excel技巧】在Excel中奇偶行提取和奇偶行筛选方法
  2. js有默认参数的函数加参数_函数参数:默认,关键字和任意
  3. 鼐鼐家为用户打造3D互动体验式营销解决方案
  4. activiti5第三弹-----------脚本任务
  5. 在石家庄扣完五险一金到手5000,算什么水平?
  6. python : itertools 中的 islice : 获取迭代器结果的切片,消耗迭代器
  7. 如何在form初始化时自动隐藏FOLDER列
  8. 在Docker和Kubernetes上运行MongoDB微服务
  9. 127 MySQL权限管理
  10. Wowza服务器系列(5):使用rtsp协议向wowza推流的wowoza配置方法
  11. Pysyft学习笔记
  12. 使用wget 工具下载网站所有目录文件
  13. 关于获得淘宝商品评论的那些事
  14. 9.1练习题3 梦中的统计 题解
  15. win10九月十五更新补丁导致共享打印机无法连接
  16. 数据可视化——投掷两个6面的骰子
  17. js 鼠标滑轮控制左右横向滚动
  18. 日语初级语法复习 Day 17 Summary(更新中)
  19. 洛谷 P3426 [POI2005]SZA-Template
  20. 获取嵌入式Linux设备下的触摸设备节点

热门文章

  1. 关于小程序部分ios版本flex样式bug修复
  2. Codeforces1395 D. Boboniu Chats with Du(贪心)
  3. 杰里之ANC版级配置【篇】
  4. 谋势、聚力、强生态,用友三十而立
  5. 梯形图能代替c语言,PLC今后会被单片机代替吗?梯形图变成高级语言?
  6. 机器学习会议期刊比较
  7. system,sys密码找回
  8. 如何在Guitar Pro更好的使用打谱
  9. 艾美捷 DetergentOUT GB-S10去垢剂的特点和多种应用
  10. http://www.javaworld.com/javaworld/javatips/jw-jav