前言
之前讲过基于LCD12864的俄罗斯方块小游戏,接下来讲一讲基于8X8点阵的俄罗斯方块
硬件设计
- MCU同样是基于C51、
- 两个8X8点阵组合成游戏显示区域
- 4位数码管作为分数记分牌
仿真图:
程序设计
#include<reg52.h>
#include<stdlib.h>
#include<math.h>
#define uchar unsigned char
sbit slock=P2^7; //译码器输出使能端
sbit upkey=P2^3; //“旋转图形/向上”按键
sbit leftkey=P2^1; //“左移/向左”按键
sbit rightkey=P2^2; //“右移/向右”按键
sbit downkey=P2^0; //“快速下移/向下”按键
sbit duan=P2^5; //数码管段选信号所用锁存器的锁存允许端
sbit wei=P2^6; //数码管位选信号所用锁存器的锁存允许端
sbit startsuspendkey=P2^4; //“开始/暂停/继续”多功能切换按键
/***********************************图形编码机制介绍**************************************
1.由于俄罗斯方块图形的宽度和高度最多只有四位,所以要以4X4为基本单元。
2.硬件采用16行扫描、8位送显示信号
3.各个图形的宽度不一致,所以要人为给图形设定居中位置。
若图形宽度为偶数可直接将其居中,若为奇数则靠左居中。
4.由于图形需要旋转,所以由基本的图形会衍生出另外3种图形。
5.因此每个图形应该给定4个8位的二进制码,并放入一个二维数组里。
6.经典俄罗斯方块游戏里有19种不同形状的方块,包括旋转得到的。
7.数组的第一个下标为该图形的编号
*****************************************************************************************/
uchar code allshape[19][4]={0x00,0x00,0x18,0x18,0x10,0x10,0x10,0x10,0x00,0x00,0x00,0x3c,
0x00,0x08,0x18,0x10,0x00,0x00,0x30,0x18,0x00,0x10,0x18,0x08,
0x00,0x00,0x18,0x30,0x00,0x08,0x08,0x18,0x00,0x00,0x38,0x08,
0x00,0x18,0x10,0x10,0x00,0x00,0x20,0x38,0x00,0x10,0x10,0x18,
0x00,0x00,0x08,0x38,0x00,0x18,0x08,0x08,0x00,0x00,0x38,0x20,
0x00,0x00,0x10,0x38,0x00,0x08,0x18,0x08,0x00,0x00,0x38,0x10,
0x00,0x10,0x18,0x10};
/****************************************************************************************/
uchar code number[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; //数码管数形显示编码
uchar code weima[4]={0x01,0x02,0x04,0x08}; //位选信号编码,方便写循环使用
char shapewidth[19]={2,1,4,2,3,2,3,2,3,2,3,2,3,2,3,3,2,3,2}; //各个图形的宽度属性,用于判断左移和右移的步格数上限
char shaperotate[19]={0,2,1,4,3,6,5,8,9,10,7,12,13,14,11,16,17,18,15}; //旋转图形时,用于改变图形的编号以实现图形的切换
uchar staticdata[20]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xff}; //用于存储方块降落后固定显示的图形信息
uchar rate[5]={0,1,3,6,10}; //规定一次性消掉的行数的分数奖励机制
/****************************************全局变量声明************************************/
char y; //方块位置属性:底部下落的高度,y=0时:刚刚出现,y=15时:下落到屏幕最底部
char shapenum; //方块形状属性:从0到18
int left; //方块位置属性:方块偏离居中位置的格数,不同的方块left值的上限不同
int mark; //玩家分数变量
int speed; //方块下落速度变量
int initialspeed; //方块下落的初始速度
int systemspeed; //系统下落速度,此值会随着玩家分数的增加而减小,相应的下落速度会增大
int fastspeed; //当用户按下down键时,方块下落速度为此值
int k; //一个全局循环变量,“帧数”变量
int startcontrol=1; //开始画面状态指示变量,为1时说明程序进入开机欢迎界面
int suspendcontrol=0; //游戏暂停与游戏继续画面状态指示变量,为1说明处在暂停界面,为0说明处在继续画面
int randnum; //随机数变量,用于产生随机方块
/****************************************************************************************/
/****************************************函数声明****************************************/
void delayms(int); //粗略延时函数
uchar move(uchar,int); //对二进制码实行移位,可为负值
void shapedisplay(); //对点阵扫描一场,对数码管扫描一场,用于显示图形和数字
void keyscan(); //对所有键盘扫描一次,并执行相应运算和操作
uchar check(char,int); //用于检查方块将要下落的位置或是将要旋转的位置是否有障碍
uchar shapedisappear(); //用于消行,返回一次性消掉的行数
numberdisplay(int amark); //用于四位数字显示
void startimage(); //游戏等待开始画面函数
void overimage(); //游戏结束后的画面
void dataset(); //对数据初始化,为游戏重新开始做准备
/****************************************************************************************/
/*****************************************主函数*****************************************/
void main()
{
char j;
startimage(); //进入开机,函数内有键盘扫描,只有按下“开始”键会退出函数,否则不退出
dataset(); //对数据进行初始化
while(1){ //进入大循环
k=speed; //确定显示的帧数
while(k--){
keyscan(); //扫描键盘,放入高速循环语句中提高键盘的响应速度,并检测此时用户是否有相应请求
shapedisplay(); //显示图形和数字
}
y++; //图形下落一格
if(check(shapenum,left)){ //在没有显示之前判断将要下落的位置是否有障碍物,如果有障碍就进入到if语句中
if(y==1){ //如果y=1;说明方块刚出现就遇到障碍物了,这时游戏需结束
overimage(); //进入游戏结束画面,此函数内没有键盘扫描,运行一段时间会自动退出
startimage(); //又进入开机画面
dataset(); //对数据进行初始化,将前一用户数据清零
}
else{ //遇到障碍物,但还不至于图形不能出现
y--; //y回到原值
for(j=0;j<4;j++){ //将下落形状的值赋给固定图形,形成停留显示的效果
staticdata[y+j]+=move(allshape[shapenum][j],left);
}
mark+=rate[shapedisappear()]; //消掉已拼满的函数,并记录所得分数
y=0; //为下一个方块赋属性值:从第一行出现
left=0; //为下一个方块赋属性值:居中显示
randnum=rand()%19;shapenum=randnum; //为下一个方块赋属性值:给定形状
systemspeed=initialspeed-10*(mark/40); //计算相应分数下的系统速度值,分数越高,速度越快
speed=systemspeed; //将此值赋给速度控制量
}
}
}
}
/****************************************************************************************/
/***************************************自定义函数***************************************/
void delayms(int xms) //粗略的延时函数
{
int i,j;
for(i=xms;i>0;i--)
for(j=110;j>0;j--);
}
/****************************************************************************************/
uchar move(uchar aa,int anum) //移位函数
{
if(anum>=0)
aa<<=anum;
else
aa>>=(-anum);
return aa;
}
/****************************************************************************************/
void shapedisplay() //显示函数
{
uchar j;
for(j=y<3?3-y:0;j<4;j++){ //显示动态方块
slock=1;P1=j+y-3;P3=move(allshape[shapenum][j],left);
slock=0;delayms(1);P3=0x00;
}
for(j=0;j<16;j++){ //显示静态方块
slock=1;P1=j;P3=staticdata[j+3];
slock=0;delayms(1);P3=0x00;
}
numberdisplay(mark); //分数显示
}