C语言实现!基于51单片机的俄罗斯方块小游戏(文章末尾带源码!)
基于51单片机8*8点阵屏的俄罗斯方块小程序,逻辑代码和设计思路,文章结尾附带源码
本项目所使用到的开发板子是普中A2开发板,使用到的MCU产品是STC-89C52RC,开头先说明,本项目是不在校大学生自己学习写出来的第一个单片机小程序,谢邀,本人打算速速加入嵌入式单片机大军!欢迎友好交流与讨论呀。下面是项目演示和项目工程介绍,文章末尾附带源码!
话不多说,先看项目的运行效果!
1.运行效果和操作指南
0.把代码复制到uVision中生成.hex文件,再用STC-ISP烧录程序把代码写入单片机中(略)
图1.当程序烧录到单片机时的开机画面
1.开机!LED点阵屏幕(以下简称屏幕)会显示“S1”即代表当按下矩阵键盘上的“S1”按键即可开始游戏,部分操作如下图所示
图2.按键操作与功能提示
2.按下开始按钮(矩阵键盘S1)开始游戏!
图3.1、图3.2 游戏运行&结束时的图片
此时只需要点按K1与K2按键即可实现方块左移和右移,K3和K4按键可以让方块下落速度放慢可加快,点按S1即可实现游戏暂停的操作(会由LED的D6发出提示音,屏幕会闪烁),当方块到达最上方的格子时,游戏结束!屏幕闪烁三下,当游戏结束时,在计分板5、6、7的位置显示你的分数(三位数),当完成一次方块的消除,就可以增加1分,以此类推显示玩家分数。
3.最后按下重置按钮,开始新的游戏!(略)
2.逻辑和代码部分
1.代码中,用到的几个基础程序有:矩阵LED程序、延时基础程序、独立键盘基础程序、矩阵键盘基础程序、数码管基础程序。这些代码的基础原理,详细可以移步观看其他基础视频,这里不过多介绍。基础的代码如下:
#include <REGX52.H>
typedef unsigned char u8;//经常用到的定义变量用的方法(好处:短+不占地)
//延时模块(两个)------------------------------
void D1(unsigned int time) // 快速延时(不到一毫秒)
{
unsigned char data i, j;
while (time--)
{
i = 1;
j = 1;
do
{
while (--j)
;
} while (--i);
}
}
void Delay(unsigned int time) // lms延时
{
unsigned char data i, j;
while (time--)
{
i = 2;
j = 239;
do
{
while (--j)
;
} while (--i);
}
}
//延时模块------------------------------
//矩阵LED模块显示模块------------------------------
sbit SRCLK = P3 ^ 6;
sbit rCLK = P3 ^ 5;
sbit SER = P3 ^ 4;
#define LEDDZ_COL_PORT P0
void h595_write_data(u8 dat) // 8*8点阵灯显示模块
{
u8 i = 0;
for (i = 0; i < 8; i++)
{
SER = dat >> 7;
dat <<= 1;
SRCLK = 0;
D1(1);
SRCLK = 1;
D1(1);
}
rCLK = 0;
D1(1);
rCLK = 1;
}
//矩阵LED模块显示模块------------------------------
//数码管------------------------------
u8 num1[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F}; //其实只用到5,6,7位置
void nix(unsigned char num,unsigned char nu1)// 得分计算版函数,num是位置,nu是显示的数字
{
switch(num)
{
case 1:P2_4=1;P2_3=1;P2_2=1;break;
case 2:P2_4=1;P2_3=1;P2_2=0;break;
case 3:P2_4=1;P2_3=0;P2_2=1;break;
case 4:P2_4=1;P2_3=0;P2_2=0;break;
case 5:P2_4=0;P2_3=1;P2_2=1;break;
case 6:P2_4=0;P2_3=1;P2_2=0;break;
case 7:P2_4=0;P2_3=0;P2_2=1;break;
case 8:P2_4=0;P2_3=0;P2_2=0;break;
}
P0=num1[nu1];
}
//数码管------------------------------
void main()//啥都没干的main函数。。
{
u8 score = 0;//玩家的分数
u8 gled_arry[8] = {0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0xFE}; // 显示LCD的每行内容,不需要修改
u8 gled_row[8] = {0x66, 0x91, 0x89, 0x66, 0x00, 0x41, 0xFF, 0x01}; // 使用时显示里面内容
u8 i;
while (1)
{
LEDDZ_COL_PORT = gled_arry[i]; // 让led亮起来
h595_write_data(gled_row[i]); // 让led亮起来
i++;
D1(1);
h595_write_data(0x00); // 让led亮起来
if (i == 8)
i = 0;
P2_5 = 0;
}
}
2.基本逻辑以及代码如下:
以上代码写完之后,直接烧录到单片机中,就会显示S1,其中gled_row[8]数组中所储存的十六进制变量,在转为二进制后为:
0 1 1 0 0 0 1 0
1 0 0 1 0 1 1 0
1 0 0 1 0 0 1 0
0 1 0 0 0 0 1 0
0 0 1 0 0 0 1 0
1 0 0 1 0 0 1 0
1 0 0 1 0 0 1 0
0 1 1 0 0 1 1 1
从上往下看,gled_row[0]=01100110=0x66,gled_row[1]=10001001=0x91...每个十六进制数转化为二进制并竖向排列,其中0代表矩阵LED灯灭,1代表灯亮。上机的效果如下:
是不是很直观了?现在,就可以开始思考我们的方块的逻辑程序了,那我们又该如何将我们想表达的形状记录下来,转化为16进制再输出到屏幕上呢?
我的解决办法就是:二维数组!用一个二维数组储存其需要亮灯时的逻辑地址,再写一个程序将逻辑地址转化为16进制,这样就能被机器识别了。
unsigned int Ar[8][8] ={0}; //这个是逻辑二维数组,统计具体哪个灯亮哪个灯没亮的
现在里面都是0,代表灯全灭了
方块的移动逻辑,我是通过两个一维数组来实现,在cube层,方块每单位时间内下落一次,在没有碰到底时方块可以被左右移动,当碰到底时,方块从cube层加到背景层,再更新新的方块,此时旧的方块已经成为背景的一部分,不能移动了。
u8 backG[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // 背景板所对应的16进制数具体物理地址,当方块落地时更新,一个很重要的东西
u8 gled_row[8] = {0}; // 在背景板之外活动的方块的图层是实时方块的物理地址
上面的gled_row和之前定义的gled_row是一个东西,这里只是展示
接下来添加方块种类,我一共添加了四种,用了两个结构体来储存这四种不同的方块,分别是竖条方块、横条方块、三角方块1、三角方块2。以及他们下降时,其物理地址所对应的16进制数组,以下是方块图片和实现逻辑。
typedef struct cubes
{ // 竖条方块的结构体
u8 cArr;
u8 ctrL[8];
} cube1; // 小的
typedef struct cube
{ // 三角方块和横条方块的结构体
u8 cArr;
u8 ctrL[8];
u8 ctrR[8];
} cube2; // 大的
void main()
{
//以下两个cu代表分别代表不同方块每下降一个位置所对应的具体16进制地址的数组
cube1 cuL = {3, {0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03}}; // 竖条方块
cube2 cu_[3] = {{3, {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}, {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}},
{3, {0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03}, {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02}},
{3, {0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}, {0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03}}};
// 横条、三角方块1、2
}
其中,cArr代表了方块下落的位置是其所对应逻辑地址的第三列开始下降,以竖条方块为例,其中的0xC0 ~ 0x03代表了方块从最顶上的11000000,下降到00000011。横条以及三角方块都占了两列,所以其结构体有2个数组分别储存左和右的形状。
接下来就是左右移动控制模块,每按一次移动按键,方块向左or右移动一次,类似于排序的交换两个数的位置,我们把它写进while(1)循环里,其实现代码如下:
if (P3_1 == 0) // 左移一个单位
{
Delay(10);
while (P3_1 == 0)
{
LEDDZ_COL_PORT = gled_arry[i];// 让led亮起来
h595_write_data(gled_row[i]);// 让led亮起来
i++;
D1(1);
h595_write_data(0x00);// 让led亮起来
if (i == 8)
i = 0;
}
if (cubeArr != 0 && cubeType == 0 && Ar[cubeArr - 1][j] != 1 && Ar[cubeArr - 1][j + 1] != 1) // 竖条方块移动逻辑
{
u8 temp;
temp = gled_row[cubeArr] - backG[cubeArr];
gled_row[cubeArr] = backG[cubeArr];
gled_row[cubeArr - 1] = temp + backG[cubeArr - 1];
cubeArr -= 1;
}
else if (cubeArr != 0 && cubeType == 1 && Ar[cubeArr - 1][j] != 1) // 横条方块移动逻辑
{
u8 temp;
temp = gled_row[cubeArr] - backG[cubeArr];
gled_row[cubeArr] = backG[cubeArr];
gled_row[cubeArr - 1] = temp + backG[cubeArr - 1];
temp = gled_row[cubeArr + 1] - backG[cubeArr + 1];
gled_row[cubeArr + 1] = backG[cubeArr + 1];
gled_row[cubeArr] = temp + backG[cubeArr];
cubeArr -= 1;
}
else if (cubeArr != 0 && cubeType == 2 && Ar[cubeArr - 1][j] != 1 && Ar[cubeArr - 1][j + 1] != 1) // 三角方块1移动逻辑
{
u8 temp;
temp = gled_row[cubeArr] - backG[cubeArr];
gled_row[cubeArr] = backG[cubeArr];
gled_row[cubeArr - 1] = temp + backG[cubeArr - 1];
temp = gled_row[cubeArr + 1] - backG[cubeArr + 1];
gled_row[cubeArr + 1] = backG[cubeArr + 1];
gled_row[cubeArr] = temp + backG[cubeArr];
cubeArr -= 1;
}
else if (cubeArr != 0 && cubeType == 3 && Ar[cubeArr][j] != 1 && Ar[cubeArr - 1][j + 1] != 1) // 三角方块2移动逻辑
{
u8 temp;
temp = gled_row[cubeArr] - backG[cubeArr];
gled_row[cubeArr] = backG[cubeArr];
gled_row[cubeArr - 1] = temp + backG[cubeArr - 1];
temp = gled_row[cubeArr + 1] - backG[cubeArr + 1];
gled_row[cubeArr + 1] = backG[cubeArr + 1];
gled_row[cubeArr] = temp + backG[cubeArr];
cubeArr -= 1;
}
}//左移模块
else if (P3_0 == 0) // 右移一个单位
{
Delay(10);
while (P3_0 == 0)
{
{
LEDDZ_COL_PORT = gled_arry[i];// 让led亮起来
h595_write_data(gled_row[i]);// 让led亮起来
i++;
D1(1);
h595_write_data(0x00);// 让led亮起来
if (i == 8)
i = 0;
}
}
if (cubeArr != 7 && cubeType == 0 && Ar[cubeArr + 1][j] != 1 && Ar[cubeArr + 1][j + 1] != 1) // 竖条方块移动逻辑
{
u8 temp;
temp = gled_row[cubeArr] - backG[cubeArr];
gled_row[cubeArr] = backG[cubeArr];
gled_row[cubeArr + 1] = temp + backG[cubeArr + 1];
cubeArr += 1;
}
else if (cubeArr != 6 && cubeType == 1 && Ar[cubeArr + 2][j] != 1) // 横条方块移动逻辑
{
u8 temp;
temp = gled_row[cubeArr + 1] - backG[cubeArr + 1];
gled_row[cubeArr + 1] = backG[cubeArr + 1];
gled_row[cubeArr + 2] = temp + backG[cubeArr + 2];
temp = gled_row[cubeArr] - backG[cubeArr];
gled_row[cubeArr] = backG[cubeArr];
gled_row[cubeArr + 1] = temp + backG[cubeArr + 1];
cubeArr += 1;
}
else if (cubeArr != 6 && cubeType == 2 && Ar[cubeArr + 2][j] != 1 && Ar[cubeArr + 1][j + 1] != 1) // 三角方块1移动逻辑
{
u8 temp;
temp = gled_row[cubeArr + 1] - backG[cubeArr + 1];
gled_row[cubeArr + 1] = backG[cubeArr + 1];
gled_row[cubeArr + 2] = temp + backG[cubeArr + 2];
temp = gled_row[cubeArr] - backG[cubeArr];
gled_row[cubeArr] = backG[cubeArr];
gled_row[cubeArr + 1] = temp + backG[cubeArr + 1];
cubeArr += 1;
}
else if (cubeArr != 6 && cubeType == 3 && Ar[cubeArr + 2][j] != 1 && Ar[cubeArr + 2][j + 1] != 1) // 三角方块2移动逻辑
{
u8 temp;
temp = gled_row[cubeArr + 1] - backG[cubeArr + 1];
gled_row[cubeArr + 1] = backG[cubeArr + 1];
gled_row[cubeArr + 2] = temp + backG[cubeArr + 2];
temp = gled_row[cubeArr] - backG[cubeArr];
gled_row[cubeArr] = backG[cubeArr];
gled_row[cubeArr + 1] = temp + backG[cubeArr + 1];
cubeArr += 1;
}
}//右移模块
移动的判定是左右都没碰墙,而且左右都没有别的方块阻挡,这里我使用了独立按键来实现左右移动。
方块下落的时间逻辑与代码:
unsigned short int gameTime = 0; // 游戏时间控制
unsigned short int timeSet = 250; // 默认每250个单位时间方块下落或者方块更新
while(1)
{
gameTime++; //每运行一次,时间+1
//-----------------------当你干了一堆事情
if (gameTime >= timeSet) // 方块保持的单位时间结束,开始下降!!!!!!!!!!!!
{
//````
gameTime=0;//归零
}
}//大循环结束,再来一次!!!!!
每到gameTime等于0时,就产生非常多的判断,比如如果方块没碰底,那就位置下降一格,如果碰底了,那就判断有没有填完一行,有没有超过限高导致游戏结束,如果填完一行,就消除那一行并产生复杂的移动等。
既然timeSet有了,那我们就可以控制代码运行速度了,原理是让timeSet大小增加或者减少来实现方块下落速度的加快和降低,这里我还是用独立键盘实现:
if (P3_3 == 0) // 按K4按键就加快方块下落速度
{
Delay(10);
if (timeSet > 50)// 每timeSet个单位时间方块下落或者方块更新,通过减少timeSet的方式进行加速
timeSet -= 50;
P2_5 = 1;
Delay(30);
P2_5 = 0;
while (P3_3 == 0)
{
LEDDZ_COL_PORT = gled_arry[i];// 让led亮起来
h595_write_data(gled_row[i]);// 让led亮起来
i++;
D1(1);
h595_write_data(0x00);// 让led亮起来
if (i == 8)
i = 0;
}
} //加速模块
else if (P3_2 == 0) // 按K3按键就放慢方块下落速度
{
Delay(10);
if (timeSet < 500)// 每timeSet个单位时间方块下落或者方块更新,通过增加timeSet的方式进行减速
timeSet += 50;
P2_5 = 1;
Delay(30);
P2_5 = 0;
while (P3_2 == 0)
{
LEDDZ_COL_PORT = gled_arry[i];// 让led亮起来
h595_write_data(gled_row[i]);// 让led亮起来
i++;
D1(1);
h595_write_data(0x00);// 让led亮起来
if (i == 8)
i = 0;
}
} //减速模块
速度上下限是50~500个单位循环时间,越小越快。
最后就是如何更新屏幕信息了,当单位时间一过,方块落地或者没落地在半路以及方块消除时,需要更新新的屏幕信息,我使用了两个switch循环来确定方块,一个在开头来生成新方块,一个在单位时间过去后处理旧的方块,并使用rand()函数来随机产生新的方块,为了让游戏可玩度更高,我采用一个lastType来确定上一局的方块,以免与下一局游戏方块重复,以增加可玩性,一定程度上降低难度。
while(1)
{
if (gameTime == 0 && j == 0) // 如果单位时间到了,并且方块落到地板了,就更新新的方块
{
cubeType = rand() % 4; // 更新方块类型
if (lastType == cubeType) // 为了游戏的合理和可玩性好,防止出现重复方块
{
do
{
cubeType = rand() % 4;
} while (lastType == cubeType); // 一直循环到出现新方块为止
}
lastType = cubeType; // 记录下新方块
switch (cubeType) // 把方块的图层与背景图层都传递给gled_row
{
case 0:
gled_row[3] = cuL.ctrL[j] + backG[3];//竖条方块
break;
case 1:
gled_row[3] = cu_[0].ctrL[j] + backG[3];//横条方块
gled_row[3 + 1] = cu_[0].ctrR[j] + backG[3 + 1];
break;
case 2:
gled_row[3] = cu_[1].ctrL[j] + backG[3];//三角方块1
gled_row[3 + 1] = cu_[1].ctrR[j] + backG[3 + 1];
break;
case 3:
gled_row[3] = cu_[2].ctrL[j] + backG[3];//三角方块2
gled_row[3 + 1] = cu_[2].ctrR[j] + backG[3 + 1];
break;
}
cubeArr = 3; // 重置方块开始下落位置
} // 更新方块判断模块
gameTime++; // 这行在循环哪里都可以出现,目的就是时间+1,并累计已经过去的游戏时间
if (gameTime >= timeSet) // 方块保持的单位时间结束,开始下降!!!!!!!!!!!!
{
//````
gameTime=0;//归零
switch (cubeType)
{
case 0:
gled_row[cubeArr] = cuL.ctrL[j] + backG[cubeArr];
break;
case 1:
gled_row[cubeArr] = cu_[0].ctrL[j] + backG[cubeArr];
gled_row[cubeArr + 1] = cu_[0].ctrR[j] + backG[cubeArr + 1];
break;
case 2:
gled_row[cubeArr] = cu_[1].ctrL[j] + backG[cubeArr];
gled_row[cubeArr + 1] = cu_[1].ctrR[j] + backG[cubeArr + 1];
break;
case 3:
gled_row[cubeArr] = cu_[2].ctrL[j] + backG[cubeArr];
gled_row[cubeArr + 1] = cu_[2].ctrR[j] + backG[cubeArr + 1];
break;
}
}
}//大循环结束,再来一次!!!!!
}
switch将二维逻辑地址转化到一维数组当中,并把gled_row的方块的最后坐标储存到backG中,这样,就可以实现二维转一维,然后输出到屏幕上了,不同方块的坐标如下:
Arr和j都是逻辑地址,需要将其转化为16进制才能输出到屏幕
游戏结束判断:检测是否有方块在最顶端,以此来判定游戏是否结束,游戏结束时屏幕闪烁3次,然后在数码管显示玩家分数。
for (num = 0; num < 8; num++)//检测一下,最上面的行有没有方块
if (Ar[num][0] == 1) // 检测到方块,芜湖,完力,游戏结束了
{
num = 0;
while (1)
{
if (num < 3)//先亮他俩下
{
num++;
while (1)
{
LEDDZ_COL_PORT = gled_arry[i];// 让led亮起来
h595_write_data(gled_row[i]);// 让led亮起来
i++;
j++;
D1(1);
h595_write_data(0x00);// 让led亮起来
if (i == 8)
i = 0;
if (j == 250)
{
j = 0;
P2_5 = ~P2_5;
break;
}
}
while (1)
{
LEDDZ_COL_PORT = gled_arry[i];// 让led亮起来
h595_write_data(0x00);// 让led亮起来
i++;
j++;
D1(1);
h595_write_data(0x00);// 让led亮起来
if (i == 8)
i = 0;
if (j == 250)
{
j = 0;
P2_5 = ~P2_5;
break;
}
}
}
else if (num >= 3 && num <= 10) //再把屏幕清空
{
P2_5 = 1;
LEDDZ_COL_PORT = gled_arry[num - 3];
h595_write_data(0x00);
num++;
}
else if (num >= 10)//最后显示你的分数
{
nix(5, score / 100);
Delay(5);
nix(6, score / 10);
Delay(5);
nix(7, score % 10);
Delay(5);
}
}
}
OK,以上就是大部分内容了,如果想到什么我以后再加,本文章可能还会有更新(也可能没有,哈哈哈哈),以下是完整代码,使用方法:复制到你的uVision项目的main.c文件中,然后生成.hex文件,再烧录到51单片机中就可以了,嘻嘻。
#include <REGX52.H>
typedef unsigned char u8; //显示led时用到的工具
sbit SRCLK = P3 ^ 6;
sbit rCLK = P3 ^ 5;
sbit SER = P3 ^ 4;
#define LEDDZ_COL_PORT P0
void Delay(unsigned int time) // lms延时
{
unsigned char data i, j;
while (time--)
{
i = 2;
j = 239;
do
{
while (--j)
;
} while (--i);
}
}
u8 num1[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};
void nix(unsigned char num, unsigned char nu1) // 得分计算版函数,num是位置,nu是数字
{
switch (num)
{
case 1:
P2_4 = 1;
P2_3 = 1;
P2_2 = 1;
break;
case 2:
P2_4 = 1;
P2_3 = 1;
P2_2 = 0;
break;
case 3:
P2_4 = 1;
P2_3 = 0;
P2_2 = 1;
break;
case 4:
P2_4 = 1;
P2_3 = 0;
P2_2 = 0;
break;
case 5:
P2_4 = 0;
P2_3 = 1;
P2_2 = 1;
break;
case 6:
P2_4 = 0;
P2_3 = 1;
P2_2 = 0;
break;
case 7:
P2_4 = 0;
P2_3 = 0;
P2_2 = 1;
break;
case 8:
P2_4 = 0;
P2_3 = 0;
P2_2 = 0;
break;
}
P0 = num1[nu1];
}
void D1(unsigned int time) // 快速延时(不到一毫秒)
{
unsigned char data i, j;
while (time--)
{
i = 1;
j = 1;
do
{
while (--j)
;
} while (--i);
}
}
void h595_write_data(u8 dat) // 8*8点阵灯
{
u8 i = 0;
for (i = 0; i < 8; i++)
{
SER = dat >> 7;
dat <<= 1;
SRCLK = 0;
D1(1);
SRCLK = 1;
D1(1);
}
rCLK = 0;
D1(1);
rCLK = 1;
}
typedef struct cubes
{ // 竖条方块的结构体
u8 cArr;
u8 ctrL[8];
} cube1; // 小的
typedef struct cube
{ // 三角方块和横条方块的结构体
u8 cArr;
u8 ctrL[8];
u8 ctrR[8];
} cube2; // 大的
unsigned int Ar[8][8] ={0}; //这个是逻辑二维数组,统计具体哪个灯亮哪个灯没亮的
void main()
{
u8 score = 0;
u8 gled_arry[8] = {0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0xFE}; // 显示LCD的每行内容,不需要修改
u8 backG[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // 背景板所对应的16进制数具体物理地址,当方块落地时更新,一个很重要的东西
u8 num;
u8 gled_row[8] = {0x66, 0x91, 0x89, 0x66, 0x00, 0x41, 0xFF, 0x01}; // 在背景板之外活动的方块的图层是实时方块的物理地址
u8 cubeArr = 3; // 方块开始移动的位置 0 1 2 3 4 5 6 7 默认是3
u8 i, j = 0; // i是最外层led灯快速更新的循坏,j是方块更新的对应的具体数组的位置
unsigned short int gameTime = 0; // 游戏时间控制
unsigned short int timeSet = 250; // 默认每250个单位时间方块下落或者方块更新
//以下两个cu代表分别代表不同方块每下降一个位置所对应的具体16进制地址的数组
cube1 cuL = {3, {0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03}}; // 竖条方块
cube2 cu_[3] = {{3, {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}, {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}},
{3, {0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03}, {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02}},
{3, {0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}, {0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03}}};
// 横条、三角方块1、2
u8 cubeType = 1; // 方块类型,0代表竖条,1代表横条,2代表三角方块1,3代表三角方块2
u8 lastType = -1; // 上一次的方块类型是什么(记下来),开局是-1,代表上一局没有方块
P1 = 0xFF;
P1_7 = 0;
if (P1_3 == 1) // 矩阵键盘s1
{
Delay(30);
while (P1_3 == 1)
{ // 下面这一大段都是为了显示S1这两个东西,意思是按s1开始
LEDDZ_COL_PORT = gled_arry[i]; // 让led亮起来
h595_write_data(gled_row[i]); // 让led亮起来
i++;
D1(1);
h595_write_data(0x00); // 让led亮起来
if (i == 8)
i = 0;
P2_5 = 0;
}
while (P1_3 == 0)
;
} // 开始之前的暂停
for (i = 0; i < 8; i++) // 清空gled_row缓存,游戏正式开始!
gled_row[i] = 0;
i = 0; // 如果不给i初始化,屏幕会闪一下
P2_5 = 0;//亮灯!开始游戏
while (1) // 大循环!!!!!!!!!!!!游戏开始!!!!!!!!!!!!!
{
if (gameTime == 0 && j == 0) // 如果单位时间到了,并且方块落到地板了,就更新新的方块
{
cubeType = rand() % 4; // 更新方块类型
if (lastType == cubeType) // 为了游戏的合理和可玩性好,防止出现重复方块
{
do
{
cubeType = rand() % 4;
} while (lastType == cubeType); // 一直循环到出现新方块为止
}
lastType = cubeType; // 记录下新方块
switch (cubeType) // 把方块的图层与背景图层都传递给gled_row
{
case 0:
gled_row[3] = cuL.ctrL[j] + backG[3];//竖条方块
break;
case 1:
gled_row[3] = cu_[0].ctrL[j] + backG[3];//横条方块
gled_row[3 + 1] = cu_[0].ctrR[j] + backG[3 + 1];
break;
case 2:
gled_row[3] = cu_[1].ctrL[j] + backG[3];//三角方块1
gled_row[3 + 1] = cu_[1].ctrR[j] + backG[3 + 1];
break;
case 3:
gled_row[3] = cu_[2].ctrL[j] + backG[3];//三角方块2
gled_row[3 + 1] = cu_[2].ctrR[j] + backG[3 + 1];
break;
}
cubeArr = 3; // 重置方块开始下落位置
} // 更新方块判断模块
gameTime++; // 这行在循环哪里都可以出现,目的就是时间+1,并累计已经过去的游戏时间
if (P3_3 == 0) // 按K4按键就加快方块下落速度
{
Delay(10);
if (timeSet > 50)// 每timeSet个单位时间方块下落或者方块更新,通过减少timeSet的方式进行加速
timeSet -= 50;
P2_5 = 1;
Delay(30);
P2_5 = 0;
while (P3_3 == 0)
{
LEDDZ_COL_PORT = gled_arry[i];// 让led亮起来
h595_write_data(gled_row[i]);// 让led亮起来
i++;
D1(1);
h595_write_data(0x00);// 让led亮起来
if (i == 8)
i = 0;
}
} //加速模块
else if (P3_2 == 0) // 按K3按键就放慢方块下落速度
{
Delay(10);
if (timeSet < 500)// 每timeSet个单位时间方块下落或者方块更新,通过增加timeSet的方式进行减速
timeSet += 50;
P2_5 = 1;
Delay(30);
P2_5 = 0;
while (P3_2 == 0)
{
LEDDZ_COL_PORT = gled_arry[i];// 让led亮起来
h595_write_data(gled_row[i]);// 让led亮起来
i++;
D1(1);
h595_write_data(0x00);// 让led亮起来
if (i == 8)
i = 0;
}
} //减速模块
P1 = 0xFF;
P1_7 = 0;
if (P1_3 == 0)//矩阵键盘s1 暂停按键
{
Delay(30);
while (P1_3 == 0)//矩阵键盘s1
;
while (P1_3 == 1)//矩阵键盘s1
{
while (P1_3 == 1)
{
LEDDZ_COL_PORT = gled_arry[i];// 让led亮起来
h595_write_data(gled_row[i]);// 让led亮起来
i++;
num++;
D1(1);
h595_write_data(0x00);// 让led亮起来
if (i == 8)
i = 0;
if (num == 250)
{
num = 0;
break;
}
}
while (P1_3 == 1)
{
LEDDZ_COL_PORT = gled_arry[i];// 让led亮起来
h595_write_data(0x00);// 让led亮起来
i++;
num++;
D1(1);
h595_write_data(0x00);// 让led亮起来
if (i == 8)
i = 0;
if (num == 250)
{
num = 0;
break;
}
}
}
if (j != 0)
j -= 1;
while (P1_3 == 0)
;
} //暂停模块
if (P3_1 == 0) // 左移一个单位
{
Delay(10);
while (P3_1 == 0)
{
LEDDZ_COL_PORT = gled_arry[i];// 让led亮起来
h595_write_data(gled_row[i]);// 让led亮起来
i++;
D1(1);
h595_write_data(0x00);// 让led亮起来
if (i == 8)
i = 0;
}
if (cubeArr != 0 && cubeType == 0 && Ar[cubeArr - 1][j] != 1 && Ar[cubeArr - 1][j + 1] != 1) // 竖条方块移动逻辑
{
u8 temp;
temp = gled_row[cubeArr] - backG[cubeArr];
gled_row[cubeArr] = backG[cubeArr];
gled_row[cubeArr - 1] = temp + backG[cubeArr - 1];
cubeArr -= 1;
}
else if (cubeArr != 0 && cubeType == 1 && Ar[cubeArr - 1][j] != 1) // 横条方块移动逻辑
{
u8 temp;
temp = gled_row[cubeArr] - backG[cubeArr];
gled_row[cubeArr] = backG[cubeArr];
gled_row[cubeArr - 1] = temp + backG[cubeArr - 1];
temp = gled_row[cubeArr + 1] - backG[cubeArr + 1];
gled_row[cubeArr + 1] = backG[cubeArr + 1];
gled_row[cubeArr] = temp + backG[cubeArr];
cubeArr -= 1;
}
else if (cubeArr != 0 && cubeType == 2 && Ar[cubeArr - 1][j] != 1 && Ar[cubeArr - 1][j + 1] != 1) // 三角方块1移动逻辑
{
u8 temp;
temp = gled_row[cubeArr] - backG[cubeArr];
gled_row[cubeArr] = backG[cubeArr];
gled_row[cubeArr - 1] = temp + backG[cubeArr - 1];
temp = gled_row[cubeArr + 1] - backG[cubeArr + 1];
gled_row[cubeArr + 1] = backG[cubeArr + 1];
gled_row[cubeArr] = temp + backG[cubeArr];
cubeArr -= 1;
}
else if (cubeArr != 0 && cubeType == 3 && Ar[cubeArr][j] != 1 && Ar[cubeArr - 1][j + 1] != 1) // 三角方块2移动逻辑
{
u8 temp;
temp = gled_row[cubeArr] - backG[cubeArr];
gled_row[cubeArr] = backG[cubeArr];
gled_row[cubeArr - 1] = temp + backG[cubeArr - 1];
temp = gled_row[cubeArr + 1] - backG[cubeArr + 1];
gled_row[cubeArr + 1] = backG[cubeArr + 1];
gled_row[cubeArr] = temp + backG[cubeArr];
cubeArr -= 1;
}
}//左移模块
else if (P3_0 == 0) // 右移一个单位
{
Delay(10);
while (P3_0 == 0)
{
{
LEDDZ_COL_PORT = gled_arry[i];// 让led亮起来
h595_write_data(gled_row[i]);// 让led亮起来
i++;
D1(1);
h595_write_data(0x00);// 让led亮起来
if (i == 8)
i = 0;
}
}
if (cubeArr != 7 && cubeType == 0 && Ar[cubeArr + 1][j] != 1 && Ar[cubeArr + 1][j + 1] != 1) // 竖条方块移动逻辑
{
u8 temp;
temp = gled_row[cubeArr] - backG[cubeArr];
gled_row[cubeArr] = backG[cubeArr];
gled_row[cubeArr + 1] = temp + backG[cubeArr + 1];
cubeArr += 1;
}
else if (cubeArr != 6 && cubeType == 1 && Ar[cubeArr + 2][j] != 1) // 横条方块移动逻辑
{
u8 temp;
temp = gled_row[cubeArr + 1] - backG[cubeArr + 1];
gled_row[cubeArr + 1] = backG[cubeArr + 1];
gled_row[cubeArr + 2] = temp + backG[cubeArr + 2];
temp = gled_row[cubeArr] - backG[cubeArr];
gled_row[cubeArr] = backG[cubeArr];
gled_row[cubeArr + 1] = temp + backG[cubeArr + 1];
cubeArr += 1;
}
else if (cubeArr != 6 && cubeType == 2 && Ar[cubeArr + 2][j] != 1 && Ar[cubeArr + 1][j + 1] != 1) // 三角方块1移动逻辑
{
u8 temp;
temp = gled_row[cubeArr + 1] - backG[cubeArr + 1];
gled_row[cubeArr + 1] = backG[cubeArr + 1];
gled_row[cubeArr + 2] = temp + backG[cubeArr + 2];
temp = gled_row[cubeArr] - backG[cubeArr];
gled_row[cubeArr] = backG[cubeArr];
gled_row[cubeArr + 1] = temp + backG[cubeArr + 1];
cubeArr += 1;
}
else if (cubeArr != 6 && cubeType == 3 && Ar[cubeArr + 2][j] != 1 && Ar[cubeArr + 2][j + 1] != 1) // 三角方块2移动逻辑
{
u8 temp;
temp = gled_row[cubeArr + 1] - backG[cubeArr + 1];
gled_row[cubeArr + 1] = backG[cubeArr + 1];
gled_row[cubeArr + 2] = temp + backG[cubeArr + 2];
temp = gled_row[cubeArr] - backG[cubeArr];
gled_row[cubeArr] = backG[cubeArr];
gled_row[cubeArr + 1] = temp + backG[cubeArr + 1];
cubeArr += 1;
}
}//右移模块
//cubeArr代表方块的定位点位置的具体纵坐标,j是方块更新的对应的具体数组的位置
if (gameTime >= timeSet) // 方块保持的单位时间结束,开始下降!!!!!!!!!!!!
{
if (cubeType == 0 && j <= 6) // 竖条方块下降逻辑,最多只能下降6个单位
{
if (Ar[cubeArr][j + 2] == 0 && j < 6)
{
j++;
}
else if (Ar[cubeArr][j + 2] == 1 && j < 6)
{
for (i = 0; i < 8; i++)
backG[i] = gled_row[i];
Ar[cubeArr][j + 1] = 1;
Ar[cubeArr][j] = 1;
j = 0;
}
else if (j == 6)
{
for (i = 0; i < 8; i++)
backG[i] = gled_row[i];
Ar[cubeArr][j + 1] = 1;
Ar[cubeArr][j] = 1;
j = 0;
}
}//竖条下降模块
else if (j <= 7 && cubeType == 1) //横条方块下降逻辑
{
if (Ar[cubeArr][j + 1] == 0 && j < 7 && Ar[cubeArr + 1][j + 1] == 0)
{
j++;
}
else if ((Ar[cubeArr][j + 1] == 1 || Ar[cubeArr + 1][j + 1] == 1) && j < 7)
{
for (i = 0; i < 8; i++)
backG[i] = gled_row[i];
Ar[cubeArr][j] = 1;
Ar[cubeArr + 1][j] = 1;
j = 0;
}
else if (j == 7)
{
for (i = 0; i < 8; i++)
backG[i] = gled_row[i];
Ar[cubeArr][j] = 1;
Ar[cubeArr + 1][j] = 1;
j = 0;
}
}//横条下降模块
else if (j <= 6 && cubeType == 2) //三角方块1下降逻辑
{
if (Ar[cubeArr][j + 2] == 0 && j < 6 && Ar[cubeArr + 1][j + 1] == 0)
{
j++;
}
else if ((Ar[cubeArr][j + 2] == 1 || Ar[cubeArr + 1][j + 1] == 1) && j < 6)
{
for (i = 0; i < 8; i++)
backG[i] = gled_row[i];
Ar[cubeArr][j] = 1;
Ar[cubeArr][j + 1] = 1;
Ar[cubeArr + 1][j] = 1;
j = 0;
}
else if (j == 6)
{
for (i = 0; i < 8; i++)
backG[i] = gled_row[i];
Ar[cubeArr][j] = 1;
Ar[cubeArr][j + 1] = 1;
Ar[cubeArr + 1][j] = 1;
j = 0;
}
}//三角方块2下降模块
else if (j <= 6 && cubeType == 3) // 三角方块2下降逻辑
{
if (Ar[cubeArr][j + 2] == 0 && j < 6 && Ar[cubeArr + 1][j + 2] == 0)
{
j++;
}
else if ((Ar[cubeArr][j + 2] == 1 || Ar[cubeArr + 1][j + 2] == 1) && j < 6)
{
for (i = 0; i < 8; i++)
backG[i] = gled_row[i];
Ar[cubeArr + 1][j] = 1;
Ar[cubeArr][j + 1] = 1;
Ar[cubeArr + 1][j + 1] = 1;
j = 0;
}
else if (j == 6)
{
for (i = 0; i < 8; i++)
backG[i] = gled_row[i];
Ar[cubeArr + 1][j] = 1;
Ar[cubeArr][j + 1] = 1;
Ar[cubeArr + 1][j + 1] = 1;
j = 0;
}
}//三角方块2下降模块
gameTime = 0; //下降完成,重新计时
if (j != 0)//如果还没下降到底,或者方块没有落地,就记录下此时的新背景和方块更新位置
{
switch (cubeType)
{
case 0:
gled_row[cubeArr] = cuL.ctrL[j] + backG[cubeArr];
break;
case 1:
gled_row[cubeArr] = cu_[0].ctrL[j] + backG[cubeArr];
gled_row[cubeArr + 1] = cu_[0].ctrR[j] + backG[cubeArr + 1];
break;
case 2:
gled_row[cubeArr] = cu_[1].ctrL[j] + backG[cubeArr];
gled_row[cubeArr + 1] = cu_[1].ctrR[j] + backG[cubeArr + 1];
break;
case 3:
gled_row[cubeArr] = cu_[2].ctrL[j] + backG[cubeArr];
gled_row[cubeArr + 1] = cu_[2].ctrR[j] + backG[cubeArr + 1];
break;
}
}//方块更新模块
else if (j == 0)//方块下降到底&方块碰地
{
u8 cubeLine[] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};//每行灯亮的对应16进制代码
u8 n, n1;
for (i = 0; i < 8; i++) //行
{
num = 0;
for (j = 0; j < 8; j++) //列
{
if (Ar[j][i] != 0)//统计逻辑地址
{
num += 1;
}
}
if (num == 8) //在逻辑地址上显示,有一行满8个单位了
{
for (n = i; n > 0; n--) //把逻辑地址删掉一行
{
for (n1 = 0; n1 < 8; n1++)
{
Ar[n1][n] = Ar[n1][n - 1];
}
}
for (n = 0; n < 8; n++)//对照逻辑地址,把物理地址更新
{
num = 0;
backG[n] = 0;
for (n1 = 0; n1 < 8; n1++)
{
if (Ar[n][n1] == 1)
{
num += cubeLine[n1];
}
}
backG[n] = num;
gled_row[n] = backG[n];
}
score++;//分数+=1
P2_5 = 1;
Delay(30);
P2_5 = 0;//哒哒捏,好听
}
}
i = 0, j = 0;//不把ij初始化等会8*8屏幕又特么乱闪了
}
for (num = 0; num < 8; num++)//检测一下,最上面的行有没有方块
if (Ar[num][0] == 1) // 检测到方块,芜湖,完力,游戏结束了
{
num = 0;
while (1)
{
if (num < 3)//先亮他俩下
{
num++;
while (1)
{
LEDDZ_COL_PORT = gled_arry[i];// 让led亮起来
h595_write_data(gled_row[i]);// 让led亮起来
i++;
j++;
D1(1);
h595_write_data(0x00);// 让led亮起来
if (i == 8)
i = 0;
if (j == 250)
{
j = 0;
P2_5 = ~P2_5;
break;
}
}
while (1)
{
LEDDZ_COL_PORT = gled_arry[i];// 让led亮起来
h595_write_data(0x00);// 让led亮起来
i++;
j++;
D1(1);
h595_write_data(0x00);// 让led亮起来
if (i == 8)
i = 0;
if (j == 250)
{
j = 0;
P2_5 = ~P2_5;
break;
}
}
}
else if (num >= 3 && num <= 10) //再把屏幕清空
{
P2_5 = 1;
LEDDZ_COL_PORT = gled_arry[num - 3];
h595_write_data(0x00);
num++;
}
else if (num >= 10)//最后显示你的分数
{
nix(5, score / 100);
Delay(5);
nix(6, score / 10);
Delay(5);
nix(7, score % 10);
Delay(5);
}
}
}
}
LEDDZ_COL_PORT = gled_arry[i];//每执行完一轮,都会刷新会屏幕,康康此时的游戏的变化
h595_write_data(gled_row[i]);
i++;
D1(1);
h595_write_data(0x00);
if (i == 8)
i = 0;
}
}
更多推荐
所有评论(0)