第二篇,主要实现俄罗斯方块中的主体部分,包括容器的数据结构以及容器的相关操作,特别是大方块和容器之间的交互逻辑,包括碰撞检测,消除检测等等。
1. 容器的表示
大方块的实现涉及到位运算,而容器同样如此。容器显示的部分是由 10 * 20 个小方块构成的矩形,如果我们将每个小方块用一个比特来表示,则一行只需要 10 比特,C语言中可以用 unsigned short 表示,不过这里我们为了后期扩展,选用了 unsigned long 类型。
unsigned long blockContainer[TETRIS_CONTAINER_HEIGHT];
blockContainer 变量代表整个容器,TETRIS_CONTAINER_HEIGHT 代表容器的高度。这里需要注意常量 TETRIS_CONTAINER_HEIGHT 并没有定义为 20,而是定义为 25,容器的每行我们用了 12 位比特表示,并没有用 10 位表示,这所以这样,其实是为了碰撞检测带来方便,其中容器的宽高定义如下:
//俄罗斯方块容器宽高
#define TETRIS_CONTAINER_WIDTH (1 + 10 + 1)
#define TETRIS_CONTAINER_HEIGHT (BLOCK_HEIGHT + 20 + 1)
这里我们用容器的示意图表示一下这样定义的好处:
上图是俄罗斯方块真正的容器区域,其中游戏界面显示的仅仅是其中的蓝色显示区域,而绿色隐藏区域用来放置准备下落的大方块,而灰色是用来碰撞检测的隔离区域。
因为 Windows 窗口的纵坐标是从上到下的,所以我们显示的时候也是从上到下,最上边是容器的第 0 行,最下边是容器的 24 行,这一行会用来兜底,防止大方块在下落的过程中越界。
2. 大方块的表示
Windows 窗口的横坐标是从左到右,所以左边是第 0 行,最右边是第 11 行。这里需要注意这和默认大方块表示的二进制顺序并不一样:
事实上,前台显示的画面左边是二进制的低位,右边是二进制的高位,所以大方块真正表示的二进制是和显示的画面水平方向正好是相反的。
3. 结构定义
明白了上面的介绍,接下来我们就可以定义俄罗斯方块的数据结构:
//俄罗斯方块
typedef struct Tetris
{
unsigned long blockContainer[TETRIS_CONTAINER_HEIGHT]; // 容器
int blockIndex; // 当前块索引
Block blocks[TETRIS_BLOCK_NUM]; // 多个块(前后台)
//......
} Tetris;
俄罗斯方块中成员很多,但最重要的就是容器和两个方块的表示