最近有空闲时间,就想编一个俄罗斯方块试试,期间也碰到了一些问题,在网上查找了很多资料。
现在代码已经完成了,游戏支持开始(包括从暂停恢复和结束后重新开始)、暂停、空格键落地及基本功能,玩起来会更好一些。
游戏截图
附上全部游戏代码(复制后保存为.c文件,编译即可),windows xp ,VS2010 WIN32工程下,C编译通过:
/******************************
软件:Tetris
别名: 俄罗斯方块
作者: su
完成日期: 2012-9-14
遗留问题:
- 画文字超出界面无法显示提示问题
- 局部刷新
- 速度问题因为定时器机制,导致10级之后没明显变化,没达到要求
修改记录:
2014.05.19 Ver: 1.2.2014.519
- 取消列对齐辅助线,仿照俄罗斯方块流行游戏功能,替换为指示当前方块下落位置,动态,更直观。
2013.11.07 Ver: 1.1.2013.1107
- 解决Tetris的位图对象内存泄露问题
2012.10.20 Ver: 1.0.2012.1020
- 检查游戏结束条件不变,(如果变为允许堆积到顶,需要生成新方块时,当前方块已经超出容器顶部,则游戏结束,则需要修改消行,最顶行需要填充为0)
2012.9.29 Ver: 1.0.2012.929
- 增加初速度选择,速度达到50后会反转
- 重新开始游戏会继承上次初速度,关闭程序才会再次出现速度选择界面
- 任何情况下都可以调整初速度,但调整完初速度后会重新开始游戏
2012.9.28 Ver: 1.0.2012.928
- 增加列对齐辅助线
- 使用实心矩形方块(空心,玩久了有些眼花)
- 解决当前下落方块最顶端贴边问题(未出现方块部分不画)
- 调整窗口创建style样式,不支持最大化,最小化,只支持关闭按钮
2012.9.20 Ver: 1.0.2012.920
- memcpy优化复制行
- 消行时一行行进行,有些复杂,继续优化
2012.9.19 Ver: 1.0.2012.919
- 内存越界问题已经解决
- 新增功能,按空格键直接落地
- 解决游戏结束,顶部绘新方块问题
- 新增功能,开始,暂停
2012.9.18 Ver: 1.0.2012.918
- 优化消行,消行只会出现在下落方块的4行里
2012.9.16 Ver: 1.0.2012.916
- 修正游戏首次生成方块。当前方块和下一方块随机数相同问题,
- 采用double buffer绘制游戏屏幕,去掉背景刷,解决重绘背景的区域闪烁问题。
- 显示下一方块。
- 加入消多行分数奖励。
- 增加速度等级,从1-50级。
- 按键Esc直接退出游戏。
- 开始程序时,显示在屏幕中间
- 解决方块刚出现,顶部无法旋转问题
- 解决游戏结束判断延迟问题
- 解决方块快堆满时,新方块碰撞计算失误问题
2012.9.15 Ver: 1.0.2012.915
- 修改随机数种子,避免随机数规律性出现。
- 修改旋转形状数组,为确保底部旋转不会导致方块上升一格,从而继续下降,旋转规则为保持底部水平(变形需要调整,到底后变形可以增高一行继续下落。)
- 优化代码,优化当前方块变量
******************************/
#include <time.h>
#include <windows.h>
/* 方块细胞之间的参数 */
#define CELLS_WIDTH (30)
#define CELLS_DISTANCE (1)
/* 方块的种类 */
#define BLOCK_KINDS (7)
#define BLOCK_ROTATES (4)
#define BLOCK_WIDTH (4) /* 方块边长4 */
#define BLOCK_SIZE (16) /* 方块占用矩阵大小4x4 BLOCK_WIDTH x BLOCK_WIDTH */
/* 消多行分数奖励 */
static const int ScoreSets[BLOCK_WIDTH] = {100, 400, 800, 1600};
/* 容纳方块的空间 逻辑坐标系 从左到右width 从上到下high */
#define CONTAINER_WIDTH (10)
#define CONTAINER_HIGH (20)
/* 容器矩形坐标 */
RECT ContainerRect = {CELLS_DISTANCE, CELLS_DISTANCE,
CELLS_DISTANCE + CELLS_DISTANCE + CONTAINER_WIDTH * (CELLS_WIDTH + 1) + CELLS_DISTANCE + CELLS_DISTANCE,
CELLS_DISTANCE + CELLS_DISTANCE + CONTAINER_HIGH * (CELLS_WIDTH + 1) + CELLS_DISTANCE + CELLS_DISTANCE};
/* 下一个方块容器矩形坐标 */
RECT NextBlockRect = {CELLS_DISTANCE + CELLS_DISTANCE + CONTAINER_WIDTH * (CELLS_WIDTH + 1) + CELLS_DISTANCE + CELLS_DISTANCE + CELLS_DISTANCE + CELLS_DISTANCE,
CELLS_DISTANCE,
CELLS_DISTANCE + CELLS_DISTANCE + CONTAINER_WIDTH * (CELLS_WIDTH + 1) + CELLS_DISTANCE + CELLS_DISTANCE + CELLS_DISTANCE + CELLS_DISTANCE + CELLS_DISTANCE + BLOCK_WIDTH * (CELLS_WIDTH + 1) + CELLS_DISTANCE + CELLS_DISTANCE,
CELLS_DISTANCE + CELLS_DISTANCE + BLOCK_WIDTH * (CELLS_WIDTH + 1) + CELLS_DISTANCE + CELLS_DISTANCE};
/* 窗口大小 */
/* 窗口宽度 = 空隙 + 容器左边界 + 方块CONTAINER_WIDTH * (空隙 + CELLS_WIDTH) + 空隙 + 容器右边界 + 空隙 + 下一个容器左边界 + BLOCK_WIDTH * (空隙 + CELLS_WIDTH) + 空隙 + 下一个容器右边界 + 空隙 + 窗口两边占用部分 */
#define WINDOW_WIDTH (CELLS_DISTANCE + CELLS_DISTANCE + CONTAINER_WIDTH * (CELLS_WIDTH + 1) + CELLS_DISTANCE + CELLS_DISTANCE + CELLS_DISTANCE + CELLS_DISTANCE + BLOCK_WIDTH * (CELLS_WIDTH + 1) + CELLS_DISTANCE + CELLS_DISTANCE + CELLS_DISTANCE + 7) /* 7 为窗口边占用 */
/* 窗口高度 = 空隙 + 容器上边界 + 方块CONTAINER_HIGH * (空隙 + CELLS_WIDTH) + 空隙 + 容器下边界 + 空隙 + 窗口上下两边占用部分 */
#define WINDOW_HIGH (CELLS_DISTANCE + CELLS_DISTANCE + CONTAINER_HIGH * (CELLS_WIDTH + 1) + CELLS_DISTANCE + CELLS_DISTANCE + CELLS_DISTANCE + 33) /* 33 为标题栏高度 */
typedef struct Tetris
{
unsigned int timerid;
unsigned int GameState; /* 游戏状态,0,游戏运行; 1,游戏开始;2,游戏暂停 */
unsigned int initalSpeed; /* 初速度 */
unsigned int speed; /* 速度 */
unsigned int score; /* 得分 */
/* 下一个方块 */
const unsigned char *next_block;
unsigned int next_kind;
unsigned int next_rotate;
/* 当前方块 */
const unsigned char *current_block;
unsigned int kind;
unsigned int rotate;
int offset_left; /* 逻辑坐标 left, 正常范围 -min_left ~ CONTAINER_WIDTH - max_right */
int offset_top; /* 逻辑坐标 top, 正常范围 -min_top ~ CONTAINER_HIGH - max_bottom */
int offset_top_destination; /* 用来表明当前方块直线下落位置 */
int min_left;
int max_right;
int min_top;
int max_bottom;
} Tetris;
Tetris tetris = {0};
static unsigned char Container[CONTAINER_HIGH][CONTAINER_WIDTH] = {0};
/* 行列式
0 1 2 3 4 5 6 7 8 9 x CONTAINER_WIDTH
00□□□□□□□□□□
01□□□□□□□□□□
02□□□□□□□□□□
03□□□□□□□□□□
04□□□□□□□□□□
05□□□□□□□□□□
06□□□□□□□□□□
07□□□□□□□□□□
08□□□□□□□□□□
09□□□□□□□□□□
10□□□□□□□□□□
11□□□□□□□□□□
12□□□□□□□□□□
13□□□□□□□□□□
14□□□□□□□□□□
15□□□□□□□□□□
16□□□□□□□□□□
17□□□□□□□□□□
18□□□□□□□□□□
19□□□□□□□□□□
y
CONTAINER_HIGH
*/
static const unsigned char BlockSets[BLOCK_KINDS * BLOCK_ROTATES * BLOCK_SIZE] =
{
/*
所有方块的集合(I, J, L, O, S, T, Z),每个方块依次顺时针旋转4次,每个4x4矩阵记录一个形状.
为确保底部旋转不会导致方块上升一格,从而继续下降,旋转规则为保持底部水平
▓
▓ ▓ ▓
▓ ▓ ▓ ▓▓ ▓▓ ▓▓▓ ▓▓
▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓ ▓▓
*/
/*
I
□■□□
□■□□
□■□□
□■□□
*/
0, 1, 0, 0,
0, 1, 0, 0,
0, 1, 0, 0,
0, 1, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
1, 1, 1, 1,
0, 1, 0, 0,
0, 1, 0, 0,
0, 1, 0, 0,
0, 1, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
1, 1, 1, 1,
/*
J
□□□□
□□■□
□□■□
□■■□
*/
0, 0, 0, 0,
0, 0, 1, 0,
0, 0, 1, 0,
0, 1, 1, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 1, 0, 0,
0, 1, 1, 1,
0, 0, 0, 0,
0, 1, 1, 0,
0, 1, 0, 0,
0, 1, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
1, 1, 1, 0,
0, 0, 1, 0,
/*
L
□□□□
□■□□
□■□□
□■■□
*/
0, 0, 0, 0,
0, 1, 0, 0,
0, 1, 0, 0,
0, 1, 1, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 1, 1, 1,
0, 1, 0, 0,
0, 0, 0, 0,
0, 1, 1, 0,
0, 0, 1, 0,
0, 0, 1, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 1, 0,
1, 1, 1, 0,
/*
O
□□□□
□□□□
□■■□
□■■□
*/
0, 0, 0, 0,
0, 0, 0, 0,
0, 1, 1, 0,
0, 1, 1, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 1, 1, 0,
0, 1, 1, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 1, 1, 0,
0, 1, 1, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 1, 1, 0,
0, 1, 1, 0,
/*
S
□□□□