本帖最后由 ifreecoding 于 2012-3-11 21:56 编辑
Mindows5.5n.part1.rar
(3.34 MB)
Mindows5.5n.part2.rar
(3.34 MB)
Mindows5.5n.part3.rar
(2.64 MB)
我手边有一块STM32F103VB的板子,在这个板子上有一个LCD显示屏和5个按键,正好可以用来做俄罗斯方块这个游戏。
图 1
STM32F103VB开发板
我先是在这块板子上写了一个实时嵌入式操作系统内核——Mindows,当我完成了这个操作系统的抢占式任务调度、信号量及队列等功能后,我想验证一下这个操作系统的功能,于是,就在这块板子上写了一个俄罗斯方块的游戏,在此介绍一下这个游戏的设计思路,并提供源代码,你可以在本文后面看到这个游戏在单板上运行的截图,如果你想观看游戏过程的视频或获取源代码,请登录我的新浪博客blog.sina.com.cn/ifreecoding获取。
俄罗斯方块的游戏我们应该都玩过,先将这个游戏的基本功能整理一下。我们最容易想到的是按键功能,需要能控制图形向下、向左和向右移动,还需要能旋转图形,当我们不按向下按键时图形还需要能自动向下走,当游戏结束时,我们需要一个按键可以重新开始游戏。在游戏过程中还需要遵守一些规则,比如说不同的图形互相之间不能重叠,图形不能移出屏幕的范围,当屏幕的一行被图形填满时本行需要被删除,并且上面所有的图像向下移动,还有最重要的一点,我们需要使用LCD将游戏的过程显示出来。
我们将这些需求做一个表格整理一下:
功能分类
| 子功能
| 描述
| 按键功能
| 开始键
| 按下开始键,游戏重新开始,所有状态归零
| 旋转键
| 按下旋转键,图形可以旋转
| 向下键
| 按下向下键,图形向下走
| 向左键
| 按下向左键,图形向左走
| 向右键
| 按下向右键,图形向右走
| 界面功能
| 全屏显示
| 需要能实时显示游戏画面
| 下个图形显示
| 将屏幕分为左右两部分,左侧为游戏空间,右侧显示下个出现的图形
| 游戏功能
| 自动走
| 在没有按向下键时,图形应以一定速率自动向下走
| 冲突检测
| 当图形移动、变形时不能与其它图形重叠
| 下个图形开始
| 当图形向下走会发生冲突时对本图形的操作结束,下个图形从屏幕顶端进入屏幕,并更新屏幕右侧的下个图形
| 删除一行
| 当图形向下走会发生冲突时对本图形的操作结束,检测是否有被图形占满的一行,若有需要删除此行
| 删行闪烁
| 在删除一行时,需要反复改变被删除行的颜色,以呈现出闪烁效果,然后再删除此行
| 图形下移
| 在一行被删除后,被删除行上面的所有图形都需要向下移一行
| 打印功能
| 任务切换打印
| 将任务切换过程从串口打印出来
|
表 1
俄罗斯方块游戏需求列表
表1中列出了我们需要做的功能,接下来我们就要想办法来实现这些功能。我们首先要解决的一个问题是我们必须要有一个办法能使用处理器来表示这些图形,这样才能对它们进行控制,并将它们显示到LCD上。LCD中的每一个像素就是一个数据,代表着这个像素的颜色,因此我们可以在内存中用一个二维数组来对应LCD中的像素,数组的两个维度对应着LCD的X轴和Y轴,数组中元素的数值就对应着LCD中对应位置的像素的数值,数组元素为黑色像素的数值时代表LCD中对应的位置没有图形,其它数值代表有图形,这样就可以把对LCD中图形的操作转换为对数组数据的操作了,然后再以一定的频率将数组中的数据输出到LCD中,这样就实现用处理器控制LCD中的图形了。
经过上面的分析,我们就可以将图形映射为数组数据,我们只要专注于处理数组数据,至于LCD中的图形则只需要将数组数据刷到LCD上就可以了。由此可以想到的一个简单的处理方法是使用一个任务专门来处理数组数据,使用另一个任务专门来将数组数据刷到LCD上,至于按键功能则可以用另一个任务专门来检测按键的输入。这样的3个任务相互之间的耦合性很小,很适合使用并行的任务实现,按键任务负责检测按键输入,如有按键输入则向处理数据的任务传送按键值,处理数据的任务接收到按键值就对数组数据进行处理,而刷新LCD的任务只需要以固定的频率将数组数据刷新到LCD上就可以了。至于打印任务切换的功能还可以使用Mindows提供的任务切换钩子函数的方法实现,在任务切换时使用任务切换钩子函数将切换信息打印到内存中,再使用一个打印任务将内存中的数据打印到串口。
按键、处理、刷屏这3个用于实现游戏功能的任务结构关系如下图所示:
图 2
俄罗斯方块游戏任务结构图
KeyTask任务周而复始的检测按键是否按下,由于对按键按下的频率有要求,因此这个任务需要以一定的间隔来读取按键值,在间隔期间内不读取按键值,这样也可以防止按键抖动的情况发生。当该任务读取到一个有效的按键值时,就将这个按键值压入队列发送给ProcessTask任务。
ProcessTask任务周而复始的从队列里取消息,当队列为空时,该任务就会被队列阻塞,处于pend状态,直到队列里有了消息才被激活,然后从队列里取出按键值,根据按键值来对图形数组的数据做相应的操作。
FlushScn任务则以一定的频率将图形数组中的数据刷新到LCD屏幕上。每个图形是由多个像素组成的,对图形处理、刷新的过程就是对图形中像素的处理、刷新过程,需要对多个像素进行多次操作才能完成对一个图形的操作。FlushScn任务和ProcessTask任务是并行执行的,为避免这两个任务在对同一个图形的处理过程中相互干扰,就需要在这2个任务中使用信号量锁住对同一个图形的处理过程,使这两个任务对同一个图形的操作保持串行性,保证图形处理、显示时的完整性。
这3个任务的结构大致如下所示:
void TEST_KeyTask(void)
{
while(1)
{
/* 任务以一定的间隔读取按键值 */
MDS_TaskDelay();
/* 读取按键值 */
DEV_ReadKey();
/* 将按键值放入队列 */
MDS_QuePut();
}
}
void TEST_ProcessTask(void)
{
while(1)
{
/* 获取按键值 */
MDS_QueGet();
/* 获取信号量 */
MDS_SemTake();
/* 根据按键值对数组数据做相应处理 */
TEST_KeyProcess();
/* 释放信号量 */
MDS_SemGive();
}
}
void TEST_FlushScnTask(void)
{
while(1)
{
/* 获取信号量 */
MDS_SemTake();
/* 刷新LCD屏幕 */
TEST_FlushScn();
/* 释放信号量 */
MDS_SemGive();
/* 任务以一定的间隔刷新LCD */
MDS_TaskDelay();
}
}
KeyTask任务和FlushScn任务比较简单,都是以固定的频率执行重复的动作,ProcessTask任务比较复杂,是实现俄罗斯方块游戏的关键,下面我们再重点介绍一下这个任务。
ProcessTask任务的主要功能是根据按键值来对数组数据做相应的处理,这其中涉及到图形移动、变形、冲突检测、删除等操作,实现了这些操作也就基本实现了俄罗斯方块这个游戏。
俄罗斯方块游戏里共有8种基本的图形,部分图形经过旋转会表现出不同的形状,如下图所示:
图 3
俄罗斯方块游戏图形
这些图形都是由基本的小方块组成,每个小方块都是由4×4的像素组成,每次对图形处理都是以这些小方块为基本单元进行的,因此,我们可以将每个小方块看做是一个像素,这样就可以使用较少的内存来映射整个LCD的屏幕了。
STM32F103VB板子上LCD的大小为128×160像素,每个像素需要用16bits数据来表示它的颜色。这其中我们使用左边的88×160像素作为游戏区域,右边36×160像素作为下个图形显示区域,中间用4×160像素的线条作为左右部分的分隔线。左边游戏区域内的图形会不断的发生变化,因此我们需要使用一个(88÷4)×(128÷4)大小的short型二维数组来表示游戏区域。中间分隔线不会变化,只需要在初始化时画出这条线就可以了,不需要在内存中保留相应的数组。右侧的下个图形显示区只是更新下个图像这一小块区域就可以了。
这些图形中最长的图形长度为5×4=20个像素,考虑到变形,我们需要使用20×20个像素的一块空间才能存放下任意一个图形,因此,我们只需要在内存中开辟一个(20÷4)×(20÷4)大小的short型二维数组就可以表示任意一个图形了,更新屏幕右侧的下一个图形窗口只需要这么大小的一块内存就可以了。
当图形在游戏区域内向下移动的时候,我们还需要开辟一个(20÷4)×(20÷4)大小的short型二维数组来表示当前的图形,当图形向下走一行时,先从游戏区域数组里将该图形清除掉,然后将当前图形数组在游戏区域数组内下移一行,如果下移后的当前图形数组与游戏区域数组没有图形数据冲突的话,就将下移后的当前图形数组写入游戏区域数组内,完成本次下移操作。如果有冲突的话,则说明当前图形已经不能再下移动了,将当前图形数组原地恢复到游戏区域数组内,再去判断当前图形所在的所有行内是否有填充满的整行数据,如果有的话就删除这些行,在删除的时候每隔一段时间变换一下这些行的颜色,以表现出删行时闪烁的效果。当删除一行后,需要将该行上面的所有图形数据都向下移动一行,并将下个图像数组复制到当前图形数组内,将当前数组的位置恢复到图形出现的最上方,再随机选择一个图形,将下个图形数组更新为该图形,完成本次操作。
其它的向左向右和变形操作也需要先从游戏区域数组内将当前图形清除掉,然后判断相应操作后是否会有图形冲突,如果没有冲突的话就将当前图形数组数据复制到操作后所在的游戏区域数组内的位置,完成本次操作,如果有冲突的话就将当前图形数组原地恢复到游戏区域数组内,结束本次操作。
上面就是有关俄罗斯方块游戏的基本介绍,代码就不具体介绍了,代码里都有注释,通过本文的介绍并结合代码应该就可以理解这个游戏的实现方法了。如果你感兴趣的话,可以再增加一个按键声音的功能,无非就是再创建一个播放声音的任务,当按键任务检测到按键时向声音任务发送一个消息激活声音任务,由声音任务播放按键声音,原来的软件结构基本不用改动。
下图是在STM32F103VB单板上运行俄罗斯方块的图片,如果你想观看游戏视频或者查看源代码的话请登录我的博客blog.sina.com.cn/ifreecoding下载。
图 4
STM32F103VB单板运行俄罗斯方块游戏 |