一直很想写一个贪吃蛇的小程序,这两天终于抽空完成了,这里把我的思路分享给大家,仅供参考!
代码如下:
先放段主函数压压惊
void main()
{
x_s[0] = 2; //初始化三个点
y_s[0] = 3;
x_s[1] = 3;
y_s[1] = 3;
x_s[2] = 4;
y_s[2] = 3;
Timer0Init();
suijishu();
while(1)
{
appear();
if(flag_a) //蛇身移动位
{
if(!flag_s) //蛇身死亡位
{
auto_move();
HC_595(0x00); //消隐
P0 = 0xFF; //消隐
shensi();
chiguo();
delay(1000);
flag_a = 0;
}
}
}
}
/*
本程序核心程序:
appear函数,显示蛇身
auto_move函数,控制蛇身惯性移动 ,同时里面附了判断是否穿墙的一段代码
direction函数,控制蛇的移动方向
suijishu(随机数)函数,随机生成果子
chiguo(吃果)函数,判断是否吃到果子
shensi(身死)函数,碰到自己身体则死亡
*/
#include #include #include #define M 12 //难度系数,12中等,越小越难 typedef unsigned char uchar; typedef unsigned int uint; sbit SRCLK = P3^6; //595芯片 sbit RCLK = P3^5; sbit SER = P3^4; sbit LSA=P2^2; //138译码器 sbit LSB=P2^3; sbit LSC=P2^4; uchar code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值 //定义方向键 sbit K1 = P3^1; //上 sbit K2 = P3^0; //下 sbit K3 = P3^2; //左 sbit K4 = P3^3; //右 uchar m; //储存果子的X坐标 uchar n; //储存果子的Y坐标 uchar snakelong = 3; //初始化蛇长 char flag_x = 1; //初始化运动方向 char flag_y = 0; //初始化运动方向 uchar flag_gg = 0; //生成果子的重复性标志 uchar flag_c = 0, sheshen = 0; //吃果标志 uchar flag_a = 0; //移动标志 uchar flag_s = 0; //死亡标志 uchar x_s[32] = {0},y_s[32] = {0}; //定义蛇的最大长度 uchar code X_[8] = {0x7F,0xBF,0xDF,0xEF,0xF7,0xFB,0xFD,0xFE}; uchar code Y_[8] = {0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01}; /* y1 //定义x,y坐标 y2 //如,我想让(4,2)亮,只需要P0 = X_[4];HC_595 = Y_[2]; y3 y3 y5 y6 y7 y8 x1 x2 x3 x4 x5 x6 7 x8 */ 一个小的延时函数: void suijishu(); //提前定义随机数生成函数 void delay(uint i) //简单的delay函数,对于51单片机,delay(1)约等于9微秒 { while(i--); } 定时器初始化: void Timer0Init() //定时器初始化函数 模式1,16位定时器 { TMOD|=0X01;//选择为定时器0模式,工作方式1,仅用TR0打开启动。 TH0=0XFC; //给定时器赋初值,定时1ms TL0=0X18; ET0=1;//打开定时器0中断允许 EA=1;//打开总中断 TR0=1;//打开定时器 } 两个显示函数,分别控制点阵和数码管; void HC_595(uchar date) //595例程,这里就不详细解释了 { uchar a; SRCLK = 0; RCLK = 0; for(a=0;a<8;a++) { SER = date>>7; date<<=1; SRCLK = 1; _nop_(); _nop_(); SRCLK = 0; } RCLK = 1; _nop_(); _nop_(); RCLK = 0; } void display() //数码管显示,用来显示蛇长 { uchar i,b=2; HC_595(0x00); //消隐 _nop_(); P0=0x00; //因为我用的板子上数码管和点阵共用的P0口,所以这里消隐的时候需要特别注意。 _nop_(); for(i=0;i<2;i++) { while(b) { //这里循环两次是为了让数码管显示更清楚一点。 switch(i) //位选,选择点亮的数码管 { case(0): LSA=0;LSB=0;LSC=0; P0=smgduan[(snakelong%10)];delay(10);P0=0x00;break; case(1): LSA=1;LSB=0;LSC=0; P0=smgduan[(snakelong/10)];delay(10);P0=0x00;break; } _nop_(); b--; } } LSA=1;LSB=1;LSC=1; P0=0xFF; //还是为了消隐。以一个数码管常亮为代价,以求不影响点阵和数码管显示蛇长 } 显示蛇身,核心函数之一: void appear() // viod 蛇身出现 { /* x_s[0] = 2; //初始化三个点 y_s[0] = 3; x_s[1] = 3; y_s[1] = 3; x_s[2] = 4; y_s[2] = 3; */ //uint t = 150; //t越小越难,越大越简单 //这里也可以控制难度,经测试也有效。 //while(t) //{ uchar i; for(i=0;i if((x_s[i]!=0)&&(y_s[i]!=0)) { P0 = X_[x_s[i]-1]; HC_595(Y_[y_s[i]-1]); delay(100); //延时 P0 = 0xFF; //消隐 HC_595(0x00); //消隐 } } // t--; //} } 自动惯性移位函数: void auto_move() // viod 自动移动 核心程序之一 { uchar i; for(i=0;i<(snakelong-1);i++) { x_s[i] = x_s[i+1]; y_s[i] = y_s[i+1]; } x_s[snakelong-1] += flag_x; y_s[snakelong-1] += flag_y; if(x_s[snakelong-1]>8) // void 穿墙判断 x_s[snakelong-1] = 1; else if(x_s[snakelong-1]<1) x_s[snakelong-1] = 8; if(y_s[snakelong-1]>8) y_s[snakelong-1] = 1; else if(y_s[snakelong-1]<1) y_s[snakelong-1] = 8; } 键盘扫描(独立键盘),控制蛇的移动方向: void direction() // void 方向控制 { if(!K1) { delay(1000); if(!K1) { if(flag_y == 1) //是否正在下降 ,若是,则点击上升无用。 {flag_y = 1;} else { flag_x = 0; flag_y = -1; //shang } } } else if(!K2) { delay(1000); if(!K2) { if(flag_y == -1) //是否正在上升 {flag_y = -1;} else { flag_x = 0; flag_y = 1; //xia } } } else if(!K3) { delay(1000); if(!K3) { if(flag_x == 1) //是否正在右移 {flag_x = 1;} else { flag_x = -1; //zuo flag_y = 0; } } } else if(!K4) { delay(1000); if(!K4) { if(flag_x == -1) //是否正在左移 {flag_x = -1;} else { flag_x = 1; //you flag_y = 0; } } } } 吃果函数,吃到果子蛇头增长 void chiguo() // void 吃果判断 { if(((x_s[snakelong-1]+flag_x)==m)&&((y_s[snakelong-1]+flag_y)==n) ) { flag_c = 1; } else if(((x_s[snakelong-1]+flag_x)==m)&&((y_s[snakelong-1]+flag_y)==n) ) //重复两次防止程序bug——穿过果子但不去吃 { flag_c = 1; } else if(((x_s[snakelong-2]+flag_x)==m)&&((y_s[snakelong-2]+flag_y)==n) ) //放宽吃果判定,以防止出现bug——穿过果子但不去吃 { flag_c = 1; sheshen = 1; } else if(((x_s[snakelong-3]+flag_x)==m)&&((y_s[snakelong-3]+flag_y)==n) ) { flag_c = 1; sheshen = 2; } if(flag_c) { flag_c = 0; TR0 = 0;//关闭定时器 snakelong++; if(sheshen == 1) { x_s[snakelong-1] = m+flag_x; y_s[snakelong-1] = n+flag_y; sheshen = 0; } else if(sheshen == 2) { x_s[snakelong-1] = m+2*flag_x; y_s[snakelong-1] = n+2*flag_y; sheshen = 0; } else { //这才是吃果的正常情况 x_s[snakelong-1] = m; y_s[snakelong-1] = n; sheshen = 0; } suijishu(); //再生成一个果子 P0 = 0xFF; //消隐 HC_595(0x00); //消隐 appear(); appear(); P0 = 0xFF; //消隐 HC_595(0x00); //消隐 TR0=1;//打开定时器 } } 碰到自己身体死亡函数 void shensi() // void 身死 碰到自己身死 { uchar b,x_1,y_1; if(snakelong>4) { for(b=0;b if(((x_s[snakelong-1])==x_s[b])&&((y_s[snakelong-1])==y_s[b]) ) { flag_s = 1; } } if(flag_s == 1) { P0 = 0x00;HC_595(0xFF);delay(2000); //闪烁两次 P0 = 0xFF;HC_595(0x00);delay(2000); P0 = 0x00;HC_595(0xFF);delay(3000); P0 = 0xFF;HC_595(0x00);delay(1000); x_1 = x_s[snakelong-1] ; y_1 = y_s[snakelong-1] ; //卡在死亡状态的下一刻 snakelong++; x_s[snakelong-1] = x_1 + flag_x; y_s[snakelong-1] = y_1 + flag_y; } } } 随机生成果子: void suijishu() //随机数,生成果子 { uchar b,m_1,n_1; do{ flag_gg = 0; m_1 = (rand()%8)+1; n_1 = (rand()%8)+1; for(b=0;b if((m_1==x_s[b])&&(n_1==y_s[b])) //如果果子和蛇身有重复,则flag_gg置1,再生成一次 flag_gg = 1; } }while(flag_gg); m = m_1; n = n_1; } 主函数: void main() { x_s[0] = 2; //初始化三个点 y_s[0] = 3; x_s[1] = 3; y_s[1] = 3; x_s[2] = 4; y_s[2] = 3; Timer0Init(); suijishu(); while(1) { appear(); if(flag_a) //蛇身移动位 { if(!flag_s) //蛇身死亡位 { auto_move(); HC_595(0x00); //消隐 P0 = 0xFF; //消隐 shensi(); chiguo(); delay(1000); flag_a = 0; } } } } /* 果子的函数放在了中断里,1ms一次进行显示,同时键盘也是1mS一次进行扫描 通过调节M的大小来实现蛇的延时移动。 */ void guozi() interrupt 1 // void 果子出现 { uint a; uchar i=3; a++; direction(); //用中断去扫描键盘,1mS/次 P0 = 0xFF; //消隐 HC_595(0x00); //消隐 while(i) //重复以增加亮度 { P0 = X_[m-1]; HC_595(Y_[n-1]); i--; } delay(100); P0 = 0xFF; //消隐 ,因为我用的板子上数码管和点阵共用的P0口,所以这里消隐的时候需要特别注意 HC_595(0x00); //消隐 _nop_(); P0=0x00; //数码管消隐 _nop_(); display(); delay(100); HC_595(0x00); //消隐 _nop_(); LSA=1;LSB=1;LSC=1; P0 = 0xFF; if (a>M) //蛇身移动控制位 { a = 0; flag_a = 1; } } /* 编写过程中共累计进行了两次优化,第一次对1,随机生成果子,2,吃果判定的放宽,3,碰到蛇身死亡,以及4,正在上升/下降时,不能下降上升 正在左移/右移时,不能右移/左移 进行了加入以及优化 第二次对数码管显示问题,吃到果子延时增长问题进行了解决,同时对死亡时的效果进行了优化 调试经验: 建议初学者首先构思好程序框架,想清楚程序需要哪几个必要模块(因此建议程序书写模块化),然后 对几个必要模块进行相对独立的调试,进而进行整合,确保基础功能的实现。 在此之后,在对程序出现的问题进行解决和优化。 */
上一篇:基于51单片机的简单计算器
下一篇:51单片机的8x8点阵贪吃蛇
推荐阅读最新更新时间:2024-11-21 10:55
设计资源 培训 开发板 精华推荐
- AN-9718 - FXMA2102 I2C 转换器应用电路
- 使用 Silicon Labs 的 Si1020-A-GM 的参考设计
- 带有VL53L1X长距离测距传感器的分线板,可轻松集成到客户设备中
- 五卡切换的PCB IC卡
- USB 声卡 + 蓝牙5.0模块(已做出实物并验证功能)
- NUCLEO-F411RE,STM32 Nucleo 开发板,带有 STM32F411RET6 MCU,支持 Arduino 和 ST morpho 连接
- LT4275BIDD LTPoE++ 90W 受电设备接口的典型应用电路
- REF43 +2.5V 精密电流源参考电压的典型应用
- L7808A 稳压器的典型应用@ (Vo(min) = Vxx + VBE)
- ETA6002 验证板 V1