C语言图形编程–俄罗斯方块制作(一)详解

最后更新于:2022-04-01 20:30:22

效果图 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-24_57bd6b1a1b68d.jpg) 用C语言实现俄罗斯方块,需要先解决下面几个问题: **1、如何用C语言绘制图形界面** EasyX图形库(http://www.easyx.cn)即TC的图形库在VC下的移植。 包含库#include 先初始化图形窗口 initgraph(WINDOW_WIDTH, WINDOW_HIGH) ;WINDOW_WIDTH为窗口的宽带,WINDOW_HIGH为窗口的高度。 清空绘图设备 cleardevice(); 设置画笔颜色 setcolor(RED) ; 设置线条风格 setlinestyle(PS_SOLID, NULL, 0); 画矩形 rectangle 还有画线、显示文字等函数,可以参照其帮助文档。 注意:由于我们用的是EasyX图形库,故源文件后缀要为.cpp,但其中内容都是C的语法。 **2、如何存储表示出俄罗斯方块的形状** 在计算机中如何让一串的01数字,代表俄罗斯方块? 一、我们可以用编号,不同的编号代表不同的俄罗斯方块,根据编号把不同方块的画法写在代码中,这样19种 方块就得有19种相应的代码来描绘。而且这样扩展性不好,若以后设计了新的方块,则需要更改大量源代码。 二、我们很自然的想到可用字模点阵的形式来表示,即设置一个4行4列的数组,元素置1即代表这个位置有小 方块,元素置0即代表这个位置无小方块,这个整个的4*4的数组组成俄罗斯方块的形状。 1000 1000 1100 0000 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-24_57bd6b1a3cde9.jpg) 这个方法挺靠谱,但我们还可以优化一下:不用4*4的数组,而是用16个bit位来表示这个点阵。这样存储起来比较方便,故我们用unsigned int 的低16位来表示方块的点阵。 我们可以用掩码与表示俄罗斯方块的位进行操作,来识别并在屏幕上画出方块。 详情见GUI.cpp中的DrawRock函数。 ~~~ //逐位扫描由unsigned int的低2字节 //16个位组成的俄罗斯方块形状点阵(其代表4*4的方块形状) mask = (unsigned int)1 << 15 ; for (i=1; i<=16; i++) { //与掩码相与为1的 即为方块上的点 if ((rockArray[rockIndex].rockShapeBits & mask) != 0) { //在屏幕上画出此方块 rectangle(rockX+2, rockY+2, rockX+ROCK_SQUARE_WIDTH-2, rockY+ROCK_SQUARE_WIDTH-2) ; } //每4次 换行 转到下一行继续画 i%4 == 0 ? (rockY += ROCK_SQUARE_WIDTH, rockX = currentLocatePtr->left) : rockX += ROCK_SQUARE_WIDTH ; mask >>= 1 ; } ~~~ 我们把俄罗斯方块点阵的数位存在rockArray中,我们可以事先把这19种方块的字模点阵自己转化成十六进制,然后在rockArray数组的初始化时赋值进去。 但这样做未免有点太费力,且扩展性也不太好,若以后设计的新方块种类加入,要改变数组rockArray中的值。 我们可以考虑把所有俄罗斯方块的点阵**存储在配置文件中**,在程序初始化时读取文件,把这些点阵转换成unsigned int的变量存储在rockArray中。 这样,以后我们增添新的方块形状只需要在配置文件中增加新的点阵即可。 @### @### @@## ####   (为使得看起来更醒目,我们用@表示1,用#表示0) **3、如何让图形动起来** 若没有按键的情况下,方块是自动下落的。 如何实现自动下落?在某位置处用函数DrawRock在屏幕上画出俄罗斯方块,然后再擦除掉(即用背景色在原位置处重绘一次方块),最后在下落的下一个位置处用函数DrawRock在屏幕上画出俄罗斯方块,如此循环,中间用计时器间隔一段时间以控制下落的速度。 同理,按下屏幕的左右键也是如此,只是在按下键盘时把方块的位置重新计算了。 那么按下上方向键时,如何让方块翻转呢? 我们在配置文件中就把方块的顺时针翻转形态放在了一起: @### @### @@## #### @@@# @### #### #### @@## #@## #@## ####  ##@# @@@# #### #### 我们每按一次上方向键改变一次方块的形状即可。若一直按上键,形状应该是循环地翻滚。 我们想到了循环链表的数据结构可实现这个效果。 可是我们若把这些一种类的方块的各种形态串成循环链表形式,那么每次重新生成方块时我们就难以随机地生成方块了。 故还是得用数组来存储,但又要有循环链表的功能,于是我们想到了**静态循环链表**。 我们用结构体来作为一个方块在rockArray中的元素 typedef struct ROCK {  //用来表示方块的形状(每一个字节是8位,用每4位表示方块中的一行)    unsigned int rockShapeBits ;     int          nextRockIndex ;  //下一个方块,在数组中的下标 } RockType ; ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-24_57bd6b1a50937.jpg) 这样,当我们按下上方向键时,把传入函数DrawRock中的rockIndex变为当前方块结构体中的nextRockIndex即可。 详情见play.cpp中的ProccessUserHit函数。 **4、如何判断方块什么时候停止什么时候满行得分** 方块一直下落,最终是要停下来的,我们要设置一个边界来约束方块的移动范围。我们把当前游戏界面划分成以俄罗斯方块中的小方格为单位的格子,用一个二维数组g_gameBoard来表示这些小方格的状态,1表示此位置有方块,0表示此位置为空。 我们按照界面的大小和方格的大小来计算此二维数组时,再多设置一圈“围墙”,即多加两行两列,并把它们的值初始化为1。 当方块准备下落或是左右移动的时候,前提前检查其即将落下的位置是否为空,若不为空,则停止下落,并把当前俄罗斯方块占用的方格都设置为1。 详情见play.cpp中的moveAbled函数。 判断满行: 从最后一行开始往上检查g_gameBoard,若有一行全为1,则说明此行满行,将此行擦出,把此行上面的所有行向下移动一个单位。 详情见play.cpp中的ProcessFullRow函数 **5、其他细节问题:** 如何快速下落 详情见play.cpp中的FastFall函数 如何暂停 详情见play.cpp中的ProccessUserHit函数 此游戏程序的主要逻辑在play.cpp中的PlayGame函数 [**源代码点击这里**](http://blog.csdn.net/yang_yulei/article/details/17651961)
';