IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 基于STM32F407的俄罗斯方块游戏代码分析 -> 正文阅读

[嵌入式]基于STM32F407的俄罗斯方块游戏代码分析

这里只给了关键代码进行分析,并非全部代码。

项目概述和测试见文章

基于STM32F407的俄罗斯方块小游戏的设计_钻仰弥坚的博客-CSDN博客

一、方块编码的方式

首先需要知道俄罗斯方块本质上为4个小方块组成的正方形。总共有7种基本类型。每种方块又可以通过旋转而变化出1到4种形状,一共就有19种。具体关系如图1所示。

1 方块对应关系图

关于方块的编码形式,我们可以借用Ucos-II的一些思想。我们知道在Ucos-II中的任务就绪表记录了系统中所有处于就绪状态的任务,从代码上来看它就是一个类型为INT8U的一维数组OSRdyTbl[]那么在俄罗斯方块中,我们也可以定义一个一维数组,设置一种规律,也就是利用枚举法,将19种方块进行编码,这样我们也就可以随机生成方块了。

举个例子,我们假设存在一个4×4的方块,这又16个小方块组成,对16个小方块编一个序号,对于19种方块都可以容纳进这样的4×4的方块之中,这里我们对一个L型的方块进行编码,如图2所示,该L型方块占据了15,14,7,6号方块,那么我们可以用0和1来区分该区域是否有方块,为1时存在方块,为0时为空,那么该图形二进制的形式编码为0100 0100 0110 0000,转换成16进制的是0x4460,那么我们可以把0x4460当成数组中的一个元素。

2 方块示例

代码如下:

void LCD_DrawBlock(u16 sx,u16 sy,u16 color)
{
	LCD_DrawRectangle(sx,sy,sx+BPIXEL-1,sy+BPIXEL-1);	
	LCD_Fill(sx+2,sy+2,sx+BPIXEL-3,sy+BPIXEL-3,color);	
}

类似地,我们可以对19种方块都进行编码,对应关系如图3所示:

3?方块对应编码图

二、单个方块的创建与消除

首先我们可以设定每个方块占20个像素,对于这20像素的宽度,我们可以细分为一个空心正方形嵌套一个实心正方形,他们具体的长度如图4所示。

? 图4 小方块长度图

可以把创建一个20像素的正方形和16像素的实心正方形写成一个函数,并且对该位置标记已占据,这样其余模块可以调用这个函数创建各种各样的方块。同理如果我们需要消除方块,也可以直接用白背景直接覆盖住这个方块,并且取消占据标记即可完成。

三、方块的移动

我们阅读STM32的底层代码时,发现STM32的按键实验和红外线模块实验均是使用while函数内加上一个switch函数来实现按键或者遥控器的控制,那么在俄罗斯方块的项目中,我们也可以这样完成方块的操纵,而方块的移动我们可以更加细化一点,就是需要实现原来位置方块处的消失,新位置方块的生成,无论方块是如何移动,这样的基本步骤不变,如图5所示。

5 方块移动流程图

这里需要提一下旋转函数,这里使用的是枚举法。我们可以根据上面提到的图3的对应关系,通过switch函数来进行转换对应的编号。

?代码如下:

u16 Tetris[19]={0x0F00,0x4444,0x0660,0x4460,0x02E0,0x6220,0x0740,0x2260,0x0E20,0x6440,0x0470,0x0C60,0x2640,0x0360,0x4620,0x04E0,0x2620,0x0E40,0x4640};

u8 Judge(u16 sx,u16 sy,u8 n,u8 mode)
{
	int cx,cy,temp1=Tetris[n],temp2=Tetris[n];
	u8 a,b,i,Flag=0;
	switch(mode)
	{
		case 1:	cx=sx-BPIXEL;cy=sy;break;
		case 2:	cx=sx+BPIXEL;cy=sy;break;
		case 3:	cx=sx;
				cy=sy;
				switch(NewShape.CurNum)
				{
					case 0:temp2=1;break;
					case 1:temp2=0;break;
					case 2:temp2=2;break;
					case 3:temp2=4;break;
					case 4:temp2=5;break;
					case 5:temp2=6;break;
					case 6:temp2=3;break;
					case 7:temp2=8;break;
					case 8:temp2=9;break;
					case 9:temp2=10;break;
					case 10:temp2=7;break;
					case 11:temp2=12;break;
					case 12:temp2=11;break;
					case 13:temp2=14;break;
					case 14:temp2=13;break;
					case 15:temp2=16;break;
					case 16:temp2=17;break;
					case 17:temp2=18;break;
					case 18:temp2=15;break;
				}
				NewShape.TurnNum=temp2;
				temp2=Tetris[temp2];
				break;
		case 5:	cx=sx;cy=sy+BPIXEL;break;
		default:cx=sx;cy=sy;break;					
	}
	for(i=0;i<16;i++)
	{
		a=i/4;	
		b=i%4;
		if(temp1&0x8000)BoxSR[(sy/BPIXEL)+a]&=~(1<<((sx/BPIXEL)+b)); //将变形前填充的模块置0
		temp1<<=1;
	}
	for(i=0;i<16;i++)
	{
		a=i/4;
		b=i%4;
		if(temp2&0x8000)
		{
			
			if(BoxSR[cy/BPIXEL+a]&(1<<((cx/BPIXEL)+b))) Flag=1;	//如果&为真,表示变化后有模块冲突,不能实现变形
		}
		temp2<<=1;			
	}
	if(Flag==0)return 1;	//可以实现变形返回1,不能变形返回0
	else return 0;
}

四、方块冲突检测

关于冲突检测也是最重要的一个环节,这样确保方块在下方有方块时不能继续下落,在四周有障碍物的时候不能移动。无论方块是如何移动的,冲突检测方式不会变。首先是假设他可以移动,确定好移动的目标位置,然后对计算出来目标位置进行检测看该处是否有方块占据,如果有方块占据则不能显示,如果没有方块占据则可以显示出来。冲突检测流程图如图6所示。而检测是否有占据的方法便是查看对应的位是置0还是置1,如果置1,说明方块已经被占据,返回无法移动的标识,如果置0,说明可以方块移动,返回可以移动的标识。

6 冲突检测流程图

代码:

if(Draw_Ready==1)	//可以左移
		{
			LCD_ClearShape(NewShape.x,NewShape.y,NewShape.CurNum,BOXS_COLOR);
			NewShape.x-=BPIXEL;
			LCD_DrawShape(NewShape.x,NewShape.y,NewShape.CurNum,SHAPE_COLOR);
		}
if(Draw_Ready==1)	//可以右移
		{
			LCD_ClearShape(NewShape.x,NewShape.y,NewShape.CurNum,BOXS_COLOR);	//清除右移前的模块显示
			NewShape.x+=BPIXEL;
			LCD_DrawShape(NewShape.x,NewShape.y,NewShape.CurNum,SHAPE_COLOR);	//显示右移后的模块
		}

五、满行检测

俄罗斯方块的游戏每下落一次方块还需要进行满行的检测,当存在满行的时候该行就会消除,位于其上方的方块全部下移。之后会给玩家以加分。前面我们已经说明了整个游戏界面是一个一维数组,只要我们每次方块停止下落的时候,遍历一次数组,如果存在某个值为0xFFFF,这样这个数组所代表的方块行将会被消除,在该行之上的方块整体下落。检测流程图如图7所示。

7 满行检测流程图

代码:

for(i=4;i<Y_BOXS+4-1;i++)	//扫描除了最顶上5行之外的行
			{
				if(BoxSR[i]==0xFFFF) //一行填满,进行数据更新和消行操作
				{
					Game.score=Game.score+40;
					Game.level=1+Game.score/100;
					if(Game.score>=Game.bestscore)
						Game.bestscore=Game.score;
					LCD_ShowNum(160,640,Game.score,3,16);
					LCD_ShowNum(160,660,Game.level,3,16);		
					LCD_ShowNum(160,680,Game.bestscore,3,16);

					Clear_Flag=1;
					for(j=i;j>3;j--)BoxSR[j]=BoxSR[j-1];	//将满行之上有模块的行平移到当前行
				}		
			}
			if(Clear_Flag)LCD_Fill(20,20,300,440,BOXS_COLOR);
			for(i=3;i<Y_BOXS+4-1;i++)	
			{
				temp=BoxSR[i];
				temp>>=1;
				for(j=1;j<15;j++) //更新每行显示
				{
					if(temp&0x0001)LCD_DrawBlock(j*BPIXEL,i*BPIXEL,SHAPE_COLOR);
					temp>>=1;
				}
			}
			Create_Shape();

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-07-17 16:39:38  更:2022-07-17 16:41:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 12:06:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码