本项目所使用到的开发板子是普中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;
	}
}

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐