基于51单片机和8X8点阵屏、独立按键的跳跃躲闪类小游戏
目录
- 系列文章目录
- 前言
- 一、效果展示
- 二、原理分析
- 三、各模块代码
- 1、8X8点阵屏
- 2、独立按键
- 3、定时器0
- 4、定时器1
- 四、主函数
- 总结
系列文章目录
前言
使用的是普中A2开发板。
【单片机】STC89C52RC
【频率】12T@11.0592MHz
【外设】8X8点阵屏、独立按键
效果查看/操作演示:B站搜索“甘腾胜”或“gantengsheng”查看。
源代码下载:B站对应视频的简介有工程文件下载链接。
一、效果展示
二、原理分析
1、点的上升和下降
如果是匀速上升和匀速下降,很明显不符合物理规律,做的是一个竖直上抛运动,速度均匀变化。
根据匀变速运动的规律,如果物体做初速度为零的匀加速直线运动,经过连续相等位移的时间间隔之比为
1 : 2 − 1 : 3 − 2 : 2 − 3 : . . . 1:\sqrt{2}-1:\sqrt{3}-\sqrt{2}:2-\sqrt{3}:... 1:2−1:3−2:2−3:...
即比值约为
1 : 0.414 : 0.318 : 0.268 : . . . 1:0.414:0.318:0.268:... 1:0.414:0.318:0.268:...
本游戏设置了上升的最大高度为4,上升和下降总共移动8个像素,根据上升和下降的对称性,移动这8次的时间间隔的比例为
0.268 : 0.318 : 0.414 : 1 : 1 : 0.414 : 0.318 : 0.268 0.268:0.318:0.414:1:1:0.414:0.318:0.268 0.268:0.318:0.414:1:1:0.414:0.318:0.268
按照这个比例进行放缩,可以控制快慢,将数据存储在一个数组中,通过定时器计数就可以控制点的上升和下降了,并按竖直上抛的规律上升和下降。
2、障碍物的创造和移动
屏幕的显示需要8个字节,还需要提前创造出障碍物并且进行保存,所以定义一个包含16个字节的数组来存储和显示障碍物的相关信息。
三、各模块代码
1、8X8点阵屏
h文件
#ifndef __MATRIXLED__
#define __MATRIXLED__extern unsigned char DisplayBuffer[];
void MatrixLED_Clear(void);
void MatrixLED_Init(void);
void MatrixLED_MoveLeft(unsigned char *Array,unsigned int Offset);
void MatrixLED_MoveUp(unsigned char *Array,unsigned int Offset);
void MatrixLED_Tick(void);
void MatrixLED_ShowPoint(unsigned char X,unsigned char Y,unsigned char State);
unsigned char MatrixLED_GetPoint(unsigned char X,unsigned char Y);#endif
c文件
#include <REGX52.H>/*引脚定义*/sbit _74HC595_DS=P3^4; //串行数据输入
sbit _74HC595_STCP=P3^5; //储存寄存器时钟输入,上升沿有效
sbit _74HC595_SHCP=P3^6; //移位寄存器时钟输入,上升沿有效/*
每一个B对应一个灯。缓存数组DisplayBuffer的8个字节分别对应这8列,高位在下
B0 B0 B0 B0 B0 B0 B0 B0
B1 B1 B1 B1 B1 B1 B1 B1
B2 B2 B2 B2 B2 B2 B2 B2
B3 B3 B3 B3 B3 B3 B3 B3
B4 B4 B4 B4 B4 B4 B4 B4
B5 B5 B5 B5 B5 B5 B5 B5
B6 B6 B6 B6 B6 B6 B6 B6
B7 B7 B7 B7 B7 B7 B7 B7
*///想要改变显示内容,改变数组DisplayBuffer的数据就行了,由定时器自动扫描
unsigned char DisplayBuffer[8];/*函数定义*//*** 函 数:LED点阵屏清空显示* 参 数:无* 返 回 值:无* 说 明:直接更改缓存数组的数据就行了,由定时器自动扫描显示*/
void MatrixLED_Clear(void)
{unsigned char i;for(i=0;i<8;i++){DisplayBuffer[i]=0;}
}/*** 函 数:MatrixLED初始化(即74HC595初始化)* 参 数:无* 返 回 值:无*/
void MatrixLED_Init(void)
{_74HC595_SHCP=0; //移位寄存器时钟信号初始化_74HC595_STCP=0; //储存寄存器时钟信号初始化MatrixLED_Clear(); //点阵屏清屏
}/*** 函 数:74HC595写入字节* 参 数:Byte 要写入的字节* 返 回 值:无*/
void _74HC595_WriteByte(unsigned char Byte)
{unsigned char i;for(i=0;i<8;i++) //循环8次{_74HC595_DS=Byte&(0x01<<i); //低位先发_74HC595_SHCP=1; //SHCP上升沿时,DS的数据写入移位寄存器_74HC595_SHCP=0;}_74HC595_STCP=1; //STCP上升沿时,数据从移位寄存器转存到储存寄存器_74HC595_STCP=0;
}/*** 函 数:8X8LED点阵屏显示数组内容* 参 数:Array 传递过来的数组的首地址(即指针),数组名就是数组的首地址* 返 回 值:Offset 偏移量,向左偏移Offset个像素* 说 明:要求逐列式取模,高位在下*/
void MatrixLED_MoveLeft(unsigned char *Array,unsigned int Offset)
{unsigned char i;Array+=Offset;for(i=0;i<8;i++){DisplayBuffer[i]=*Array;Array++; //地址自增}
}/*** 函 数:8X8LED点阵屏显示数组内容* 参 数:Array 传递过来的数组的首地址(即指针),数组名就是数组的首地址* 返 回 值:Offset 显示数组数据的偏移量,向上偏移Offset个像素* 说 明:要求逐列式取模,高位在下*/
void MatrixLED_MoveUp(unsigned char *Array,unsigned int Offset)
{unsigned char i,m,n;m=Offset/8;n=Offset%8;Array+=m*8;for(i=0;i<8;i++){DisplayBuffer[i]=(*Array>>n)|(*(Array+8)<<(8-n));Array++;}
}/*** 函 数:LED点阵屏驱动函数,中断中调用* 参 数:无* 返 回 值:无*/
void MatrixLED_Tick(void)
{static unsigned char i=0; //定义静态变量P0=0xFF; //消影_74HC595_WriteByte(DisplayBuffer[i]); //将数据写入到74HC595中锁存P0=~(0x80>>i); //位选,低电平选中i++; //下次进中断后扫描下一列i%=8; //显示完第八列后,又从第一列开始显示
}/*** 函 数:MatrixLED在指定位置按指定状态显示一个点* 参 数:X 指定点的横坐标,范围:0~7* 参 数:Y 指定点的纵坐标,范围:0~7* 参 数:State 亮灭状态,0:灭,1:亮* 返 回 值:无* 说 明:左上角的LED为原点(0,0),向右为X轴正方向,向下为Y轴正方向*/
void MatrixLED_ShowPoint(unsigned char X,unsigned char Y,unsigned char State)
{if(State==0){if(X>=0 && X<=7 && Y>=0 && Y<=7){ DisplayBuffer[X] &= ~(0x01<<Y); }}else if(State==1){if(X>=0 && X<=7 && Y>=0 && Y<=7){ DisplayBuffer[X] |= 0x01<<Y; }}
}/*** 函 数:MatrixLED获取其中一个LED的状态* 参 数:X 指定点的横坐标,范围:0~7* 参 数:Y 指定点的纵坐标,范围:0~7* 返 回 值:LED的亮灭状态,1:亮,0:灭,2:说明超出了屏幕范围* 说 明:左上角的LED为原点(0,0),向右为X轴正方向,向下为Y轴正方向*/
unsigned char MatrixLED_GetPoint(unsigned char X,unsigned char Y)
{if(X>=0 && X<=7 && Y>=0 && Y<=7){if( DisplayBuffer[X] & (0x01<<Y) ) {return 1;}else {return 0;}}else {return 2;}
}
2、独立按键
h文件
#ifndef __KEYSCAN_H__
#define __KEYSCAN_H__unsigned char Key(void);
void Key_Tick(void);#endif
c文件
#include <REGX52.H>sbit Key1=P3^1;
sbit Key2=P3^0;
sbit Key3=P3^2;
sbit Key4=P3^3;unsigned char KeyNumber;/*** 函 数:获取独立按键键码* 参 数:无* 返 回 值:按下按键的键码,范围:0~12,0表示无按键按下* 说 明:在下一次检测按键之前,第二次获取键码一定会返回0*/
unsigned char Key(void)
{unsigned char KeyTemp=0;KeyTemp=KeyNumber;KeyNumber=0;return KeyTemp;
}/*** 函 数:按键驱动函数,在中断中调用* 参 数:无* 返 回 值:无*/
void Key_Tick(void)
{static unsigned char NowState,LastState;
// static unsigned int KeyCount;LastState=NowState; //保存上一次的按键状态NowState=0; //如果没有按键按下,则NowState为0//获取当前按键状态if(Key1==0){NowState=1;}if(Key2==0){NowState=2;}if(Key3==0){NowState=3;}if(Key4==0){NowState=4;}//如果上个时间点按键未按下,这个时间点按键按下,则是按下瞬间if(LastState==0){switch(NowState){case 1:KeyNumber=1;break;case 2:KeyNumber=2;break;case 3:KeyNumber=3;break;case 4:KeyNumber=4;break;default:break;}}//如果上个时间点按键按下,这个时间点按键按下,则是一直按住按键if(LastState && NowState){
// KeyCount++;
// if(KeyCount%5==0) //定时器中断函数中每隔20ms检测一次按键
// { //长按后每隔100ms返回一次长按的键码if (LastState==1 && NowState==1){KeyNumber=5;}else if(LastState==2 && NowState==2){KeyNumber=6;}else if(LastState==3 && NowState==3){KeyNumber=7;}else if(LastState==4 && NowState==4){KeyNumber=8;}
// }
// }
// else
// {
// KeyCount=0;}//如果上个时间点按键按下,这个时间点按键未按下,则是松手瞬间if(NowState==0){switch(LastState){case 1:KeyNumber=9;break;case 2:KeyNumber=10;break;case 3:KeyNumber=11;break;case 4:KeyNumber=12;break;default:break;}}}
3、定时器0
h文件
#ifndef __TIMER0_H__
#define __TIMER0_H__void Timer0_Init(void);#endif
c文件
#include <REGX52.H>/*** 函 数:定时器0初始化* 参 数:无* 返 回 值:无*/
void Timer0_Init(void)
{ TMOD&=0xF0; //设置定时器模式(高四位不变,低四位清零)TMOD|=0x01; //设置定时器模式(通过低四位设为16位不自动重装)TL0=0x66; //设置定时初值,定时1ms,12T@11.0592MHzTH0=0xFC; //设置定时初值,定时1ms,12T@11.0592MHzTF0=0; //清除TF0标志TR0=1; //定时器0开始计时ET0=1; //打开定时器0中断允许EA=1; //打开总中断PT0=0; //当PT0=0时,定时器0为低优先级,当PT0=1时,定时器0为高优先级
}/*定时器中断函数模板
void Timer0_Routine() interrupt 1 //定时器0中断函数
{static unsigned int T0Count; //定义静态变量TL0=0x66; //设置定时初值,定时1ms,12T@11.0592MHzTH0=0xFC; //设置定时初值,定时1ms,12T@11.0592MHzT0Count++;if(T0Count>=1000){T0Count=0;}
}
*/
4、定时器1
h文件
#ifndef __TIMER1_H__
#define __TIMER1_H__void Timer1_Init(void);#endif
c文件
#include <REGX52.H>/*** 函 数:定时器1初始化* 参 数:无* 返 回 值:无*/
void Timer1_Init(void)
{TMOD&=0x0F; //设置定时器模式(低四位不变,高四位清零)TMOD|=0x10; //设置定时器模式(通过高四位设为16位不自动重装的模式)TL1=0x66; //设置定时初值,定时1ms,12T@11.0592MHzTH1=0xFC; //设置定时初值,定时1ms,12T@11.0592MHzTF1=0; //清除TF1标志TR1=1; //定时器1开始计时ET1=1; //打开定时器1中断允许EA=1; //打开总中断PT1=1; //当PT1=0时,定时器1为低优先级,当PT1=1时,定时器1为高优先级
}/*定时器中断函数模板
void Timer1_Routine() interrupt 3 //定时器1中断函数
{static unsigned int T1Count; //定义静态变量TL1=0x66; //设置定时初值,定时1ms,12T@11.0592MHzTH1=0xFC; //设置定时初值,定时1ms,12T@11.0592MHzT1Count++;if(T1Count>=1000){T1Count=0;}
}
*/
四、主函数
main.c
/*by甘腾胜@20250603
【效果查看/操作演示】B站搜索“甘腾胜”或“gantengsheng”查看
【单片机】STC89C52RC
【频率】12T@11.0592MHz
【外设】8X8LED点阵屏、独立按键
【简单的原理分析】https://blog.csdn.net/gantengsheng/article/details/143581157
【注意】点阵屏旁边的跳线帽要接三个排针的左边两个
【操作说明】
(1)循环滚动显示游戏英文名的界面按任意按键开始游戏
(2)K1按键控制跳跃
(3)游戏时按K4可以暂停或继续游戏
(4)游戏结束全屏闪烁界面按K4进入滚动显示得分的英文的界面
(5)滚动显示得分的英文的界面可按K4跳过
(6)循环滚动显示得分界面可按K3返回,重新开始游戏
*/#include <REGX52.H> //51单片机头文件
#include "MatrixLED.h" //8X8点阵屏
#include "KeyScan.h" //独立按键
#include "Timer0.h" //定时器0
#include "Timer1.h" //定时器1
#include <STDLIB.H> //随机函数unsigned char KeyNum; //存储获取的键码
unsigned char Mode; //游戏模式,0:循环滚动显示游戏英文名,1:游戏中,2:游戏结束全屏闪烁,3:滚动显示得分的英文,4:循环滚动显示得分
bit OnceFlag; //各模式中切换为其他模式前只执行一次的标志(类似于主函数主循环前的那部分,用于该模式的初始化),1:执行,0:不执行
bit FlashFlag; //闪烁的标志,1:不显示,0:显示
bit GameOverFlag; //游戏结束的标志,1:游戏结束,0:游戏未结束
unsigned char Offset; //偏移量,用来控制字母或数字向左滚动显示
bit RollFlag; //字母或数字滚动一个像素的标志,1:滚动,0:不滚动
bit MoveFlag; //障碍物向左移动一个像素的标志,1:移动,0:不移动
unsigned int Duration; //障碍物移动的时间间隔,单位是1ms
unsigned int Score; //游戏得分,范围:0~65535
unsigned char ScoreLength; //游戏得分的位数,范围:1~5
bit PauseFlag; //暂停的标志,1:暂停,0:继续
unsigned int T0Count_0; //定时器0计数全局变量
unsigned char T0Count_1; //定时器0计数全局变量
unsigned char Player; //玩家高度,范围:0~4,对应第8行到第4行
bit JumpFlag; //跳跃的标志,1:已跳起(在空中),0:在地面
unsigned char idata Obstacles[16]; //障碍物,idata:变量存储在片内间接访问区
unsigned char ShowPointer; //现在屏幕显示的第一列的内容对应数组Obstacles中的位置,范围:0~15,0:对应数组索引0,1:对应数组索引1,以此类推
unsigned char CreatPointer; //即将要创造的障碍物对应数组Obstacles中的位置,范围:0~15,0:对应数组索引0,1:对应数组索引1,以此类推
unsigned char ColumnQuantity; //创造的障碍物的列数,范围:1~2
unsigned char xdata ScoreShow[]={ //游戏得分(用于滚动显示),xdata:外部扩展RAM
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 无显示
0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00, // 得分最多五位数,每一个数字对应6个字节
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 无显示
};
unsigned char code JumpDuration[]={9,11,14,34,34,14,11,9,}; //按竖直上抛的规律上升和下降
unsigned char NowJumpCount; //跳起后移动的像素的计数
unsigned char LastJumpCount; //跳起后移动的像素的计数(上一次)//取模要求:阴码(亮点为1),纵向取模,高位在下
//我分享的工程文件夹中有6X8像素的ASCII字符字模
//code:数据保存在flash中
unsigned char code Table1[]={ //游戏名称“跳跃或者死亡”的英文:<<JUMP OR DIE>>,宽6高8
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 无显示
0x00,0x08,0x14,0x22,0x49,0x14,0x22,0x41, // << 宽8高8(自定义书名号:两个小于号)
0x00,0x20,0x40,0x41,0x3F,0x01, // J
0x00,0x3F,0x40,0x40,0x40,0x3F, // U
0x00,0x7F,0x02,0x0C,0x02,0x7F, // M
0x00,0x7F,0x09,0x09,0x09,0x06, // P
0x00,0x00,0x00,0x00,0x00,0x00, //
0x00,0x3E,0x41,0x41,0x41,0x3E, // O
0x00,0x7F,0x09,0x19,0x29,0x46, // R
0x00,0x00,0x00,0x00,0x00,0x00, //
0x00,0x7F,0x41,0x41,0x22,0x1C, // D
0x00,0x00,0x41,0x7F,0x41,0x00, // I
0x00,0x7F,0x49,0x49,0x49,0x41, // E
0x00,0x41,0x22,0x14,0x49,0x22,0x14,0x08, // >>
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 无显示
};
unsigned char code Table2[]={ //“得分”的英文:“SCORE”,宽6高8
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //无显示
0x00,0x46,0x49,0x49,0x49,0x31, // S
0x00,0x3E,0x41,0x41,0x41,0x22, // C
0x00,0x3E,0x41,0x41,0x41,0x3E, // O
0x00,0x7F,0x09,0x19,0x29,0x46, // R
0x00,0x7F,0x49,0x49,0x49,0x41, // E
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //无显示
};
unsigned char code Table3[]={ //游戏得分的字模数据,宽6高8
0x00,0x3E,0x51,0x49,0x45,0x3E, // 0
0x00,0x00,0x42,0x7F,0x40,0x00, // 1
0x00,0x42,0x61,0x51,0x49,0x46, // 2
0x00,0x21,0x41,0x45,0x4B,0x31, // 3
0x00,0x18,0x14,0x12,0x7F,0x10, // 4
0x00,0x27,0x45,0x45,0x45,0x39, // 5
0x00,0x3C,0x4A,0x49,0x49,0x30, // 6
0x00,0x01,0x71,0x09,0x05,0x03, // 7
0x00,0x36,0x49,0x49,0x49,0x36, // 8
0x00,0x06,0x49,0x49,0x29,0x1E, // 9
};/*** 函 数:幂函数/指数函数* 参 数:X 底* 参 数:Y 幂* 返 回 值:X的Y次方* 说 明:辅助取出Score中的某一位数字*/
unsigned int Pow(unsigned char X,unsigned char Y)
{unsigned char i;unsigned int Result=1;for(i=0;i<Y;i++){Result*=X;}return Result;
}/*** 函 数:主函数(有且仅有一个)* 参 数:无* 返 回 值:无* 说 明:主函数是程序执行的起点,负责执行整个程序的主要逻辑*/
void main()
{unsigned char i,j; //For循环用到的临时变量P2_5=0; //防止开发板上的蜂鸣器发出声音Timer0_Init(); //定时器0初始化Timer1_Init(); //定时器1初始化MatrixLED_Init(); //点阵屏初始化while(1){KeyNum=Key(); //获取键码/*键码处理*/if(KeyNum){srand(TL0); //每次获取非零键码都用定时器0的低8位做种子,从而产生真随机数if(Mode==0) //如果是循环滚动显示游戏英文名的界面{if(KeyNum>=9 && KeyNum<=12) //如果按下任意按键(松手瞬间){Mode=1; //切换到模式1OnceFlag=1; //切换模式前只执行一次的标志置1}}else if(Mode==1) //如果是正在游戏的界面{if( (KeyNum==1 || KeyNum==5) && JumpFlag==0 && PauseFlag==0) //如果按下K1(按下瞬间或长按),且不是在空中,且不是暂停状态{JumpFlag=1; //跳跃的标志置1NowJumpCount=0; //跃起后移动的像素的计数清零LastJumpCount=0; //跃起后移动的像素的计数(上一次)清零T0Count_1=0; //定时器0的全局计数变量清零}if(KeyNum==12) //如果按下K4(松手瞬间){PauseFlag=!PauseFlag; //置反暂停的标志if(PauseFlag==0){MatrixLED_ShowPoint(2,7-Player,0);} //由暂停变成继续,则清除玩家位置的显示(防止继续时出错)}}else if(Mode==2) //如果是游戏结束全屏闪烁的界面{if(KeyNum==12) //如果按下K4(松手瞬间){Mode=3;OnceFlag=1;}}else if(Mode==3) //如果是滚动显示英文“SCORE”的界面{if(KeyNum==12) //如果按下K4(松手瞬间){Mode=4;OnceFlag=1;}}else if(Mode==4) //如果是循环滚动显示得分的界面{if(KeyNum==11) //如果按下K3(松手瞬间){Mode=1; //重新开始游戏OnceFlag=1;}}}/*游戏处理*/if(Mode==0) //循环滚动显示游戏英文名{if(OnceFlag) //切换到其他模式前,此if中的代码只执行1次{OnceFlag=0; //只执行一次的标志置0Offset=0; //滚动显示的偏移量清零}if(RollFlag) //如果滚动的标志RollFlag为真(非零即真){RollFlag=0; //滚动的标志RollFlag置0MatrixLED_MoveLeft(Table1,Offset); //更新显示Offset++; //每次向左移动一个像素Offset%=90; //越界清零,循环滚动显示}}else if(Mode==1) //游戏进行中{if(OnceFlag){OnceFlag=0;//游戏初始化MatrixLED_Clear(); //清屏PauseFlag=0; //暂停的标志置0GameOverFlag=0; //游戏结束的标志置0Score=0; //得分清零Duration=250; //障碍物移动的时间间隔重置,每隔250ms移动一次ShowPointer=0; //屏幕正在显示的内容的第一列对应缓存数组中的数据的位置CreatPointer=8; //即将创造的障碍物对应缓存数组中的数据的位置JumpFlag=0; //跳跃的标志置0NowJumpCount=0; //跃起后移动的像素的计数清零LastJumpCount=0; //跃起后移动的像素的计数(上一次)清零Player=0; //玩家的初始高度为0MatrixLED_ShowPoint(2,7,1); //玩家初始位置在3列8行for(i=0;i<16;i++){Obstacles[i]=0;} //清空障碍物缓存数组的数据for(i=0;i<46;i++){ScoreShow[i]=0;} //清空分数显示的缓存数组的数据MoveFlag=0; //障碍物移动的标志置0T0Count_0=0; //定时器0全局计数变量清零}while( ( (CreatPointer>ShowPointer) && ((ShowPointer+16-CreatPointer)>=7) ) || ( (CreatPointer<ShowPointer) && ((ShowPointer-CreatPointer)>=7) ) ){ //如果缓存数组中未创造障碍物的区域足够多ColumnQuantity=rand()%2+1; //障碍物的列数,范围:1~2for(i=0;i<ColumnQuantity;i++){Obstacles[CreatPointer]=rand()%3+1; //障碍物高度范围:1~3CreatPointer++;CreatPointer%=16;}for(i=0;i<7-ColumnQuantity;i++) //障碍物之间的间隙{Obstacles[CreatPointer]=0;CreatPointer++;CreatPointer%=16;}}if(PauseFlag==0) //如果不是暂停状态{if(JumpFlag) //如果正在跳跃{if(NowJumpCount!=LastJumpCount) //如果跃起后移动的像素的计数跟上一次不一样{if(NowJumpCount>=5 && NowJumpCount<=8) //如果正在下降{if( MatrixLED_GetPoint(2,NowJumpCount-1)==1 ) //检测玩家将要移动到的下一个位置是否已经点亮{ //如果是Mode=2; //则游戏结束GameOverFlag=1; //游戏结束的标志置1}}if(GameOverFlag==0) //如果游戏未结束{if(NowJumpCount>=1 && NowJumpCount<=4) //上升{MatrixLED_ShowPoint(2,8-NowJumpCount,0); //清除上一个位置的显示MatrixLED_ShowPoint(2,7-NowJumpCount,1); //点亮下一个位置的LED}if(NowJumpCount>=5 && NowJumpCount<=8) //下降{MatrixLED_ShowPoint(2,NowJumpCount-2,0); //清除上一个位置的显示MatrixLED_ShowPoint(2,NowJumpCount-1,1); //点亮下一个位置的LED}if(NowJumpCount==8) //如果回到地面{NowJumpCount=0; //计数清零JumpFlag=0; //跳跃的标志置0}LastJumpCount=NowJumpCount; //更新变量的值}}}if(MoveFlag) //如果移动的标志为真{MoveFlag=0; //移动的标志置0if( MatrixLED_GetPoint(3,7-Player)==1 ) //如果玩家右侧的LED是点亮的{Mode=2; //则游戏结束GameOverFlag=1;}if(GameOverFlag==0) //如果游戏未结束{ShowPointer++;ShowPointer%=16;for(i=0;i<8;i++) //更新后三行的显示{if(Obstacles[(ShowPointer+i)%16]==0){MatrixLED_ShowPoint(i,7,0);MatrixLED_ShowPoint(i,6,0);MatrixLED_ShowPoint(i,5,0);}else if(Obstacles[(ShowPointer+i)%16]==1){MatrixLED_ShowPoint(i,7,1);MatrixLED_ShowPoint(i,6,0);MatrixLED_ShowPoint(i,5,0);}else if(Obstacles[(ShowPointer+i)%16]==2){MatrixLED_ShowPoint(i,7,1);MatrixLED_ShowPoint(i,6,1);MatrixLED_ShowPoint(i,5,0);}else if(Obstacles[(ShowPointer+i)%16]==3){MatrixLED_ShowPoint(i,7,1);MatrixLED_ShowPoint(i,6,1);MatrixLED_ShowPoint(i,5,1);}}if(NowJumpCount==LastJumpCount) //如果玩家移动的计时未到{MatrixLED_ShowPoint(2,7-Player,1); //重新显示玩家位置}}if( MatrixLED_GetPoint(1,7)==1 && MatrixLED_GetPoint(2,7)==0 ) //如果过了障碍物{Score++; //分数加一for(i=0;i<8;i++) //第一行显示Score的低八位的值(即除以256的余数){if( Score & (0x01<<i) ){MatrixLED_ShowPoint(7-i,0,1);}else{MatrixLED_ShowPoint(7-i,0,0);}}for(i=0;i<8;i++) //第二行显示Score高八位的值(即除以256的商){if( Score & (0x01<<(i+8)) ){MatrixLED_ShowPoint(7-i,1,1);}else{MatrixLED_ShowPoint(7-i,1,0);}}}}}else //如果是暂停状态{if(FlashFlag) //如果闪烁的标志为真{MatrixLED_ShowPoint(2,7-Player,0); //不显示}else{MatrixLED_ShowPoint(2,7-Player,1); //显示}}}else if(Mode==2) //游戏结束全屏闪烁{//在定时器1中实现全屏闪烁}else if(Mode==3) //滚动显示得分的英文“SCORE”{if(OnceFlag){OnceFlag=0;Offset=0;}if(RollFlag && Offset<=38) //只滚动显示一次英文{RollFlag=0;MatrixLED_MoveLeft(Table2,Offset);Offset++;}else if(Offset>38) //滚动结束后,自动切换到循环滚动显示得分的模式{Mode=4;OnceFlag=1;} }else if(Mode==4) //循环滚动显示得分{if(OnceFlag){OnceFlag=0;Offset=0;//判断得分是多少位数if(Score>=10000){ScoreLength=5;}else if(Score>=1000){ScoreLength=4;}else if(Score>=100){ScoreLength=3;}else if(Score>=10){ScoreLength=2;}else{ScoreLength=1;}//将得分的字模写入数组ScoreShow中for(j=0;j<ScoreLength;j++){for(i=0;i<6;i++){ScoreShow[8+6*j+i]=Table3[(Score/Pow(10,ScoreLength-1-j)%10)*6+i];}}}if(RollFlag){RollFlag=0;MatrixLED_MoveLeft(ScoreShow,Offset);Offset++;Offset%=8+ScoreLength*6; //循环滚动显示}}}
}/*** 函 数:定时器0中断函数* 参 数:无* 返 回 值:无*/
void Timer0_Routine() interrupt 1
{static unsigned char T0Count0,T0Count1,T0Count2; //定时器计数变量TL0=0x00; //设置定时初值,定时10ms,12T@11.0592MHzTH0=0xDC; //设置定时初值,定时10ms,12T@11.0592MHzT0Count0++;T0Count1++;T0Count2++;if(PauseFlag==0) //不暂停时才计数{T0Count_0++;T0Count_1++;}if(T0Count0>=2) //每隔20ms检测一次键码{T0Count0=0;Key_Tick();}if(T0Count1>=50) //每隔500ms置反FlashFlag{T0Count1=0;FlashFlag=!FlashFlag;}if(T0Count2>=10) //每隔100ms滚动显示一次字母或数字{T0Count2=0;RollFlag=1;}if(T0Count_0>=Duration/10) //每隔Duration ms移动一次障碍物(移动一个像素){T0Count_0=0;MoveFlag=1;}if(T0Count_1>=JumpDuration[NowJumpCount] && JumpFlag) //跃起后如果计时到了{T0Count_1=0;NowJumpCount++; //通过NowJumpCount控制玩家移动一个像素if(NowJumpCount>=4){Player=8-NowJumpCount;}else{Player=NowJumpCount;}}
}/*** 函 数:定时器1中断函数* 参 数:无* 返 回 值:无* 说 明:专门用定时器1来扫描显示LED点阵屏,定时器1的优先级要比定时器0的高,否则显示会有闪烁现象*/
void Timer1_Routine() interrupt 3
{TL1=0x66; //设置定时初值,定时1ms,12T@11.0592MHzTH1=0xFC; //设置定时初值,定时1ms,12T@11.0592MHzif(Mode==2 && FlashFlag){P0=0xFF;} //控制游戏结束后的全屏闪烁else{MatrixLED_Tick();}
}
总结
有两个移动,障碍物向左移动,玩家控制的点的上升和下降,主要是玩家控制的点在下降的时候与障碍物的碰撞的检测稍复杂一些。