MM32SPIN27PS实现俄罗斯方块游戏

大概思路

因为智能车实验室选拔需要(虽然很遗憾,今年学校实验室又不收大二的学生了),用MM32做一个俄罗斯方块的小游戏。整体方案的硬件部分由一个MM32SPIN27PS黑色芯片、五向按键开关以及1.8寸TFTLCD屏幕组成。分析下来主要实现要点有三个,游戏逻辑的实现,显示函数的实现,定时器的配置实现。

因为实验室提供了基础的调用教学和工程模板,没有配置环境等一系列问题,只需要调用工程模板里面的库函数实现上述三大功能。


画圆函数和画线函数

void Gui_DrawLine(u16 x0, u16 y0,u16 x1, u16 y1,u16 Color)  

{

int dx,       // difference in x's

  dy,       // difference in y's

  dx2,      // dx,dy * 2

  dy2, 

  x_inc,     // amount in pixel space to move during drawing

  y_inc,     // amount in pixel space to move during drawing

  error,     // the discriminant i.e. error i.e. decision variable

  index;     // used for looping    

 

 

 Lcd_SetXY(x0,y0);

 dx = x1-x0;//计算x距离

 dy = y1-y0;//计算y距离

 

 if (dx>=0)

 {

​      x_inc = 1;

 }

 else

 {

​      x_inc = -1;

​      dx  = -dx; 

 } 

 

 if (dy>=0)

 {

​      y_inc = 1;

 } 

 else

 {

​      y_inc = -1;

​      dy  = -dy; 

 } 

 

 dx2 = dx << 1;

 dy2 = dy << 1;

 

 if (dx > dy)//x距离大于y距离,那么每个x轴上只有一个点,每个y轴上有若干个点

 {//且线的点数等于x距离,以x轴递增画点// initialize error term

​      error = dy2 - dx;// draw the linefor (index=0; index <= dx; index++)//要画的点数不会超过x距离{//画点Gui_DrawPoint(x0,y0,Color);

​           

​           // test if error has overflowedif (error >= 0) //是否需要增加y坐标值{

​                error-=dx2;// move to next line

​                y0+=y_inc;//增加y坐标值} // end if error overflowed// adjust the error term

​           error+=dy2;// move to the next pixel

​           x0+=x_inc;//x坐标值每次画点后都递增1} // end for

 } // end if |slope| <= 1

 else//y轴大于x轴,则每个y轴上只有一个点,x轴若干个点

 {//以y轴为递增画点// initialize error term

​      error = dx2 - dy;// draw the linefor (index=0; index <= dy; index++){// set the pixelGui_DrawPoint(x0,y0,Color);// test if error overflowedif (error >= 0){

​                error-=dy2;// move to next line

​                x0+=x_inc;} 
​           error+=dx2;
​           y0+=y_inc;} 
 }
}
void Gui_Circle(u16 X,u16 Y,u16 R,u16 fc) 

{ 

  unsigned short a,b; 

  int c; 

  a=0; 

  b=R; 

  c=3-2*R; 

  while (a<b) 

  {Gui_DrawPoint(X+a,Y+b,fc);   //    7 Gui_DrawPoint(X-a,Y+b,fc);   //    6 Gui_DrawPoint(X+a,Y-b,fc);   //    2 Gui_DrawPoint(X-a,Y-b,fc);   //    3 Gui_DrawPoint(X+b,Y+a,fc);   //    8 Gui_DrawPoint(X-b,Y+a,fc);   //    5 Gui_DrawPoint(X+b,Y-a,fc);   //    1 Gui_DrawPoint(X-b,Y-a,fc);   //    4 if(c<0) c=c+4*a+6;else{ 

​      c=c+4*(a-b)+10; 

​      b-=1;} 

​    a+=1; 

  } 

  if (a==b) 

  {Gui_DrawPoint(X+a,Y+b,fc);Gui_DrawPoint(X+a,Y+b,fc);Gui_DrawPoint(X+a,Y-b,fc);Gui_DrawPoint(X-a,Y-b,fc);Gui_DrawPoint(X+b,Y+a,fc);Gui_DrawPoint(X-b,Y+a,fc);Gui_DrawPoint(X+b,Y-a,fc);Gui_DrawPoint(X-b,Y-a,fc); 

  } 

}

方块绘制函数

TFT显示屏为128*160,选取10*10作为一个方块,游戏界面可以设置为长15格,宽12格,右侧留出一格提前显示下一个俄罗斯方块。

void Draw_realbox(uint16 x,uint16 y)
{
	uint8 i,n;
	for(i=1;i<=8;i++)//绘制小方块内填充色8*8
	{
		for(n=1;n<=8;n++)
		{
			lcd_drawpoint((x+i),(y+n),RED);	
		}	
	}
	for(i=0;i<=9;i++)//绘制小方块轮廓10*10
	{
		lcd_drawpoint((x+i),y,BLACK	);
		lcd_drawpoint((x+i),(y+9),BLACK	);		
		lcd_drawpoint(x,(y+i),BLACK	);		
		lcd_drawpoint((x+9),(y+i),BLACK	);				
	}	
}

void Draw_realbox1(uint16 x,uint16 y)
{
	uint8 i,n;
	for(i=1;i<=3;i++)
	{
		for(n=1;n<=3;n++)
		{
			lcd_drawpoint((x+i),(y+n),RED);	
		}	
	}
	for(i=0;i<=4;i++)
	{
		lcd_drawpoint((x+i),y,BLACK	);
		lcd_drawpoint((x+i),(y+4),BLACK	);		
		lcd_drawpoint(x,(y+i),BLACK	);		
		lcd_drawpoint((x+4),(y+i),BLACK	);				
	}	
}

删除方块实现方法是绘制成白色

void Del_realbox(uint16 x,uint16 y)
{
	uint8 i,n;
	for(i=0;i<=9;i++)
	{
		for(n=0;n<=9;n++)
		{
			lcd_drawpoint((x+i),(y+n),WHITE);	
		}	
	}
}


void Del_realbox1(uint16 x,uint16 y)
{
	uint8 i,n;
	for(i=0;i<=4;i++)
	{
		for(n=0;n<=4;n++)
		{
			lcd_drawpoint((x+i),(y+n),WHITE);	
		}	
	}
}

有了以上两个函数,我们只需要在规定的坐标处调用四次小方块绘制或删除函数。即可得到或消除一块完整的俄罗斯方块,利用random产生随机数,加上switch语句进行选择需要何种方块,总方块在switch中设计了19种。
图形绘制函数如下(部分):

void Del_tuxing(uint16 x,uint16 y,uint8 what)
{
	switch (what)
	{
		case 1:
		{
		Del_realbox(x,y);
		Del_realbox(x+10,y);
		Del_realbox(x,y+10);
		Del_realbox(x+10,y+10);
		}
		break;
		
		case 2:
		{
		Del_realbox(x,y);
		Del_realbox(x+10,y);
		Del_realbox(x+20,y);
		Del_realbox(x+30,y);
		}
		break;
		
		case 3:
		{
		Del_realbox(x,y);
		Del_realbox(x,y+10);
		Del_realbox(x,y+20);
		Del_realbox(x,y+30);
		}
		break;
		
		case 4:
		{
		Del_realbox(x+10,y);
		Del_realbox(x,y+10);
		Del_realbox(x+10,y+10);
		Del_realbox(x+20,y+10);
		}
		break;
		
		case 5:
		{
		Del_realbox(x+10,y+10);
		Del_realbox(x,y);
		Del_realbox(x,y+10);
		Del_realbox(x,y+20);
		}
		break;

		case 6:
		{
		Del_realbox(x,y+10);
		Del_realbox(x+10,y);
		Del_realbox(x+10,y+10);
		Del_realbox(x+10,y+20);
		}
		break;
		
		case 7:
		{
		Del_realbox(x+10,y+10);
		Del_realbox(x,y);
		Del_realbox(x+10,y);
		Del_realbox(x+20,y);
		}
		break;
	
		case 8:
		{
		Del_realbox(x,y);
		Del_realbox(x,y+10);
		Del_realbox(x,y+20);
		Del_realbox(x+10,y+20);
		}
		break;
		
		case 9:
		{
		Del_realbox(x,y);
		Del_realbox(x,y+10);
		Del_realbox(x+10,y);
		Del_realbox(x+20,y);
		}
		break;
		
		case 10:
		{
		Del_realbox(x,y);
		Del_realbox(x+10,y);
		Del_realbox(x+10,y+10);
		Del_realbox(x+10,y+20);
		}
		break;
		
		case 11:
		{
		Del_realbox(x,y+10);
		Del_realbox(x+10,y+10);
		Del_realbox(x+20,y+10);
		Del_realbox(x+20,y);
		}
		break;
		
		case 12:
		{
		Del_realbox(x+10,y);
		Del_realbox(x+10,y+10);
		Del_realbox(x+10,y+20);
		Del_realbox(x,y+20);
		}
		break;
		
		case 13:
		{
		Del_realbox(x,y);
		Del_realbox(x+10,y);
		Del_realbox(x+20,y);
		Del_realbox(x+20,y+10);
		}
		break;
		
		case 14:
		{
		Del_realbox(x,y);
		Del_realbox(x+10,y);
		Del_realbox(x,y+10);
		Del_realbox(x,y+20);
		}
		break;
		
		case 15:
		{
		Del_realbox(x,y);
		Del_realbox(x,y+10);
		Del_realbox(x+10,y+10);
		Del_realbox(x+20,y+10);
		}
		break;
		
		
		case 16:
		{
		Del_realbox(x+10,y);
		Del_realbox(x,y+10);
		Del_realbox(x+10,y+10);
		Del_realbox(x,y+20);
		}
		break;
		
		case 17:
		{
		Del_realbox(x,y);
		Del_realbox(x+10,y);
		Del_realbox(x+10,y+10);
		Del_realbox(x+20,y+10);
		}
		break;
		
		case 18:
		{
		Del_realbox(x,y);
		Del_realbox(x,y+10);
		Del_realbox(x+10,y+10);
		Del_realbox(x+10,y+20);
		}
		break;
		
		case 19:
		{
		Del_realbox(x,y+10);
		Del_realbox(x+10,y+10);
		Del_realbox(x+10,y);
		Del_realbox(x+20,y);
		}
		break;
	
	
	
	
	}

}


void Del_tuxing1(uint16 x,uint16 y,uint8 what)
{
	switch (what)
	{
		case 1:
		{
		Del_realbox1(x,y);
		Del_realbox1(x+5,y);
		Del_realbox1(x,y+5);
		Del_realbox1(x+5,y+5);
		}
		break;
		
		case 2:
		{
		Del_realbox1(x,y);
		Del_realbox1(x+5,y);
		Del_realbox1(x+10,y);
		Del_realbox1(x+15,y);
		}
		break;
		
		case 3:
		{
		Del_realbox1(x,y);
		Del_realbox1(x,y+5);
		Del_realbox1(x,y+10);
		Del_realbox1(x,y+15);
		}
		break;
		
		case 4:
		{
		Del_realbox1(x+5,y);
		Del_realbox1(x,y+5);
		Del_realbox1(x+5,y+5);
		Del_realbox1(x+10,y+5);
		}
		break;
		
		case 5:
		{
		Del_realbox1(x+5,y+5);
		Del_realbox1(x,y);
		Del_realbox1(x,y+5);
		Del_realbox1(x,y+10);
		}
		break;

		case 6:
		{
		Del_realbox1(x,y+5);
		Del_realbox1(x+5,y);
		Del_realbox1(x+5,y+5);
		Del_realbox1(x+5,y+10);
		}
		break;
		
		case 7:
		{
		Del_realbox1(x+5,y+5);
		Del_realbox1(x,y);
		Del_realbox1(x+5,y);
		Del_realbox1(x+10,y);
		}
		break;
	
		case 8:
		{
		Del_realbox1(x,y);
		Del_realbox1(x,y+5);
		Del_realbox1(x,y+10);
		Del_realbox1(x+5,y+10);
		}
		break;
		
		case 9:
		{
		Del_realbox1(x,y);
		Del_realbox1(x,y+5);
		Del_realbox1(x+5,y);
		Del_realbox1(x+10,y);
		}
		break;
		
		case 10:
		{
		Del_realbox1(x,y);
		Del_realbox1(x+5,y);
		Del_realbox1(x+5,y+5);
		Del_realbox1(x+5,y+10);
		}
		break;
		
		case 11:
		{
		Del_realbox1(x,y+5);
		Del_realbox1(x+5,y+5);
		Del_realbox1(x+10,y+5);
		Del_realbox1(x+10,y);
		}
		break;
		
		case 12:
		{
		Del_realbox1(x+5,y);
		Del_realbox1(x+5,y+5);
		Del_realbox1(x+5,y+10);
		Del_realbox1(x,y+10);
		}
		break;
		
		case 13:
		{
		Del_realbox1(x,y);
		Del_realbox1(x+5,y);
		Del_realbox1(x+10,y);
		Del_realbox1(x+10,y+5);
		}
		break;
		
		case 14:
		{
		Del_realbox1(x,y);
		Del_realbox1(x+5,y);
		Del_realbox1(x,y+5);
		Del_realbox1(x,y+10);
		}
		break;
		
		case 15:
		{
		Del_realbox1(x,y);
		Del_realbox1(x,y+5);
		Del_realbox1(x+5,y+5);
		Del_realbox1(x+10,y+5);
		}
		break;
		
		
		case 16:
		{
		Del_realbox1(x+5,y);
		Del_realbox1(x,y+5);
		Del_realbox1(x+5,y+5);
		Del_realbox1(x,y+10);
		}
		break;
		
		case 17:
		{
		Del_realbox1(x,y);
		Del_realbox1(x+5,y);
		Del_realbox1(x+5,y+5);
		Del_realbox1(x+10,y+5);
		}
		break;
		
		case 18:
		{
		Del_realbox1(x,y);
		Del_realbox1(x,y+5);
		Del_realbox1(x+5,y+5);
		Del_realbox1(x+5,y+10);
		}
		break;
		
		case 19:
		{
		Del_realbox1(x,y+5);
		Del_realbox1(x+5,y+5);
		Del_realbox1(x+5,y);
		Del_realbox1(x+10,y);
		}
		break;
	}

}

数组实现逻辑

使用了15*12的数组来记录方块的位置,用0,1对当前方块进行标记,逻辑上可以进行后续的碰撞,方块消除的判断。需要区分改变数组状态并不等于直接显示,改变数组状态之后还需要调用相关的显示函数才可以实现实现相关的显示功能。
绘制一个状态

void Draw_a_zhuangtai(uint16 x,uint16 y)
{
	  zhuangtai[y/10][x/10+1]=1;
}

删除一个状态

void Del_a_zhuangtai(uint16 x,uint16 y)
{
	  zhuangtai[y/10][x/10+1]=0;
}

碰撞判断

使用求和的方式找出碰撞的方块。在方块没发生碰撞之前,对该数组求和的值为把目前数组扫一遍的值。当方块发生碰撞,两个方块之间的交集会使得方块占的值变化(变小),与原值比较后可得出方块是否碰撞,碰撞则返回一个值。


int panduan(uint16 x,uint16 y,uint8 what,uint8 fangxiang)//碰撞检测
{
	uint16 sum1=0,sum2=0;
	uint8 i,n;
	uint8 sbuff[A][B];
	x=x/10+1;//得到换算后的真实坐标
	y=y/10;
	for(i=0;i<A;i++)
	{
		for(n=0;n<B;n++)
		{
			sbuff[i][n]=zhuangtai[i][n];
			sum1=sum1+zhuangtai[i][n];	
		}  
	}
	switch(fangxiang)
	{
		case 1:Left_zhuangtai_move((x-1)*10,10*y,what);break;
		case 2:Down_zhuangtai_move((x-1)*10,10*y,what);break;
		case 3:Right_zhuangtai_move((x-1)*10,10*y,what);break;
		case 4:change_zhuangtai();break;
		
	}
		for(i=0;i<A;i++)
	{
		
		for(n=0;n<B;n++)
		{
			sum2=sum2+zhuangtai[i][n];	
			zhuangtai[i][n]=sbuff[i][n];			
    }
	}	
		return !(sum1==sum2);
}

物理消除

由函数xiaochu()实现,每发生一次碰撞就检测一次是否满足消除条件

void xiaochu()
{
	uint8 n;
	for(n=12;n>0;n--)
	{
		if(n>=12)
		{
			n=12;
		}
		if(	(
					zhuangtai[n+1][1]&&zhuangtai[n+1][2]&&zhuangtai[n+1][3]&&zhuangtai[n+1][4]&&zhuangtai[n+1][5]&&zhuangtai[n+1][6]&&zhuangtai[n+1][7]&&zhuangtai[n+1][8]&&zhuangtai[n+1][9]&&zhuangtai[n+1][10]&&zhuangtai[n+1][11]
				) 
				 &&
				!(
					zhuangtai[n][1]&&zhuangtai[n][2]&&zhuangtai[n][3]&&zhuangtai[n][4]&&zhuangtai[n][5]&&zhuangtai[n][6]&&zhuangtai[n][7]&&zhuangtai[n][8]&&zhuangtai[n][9]&&zhuangtai[n][10]&&zhuangtai[n][11]
					)
		   )//描述:
		  
		{
			lie_move(10*n);//将被消除行于其上一行交换 
			n=n+2;
		}
		
		if((
				!zhuangtai[n][1]&&!zhuangtai[n][2]&&!zhuangtai[n][3]&&!zhuangtai[n][4]&&!zhuangtai[n][5]&&!zhuangtai[n][6]&&!zhuangtai[n][7]&&!zhuangtai[n][8]&&!zhuangtai[n][9]&&!zhuangtai[n][10]&&!zhuangtai[n][11]
				)//该条件用于判断换行是否完成(因为换行瞬间两行是清空的,即均为0)
		  )
		{
			for(n=0;n<12 ;n++){
				if(
						(
						zhuangtai[n][1]&&zhuangtai[n][2]&&zhuangtai[n][3]&&zhuangtai[n][4]&&zhuangtai[n][5]&&zhuangtai[n][6]&&zhuangtai[n][7]&&zhuangtai[n][8]&&zhuangtai[n][9]&&zhuangtai[n][10]&&zhuangtai[n][11]
						)
					){
						Del_lie(n*10);
						fengshu++;
						display_fengshu();
					if(fengshu%10==0)
					{
						speed=speed-10;
						leave++;
						display_leave();				
					}
				}	
			}
	break;		
		}
	}
}

其中,消除函数的“消除”功能是由调用换行函数lie_move()和删行函数Del_lie()实现的

void lie_move(uint16 y)//交换上下两行的缓存值
{
	uint8 i;
	y=y/10;
	for(i=1;i<15;i++)
	{
		if(zhuangtai[y][i]==1)
		{
			zhuangtai[y][i]=zhuangtai[y+1][i];
			zhuangtai[y+1][i]=1;
			
				Del_realbox((i-1)*10,y*10);
			Draw_realbox((i-1)*10,(y+1)*10);
		}
		else if(zhuangtai[y][i]==0)
		{
			zhuangtai[y][i]=zhuangtai[y+1][i];
			zhuangtai[y+1][i]=0;
			
				Del_realbox((i-1)*10,y*10);
			Del_realbox((i-1)*10,(y+1)*10);
		}
	}
}
void Del_lie(uint16 y)//删除一行
{
	uint8 i;
	y=y/10;
	for(i=1;i<15;i++)
	{
		zhuangtai[y][i]=0;
		Del_realbox((i-1)*10,y*10);
	}
}

形状控制函数

绘制出规定形状的俄罗斯方块的函数,一共有两个,change()函数用于绘制LCD上的,change_Zhuangtai()函数用于改变状态数组中的。

void change()
{
	switch(what)
	{
		case 1:break;
		
		case 2:Del(x,y,2);Draw(x,y,3);what=3;break;
		case 3:Del(x,y,3);Draw(x,y,2);what=2;break;
		
		case 4:Del(x,y,4);Draw(x,y,5);what=5;break;
		case 5:Del(x,y,5);Draw(x,y,7);what=7;break;
		case 6:Del(x,y,6);Draw(x,y,4);what=4;break;
		case 7:Del(x,y,7);Draw(x,y,6);what=6;break;
		
		case 8:Del(x,y,8);Draw(x,y,9);what=9;break;
		case 9:Del(x,y,9);Draw(x,y,10);what=10;break;
		case 10:Del(x,y,10);Draw(x,y,11);what=11;break;
		case 11:Del(x,y,11);Draw(x,y,8);what=8;break;
		
		case 12:Del(x,y,12);Draw(x,y,15);what=15;break;
		case 13:Del(x,y,13);Draw(x,y,12);what=12;break;
		case 14:Del(x,y,14);Draw(x,y,13);what=13;break;
		case 15:Del(x,y,15);Draw(x,y,14);what=14;break;
		
		case 16:Del(x,y,16);Draw(x,y,17);what=17;break;
		case 17:Del(x,y,17);Draw(x,y,16);what=16;break;
		
		case 18:Del(x,y,18);Draw(x,y,19);what=19;break;
		case 19:Del(x,y,19);Draw(x,y,18);what=18;break;
	
	}
}
void change_zhuangtai()
{
	switch(what)
	{
		case 1:break;
		case 2:Del_zhuangtai_tuxing(x,y,2);Draw_zhuangtai_tuxing(x,y,3);break;
		case 3:Del_zhuangtai_tuxing(x,y,3);Draw_zhuangtai_tuxing(x,y,2);break;
		case 4:Del_zhuangtai_tuxing(x,y,4);Draw_zhuangtai_tuxing(x,y,5);break;
		case 5:Del_zhuangtai_tuxing(x,y,5);Draw_zhuangtai_tuxing(x,y,7);break;
		case 6:Del_zhuangtai_tuxing(x,y,6);Draw_zhuangtai_tuxing(x,y,4);break;
		case 7:Del_zhuangtai_tuxing(x,y,7);Draw_zhuangtai_tuxing(x,y,6);break;
		case 8:Del_zhuangtai_tuxing(x,y,8);Draw_zhuangtai_tuxing(x,y,9);break;
		case 9:Del_zhuangtai_tuxing(x,y,9);Draw_zhuangtai_tuxing(x,y,10);break;
		case 10:Del_zhuangtai_tuxing(x,y,10);Draw_zhuangtai_tuxing(x,y,11);break;
		case 11:Del_zhuangtai_tuxing(x,y,11);Draw_zhuangtai_tuxing(x,y,8);break;
		case 12:Del_zhuangtai_tuxing(x,y,12);Draw_zhuangtai_tuxing(x,y,15);break;
		case 13:Del_zhuangtai_tuxing(x,y,13);Draw_zhuangtai_tuxing(x,y,12);break;
		case 14:Del_zhuangtai_tuxing(x,y,14);Draw_zhuangtai_tuxing(x,y,13);break;
		case 15:Del_zhuangtai_tuxing(x,y,15);Draw_zhuangtai_tuxing(x,y,14);break;
		case 16:Del_zhuangtai_tuxing(x,y,16);Draw_zhuangtai_tuxing(x,y,17);break;
		case 17:Del_zhuangtai_tuxing(x,y,17);Draw_zhuangtai_tuxing(x,y,16);break;
		case 18:Del_zhuangtai_tuxing(x,y,18);Draw_zhuangtai_tuxing(x,y,19);break;
		case 19:Del_zhuangtai_tuxing(x,y,19);Draw_zhuangtai_tuxing(x,y,18);break;
	
	}
}
	

随机数的生成

what=rand()%19+1;//what代表着不同俄罗斯方块

方向控制

方块可进行左、右、下的移动,因此我们需要在接到指令后再对应坐标画出完整的俄罗斯方块并将原坐标处的图形消除,只需调用之前定义的图形调用/删除函数即可。值得注意的是,在移动图像的同时,我们也需要对状态数组中的数据进行相应的移动,为此我们需要一些整合后的函数如Down(),Left(),Right(),Del().

void Down(uint16 x,uint16 y,uint8 what)//调用方向移动函数
{
	Down_zhuangtai_move(x,y,what);
	Down_tuxing_move(x,y,what);			
}```

```c
void Left(uint16 x,uint16 y,uint8 what)
{
	Left_zhuangtai_move(x,y,what);
	Left_tuxing_move(x,y,what);			
}
void Right(uint16 x,uint16 y,uint8 what)
{
	Right_zhuangtai_move(x,y,what);
	Right_tuxing_move(x,y,what);			
}

void Del(uint16 x,uint16 y,uint8 what)
{
	Del_tuxing(x,y,what);
	Del_zhuangtai_tuxing(x,y,what);			
}

配合KEY_Scan()就可以实现方向判断。

#define KEY_UP 1
#define KEY_DOWN 2
#define KEY_LEFT 3
#define KEY_RIGHT 4  
#define KEY_MID 5  
u8 Key_Scan(void)
{

	
	if(key_up&&(KB1||KB2||KB3||KB4||KB5))
	{

		systick_delay_ms(10);//去抖动// 
		key_up=0;
		if     (KB1)	
			return KEY_UP;
		else if(KB2)
			return KEY_DOWN;
		else if(KB3)
			return KEY_LEFT;
		else if(KB4)
			return KEY_RIGHT;
		else if(KB5)
			return KEY_MID;
	}
	else if(!KB1&&!KB2&&!KB3&&!KB4&&!KB5)
	{
		key_up=1;
	}
 	return 0;// 无按键按下	
}

		

开始函数

通过begin()函数和first()函数实现。方块的实际绘制是begin()函数完成的,此外该函数定义了方块的初始刷新位置并且对方块是否触顶进行判断以提示游戏结束。first()函数以负责清空上一次数组保存的状态和基本UI的绘制,由于我们设置的边界,UI部分在游戏过程中不会受到刷新影响,故只需要绘制一次。

void begin()//绘制方块,开始游戏
{
	Del_tuxing1(150,25,suijishu[4]);
	Del_tuxing1(150,50,suijishu[3]);
	Del_tuxing1(150,75,suijishu[2]);
	
	what=suijishu[4];//
	suijishu[4]=suijishu[3];
	suijishu[3]=suijishu[2];
	suijishu[2]=suijishu[1];
	suijishu[1]=suijishu[0];
	suijishu[0]=rand()%19+1;
	
	Draw_tuxing1(150,25,suijishu[4]);
	Draw_tuxing1(150,50,suijishu[3]);
	Draw_tuxing1(150,75,suijishu[2]);
	
	x=60,y=0;
	
	Draw(60,0,what);
	if(panduan(x,y,what,2))
	{
		game_over();
	}
}
void first()
{ 
	uint8 i,n;
	fengshu=0;
	leave=1;
	speed=80;
	lcd_clear(WHITE);//WHITE清除欢迎界面
   Gui_DrawLine(150 ,0,150,160,BLACK);//BLACK
	//清空状态
	for(i=0;i<A;i++)
	{
		for(n=1;n<B;n++)
		{
			zhuangtai[i][n]=0;
		}
	}
}

主程序

int main(void)
{
	lcd_init();
	Key_Init();
	NVIC_Init(NVIC_PriorityGroup_2);
	TIM3_Init(20,36000-1);
	lcd_clear(WHITE);
	welcome();
	systick_delay_ms(10);
	suijishu[0]=rand()%19+1;
	suijishu[1]=rand()%19+1;
	suijishu[2]=rand()%19+1;
	suijishu[3]=rand()%19+1;
	suijishu[4]=rand()%19+1;
		/*
		#define KEY_UP 1
		#define KEY_DOWN 2
		#define KEY_LEFT 3
		#define KEY_RIGHT 4  
		#define KEY_MID 5  */
	while(1)
	{
		switch(Key_Scan())
		{
			case 0:
				break;
			//功能区
			case 1:
				if(game==1){
					game=2;
					first();
					begin();
				}
				else if(game==2){
					game=4;
					stop();
				}
				else if(game==4){
					game=2;
					star();
					}
				else if(game==3){
					game=1;
				}
				break;
			
			case 2:
				if(game!=2)
					break;
				if(!panduan(x,y,what,2)){//方向判断
					Down(x,y,what);
					y=y+10;
					}else{	
						xiaochu();
						begin();
						} break;
			case 3:
				if(game!=2)
					break;
				if(!panduan(x,y,what,1)){
					Left(x,y,what);x=x-10;
					}break;
			case 4:
				if(game!=2)
					break;
				if(!panduan(x,y,what,3)){
					Right(x,y,what);x=x+10;
					}break;
			case 5:
				if(game!=2)
					break;
				if(!panduan(x,y,what,4))
					change();
					break;
	  	
		}
//方块下落		
		if(game==2)
		{
		if(i>speed)
		{
			i=0;
		
		if(panduan(x,y,what,2))//若判断碰撞且满足消除条件,返回值并调用消除函数
			{
				xiaochu();
				begin();							
		  }
			else
			{
				Down(x,y,what);		//否则匀速下移	
				y=y+10;		
			}
		}
			
}
	}
}		

定时器及中断服务函数

这个地方搞了很久,因为之前只接触过51的定时器,stm32的定时器只在教学视频中看见过,从来都没有进行过实操。这里选取TIM3定时器就可以完成。定时器真的曲折,光是里面定时器的调用函数和结构体就找了半天。

定时器3的初始化函数

void TIM3_Init(u16 per,u16 psc)
{
	exNVIC_Init_TypeDef NVIC_InitStructure;
	TIM_TimeBaseInitTypeDef TimeBaseInitStrue;
	//TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	//TIM_DeInit(TIM3);
	TimeBaseInitStrue.TIM_Period=per;
	TimeBaseInitStrue.TIM_Prescaler=psc;
	TimeBaseInitStrue.TIM_CounterMode=TIM_CounterMode_Up;
	TimeBaseInitStrue.TIM_ClockDivision=TIM_CKD_DIV1;
	 
	TIM_TimeBaseInit(TIM3, &TimeBaseInitStrue);
	TIM_ITConfig(TIM3,TIM_EventSource_Update, ENABLE);
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;		
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;	
	NVIC_Init(&NVIC_InitStructure);	
	
	TIM_Cmd(TIM3, ENABLE);
}

中断服务函数,这里也有些奇怪,MM32和stm32的例程不太一样。

void TIM3_IRQHandler (void) //中断服务函数
{
	uint32 state = TIM3->SR;													// 读取中断状态
	i++;   
	TIM3->SR &= ~state;																// 清空中断状态
}

上面的代码俄罗斯方块累满了一行之后消去会神奇的产生问题,我调了一晚上和一下午都解决不了,可能游戏逻辑理解的不是特别深刻,在很多遍历数组的时候i的界限被我修改乱了,导致数组的一行不能完全遍历,而且有时候还会出现同时消掉两行,和方块掉到越界区域的问题。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

唔~~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值