在VS中新建win32 Application Proj,选择Empty ,完成TetrisWin项目创建。新建tetris.c和tetris.h两个文件,打开tetris.h文件。

首先要包括的是可能要用到的头文件,那在这里要用到是什么头文件呢? 本系统是开发一个游戏,那么游戏的话就需要有和用户进行交互的游戏界面,那就需要绘图操作,那么就会用到windows的绘图函数库,所以第一步就是要包括这个windows头文件,但是要注意我们现在是在头文件tetris.h中来包含这个头文件,这里就需要注意使用#ifndef 宏来进行包含处理,因为tetris.h最终会被包含在实现文件中去,但是不确定实现文件的头部是否也会包含这个windows库头文件,如果包括了的话我们就没有必要再包括了,否则就会引发重定义错误,所以在头文件中要包括什么库的头文件时,最好用#ifndef宏先判断是否已包含了该头文件,只有在没有包括的情况下再去包含该头文件,此时才不会报重定义的错误。具体实现如下:

//header infor
#ifndef WIN_H_H
#define WIN_H_H
#include <Windows.h>
#endif

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

因为游戏中只有7种游戏方块,所以可以声明为一个枚举类型,同时也有利于在游戏中为了方便的直到当前方块形状和下一个方块形状。具体实现如下:

//self_definition enum 
typedef enum tetris_shape{
    ZShape = 0,
    SShape,
    LineShape,
    TShape,
    SquareShape,
    LShape,
    MirroredLShape
}shape;

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

接下来根据要实现的功能模块声明相应的处理函数。具体实现如下:

//function declaraction
int maxX();        //取得当前方块的最大x坐标
int minX();        //取得当前方块的最小x坐标
void turn_left();  //将当前方块逆时针旋转90度
void turn_right(); //将当前方块顺时针旋转90度
int out_of_table();//检查当前方块是否超出桌面的范围
void transform();  //旋转当前方块
int leftable();    //判断当前方块能否左移
int rightable();   //判断当前方块能否右移
int downable();    //判断当前方块能否下移
void move_left();  //向左移动当前方块
void move_right(); //向右移动当前方块
 
//operation function
int add_to_table();//将当前方块固定到桌面上,若返回0,表示游戏结束
void remove_full();//删除桌面上填满的行
 
//control function
void new_game(); //创建一个新游戏
void run_game(); //运行游戏
void next_shape(); //将下一个方块设为当前方块,并设置下一个方块
int random(int seed);//取得一个随机数,例如random(7)将返回一个0-6之间的随机数
 
//paint function
void paint();  //将内存位图输出到窗口上
void draw_table(); //绘制游戏桌面
 
//other functions
void key_down(WPARAM wParam); //处理键盘按下事件
void resize();   //改变窗口大小时调用的函数
void intialize();//初始化
void finalize(); //结束时,释放资源
 
//callback function
//回调函数,用来处理windows消息
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

上面基本上把需要用到的处理函数都已声明完毕,接下来打开tetris.c文件来实现相应的功能,首先需要包含一些头文件,1.系统中需要用到sprintf()这样函数来格式化输出一些字符到相应的变量中,所以需要包含stdio.h文件,同时需要使用当前时间作为rand()种子来获取随机数,所以需要包含time.h文件,当然还需要包含tetris.h文件,具体实现如下:

//Header Info
#include <time.h>
#include <stdio.h>
#include "tetris.h"

接下来需要定义一些常量,包括游戏开始结束时的提示信息以及相应的颜色值等,具体定义如下:

//constant definition
#define APP_NAME "TETRIS"
#define APP_TITLE "Tetris Game"
#define GAMEOVER "GAME OVER"
 
#define SHAPE_COUNT 7  //形状的个数
#define BLOCK_COUNT 4  //每个形状由几个小表格构成
#define MAX_SPEED 5   //速度级别
#define COLUMS 20     //游戏桌面表格的列数
#define ROWS 30       //游戏桌面表格的行数
 
//7种颜色
#define RED RGB(255,0,0)
#define YELLOW RGB(255,255,0)
#define GRAY RGB(128,128,128)
#define BLACK RGB(0,0,0)
#define WHITE RGB(255,255,255)
#define STONE RGB(192,192,192)
 
#define CHARS_IN_LINE 14    //提示信息的一行有多少个字符
#define SCORE "SCORE  %4d"  //得分格式化

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }接下来定义一些全局变量:

//global variables definition
char score_char[CHARS_IN_LINE] = {0}; //声明一个接收得分情况的字符数组
char* press_enter = "Press Enter Key...";
char* help[] =  //帮助提示信息
{
    "Press space or up key to transform shape.",
    "Press left or right key to move shape.",
    "Press down key to speed up.",
    "Press enter key to pause game.",
    "Enjoy it. :-)",
    0
};

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

接下来把游戏状态定义为一个枚举类型,便于对其进行操作,具体实现如下:

//enum  the state of game
enum game_state
{
    game_start,
    game_run,
    game_pause,
    game_over
}state = game_start;

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

之前我们把游戏的7种方块定义成了一个枚举类型,那么我们会通过这些枚举变量的相应的值来判断目前是哪种方块,同时我们要为这些方块涂上不同的颜色,所以我们最好也把这些颜色定义为一个数组,这样到时候要为某个方块涂上对应颜色的时候也只需要根据当前方块的枚举变量值做为数组的下标值来确定相应的颜色方案,所以下面来定义颜色数组:

//color of Rectangle 
COLORREF shape_color[] = 
{
    RGB(255,0,0),
    RGB(0,255,0),
    RGB(0,0,255),
    RGB(255,255,0),
    RGB(0,255,255),
    RGB(255,0,255),
    RGB(255,255,255)
};

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

接下来定义表示7种方形相对坐标的三维数组,具体实现如下:

//SEVEN SHAPE OF RECTANGLE
int shape_coordinate[SHAPE_COUNT][BLOCK_COUNT][2] = 
{
    {{0,-1},{0,0},{-1,0},{-1,1}},
    {{0,-1},{0,0},{1,0},{1,1}},
    {{0,-1},{0,0},{0,1},{0,2}},
    {{-1,0},{0,0},{1,0},{0,1}},
    {{0,0},{1,0},{0,1},{1,1}},
    {{-1,-1},{0,-1},{0,0},{0,1}},
    {{1,-1},{0,-1},{0,0},{0,1}}
};

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

接下来还需要定义一些辅助变量如记录得分的变量score,如当前方块的最左边的坐标位置等以及相应的绘图变量,具体定义和说明如下:

int score = 0;                         //得分
 
//shape next = 0;
//shape current = 0;
shape next = ZShape;                  //下一个方块
shape current = ZShape;               //当前方块
 
int current_coordinate[4][2] = {0};   //当前方块的每一部分的坐标,初始化为0
int table[ROWS][COLUMS] = {0};        //游戏桌面,初始化为全0,表示桌面上还没有方块
int shapex = 0;                       //当前方块的x坐标
int shapey = 0;                       //当前方块的y坐标
int speed = 0;                        //方块下移的速度
clock_t start = 0;                    //每一帧的开始时间
clock_t finish = 0;                   //每一帧的结束时间
 
//windows paint function
HWND  gameWND;                    //window窗口句柄
HBITMAP memBM;                    //内存位图
HBITMAP memBMOld;                 //内存原始位图
HDC memDC;                        //内存DC
RECT clientRC;                    //客户区矩形区域
HBRUSH blackBrush;                //黑色画笔
HBRUSH stoneBrush;                //深灰色画笔
HBRUSH shapeBrush[SHAPE_COUNT];   //方块画笔,7种方块,每种一个
HPEN grayPen;                     //灰色画笔
HFONT bigFont;                    //大字体,用来显示游戏名称及"GAME OVER"
HFONT smallFont;                  //小字体用来显示帮助信息等

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

目前,该声明的变量都已经声明完毕, 接下来就开始根据相应的功能实现其处理函数

1.取最大坐标

函数名称:maxX

函数功能:取得当前方块的最大x坐标。具体实现如下:

//main functions
//对比当前方块的BLOCK_COUNT个小块
//选择它们最大的x坐标
int maxX()
{
    int i =0;
    int x = current_coordinate[i][0];
    int m = x;
    for(i = 1; i< BLOCK_COUNT; i++)
    {
        x = current_coordinate[i][0];
        if(m < x)
        {
            m = x;
        }
    }
    return m;
}

2.取最小坐标

函数名称:minX

函数功能:取得当前方块的最小x坐标。具体实现如下:

int minX()
{
    int i = 0;
    int x = current_coordinate[i][0];
    int m = x;
    for(i = 1;i<BLOCK_COUNT;i++)
    {
        x = current_coordinate[i][0];
        if(m > x)
        {
            m = x;
        }
    }
    return m;
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }3.逆时针旋转方块

函数名称:turn_left

函数功能:将当前方块逆时针旋转90度。具体实现如下:

//逆时针旋转90度
//旋转公式 x' = y; y' =  -x
void turn_left()
{
    int i = 0;
    int x, y;
    for(i = 0; i < 4; i++)
    {
        x = current_coordinate[i][0];
        y = current_coordinate[i][1];
        current_coordinate[i][0] = y;
        current_coordinate[i][1] = -x;
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

4.顺时针旋转方块

函数名称:turn_right

函数功能:将当前方块顺时针旋转90度。旋转公式:x' = –y , y' = x; 具体实现如下:

//顺时针旋转
void turn_right()
{
    int i = 0;
    int x, y;
    for(i = 0; i< 4; i++)
    {
        x = current_coordinate[i][0];
        y = current_coordinate[i][1];
        current_coordinate[i][0] = -y;
        current_coordinate[i][1] = x;
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

5.检测方块是否越界

函数名称:out_of_table

函数功能:检查当前方块是否超出桌面范围。具体实现如下:

//检测是否越界
int out_of_table()
{
    int i = 0;
    int x , y;
    for(i = 0; i < 4; i++)
    {
        //shapex,shapey的值在生成一个新的方块时设定,它们是方块生成时最左边的初始边界坐标点值
        //current_coordinate[i][]值是相对应shapex,shapey的偏移值
        //所以直接检测shapex + current_coordiante[][]值就是其目前在游戏桌面上的坐标值
        x = shapex + current_coordinate[i][0];
        y = shapey + current_coordinate[i][1];
        //如果x值为负值,则表示游戏方块已经超出了游戏桌面的最左边的表示边界
        //如果x值大于COLUMS-1表示超出了游戏桌面的最右边表示边界
        //如果y值大于游戏桌面最下面的表示边界,则表示超出了游戏边界
        if(x < 0 || x > (COLUMS-1)|| y > (ROWS - 1))
        {
            return 1;
        }
        //table[ROWS][COLUMS]表示为table[y][x]本身是初始化为全0的,如果由值不是0 表示
        //当前方形已经运行到某个方形上面了,则也表示越界
        if(table[y][x])
        {
            return 1;
        }
    }
    //方形没有越界则返回0,否则返回1
    return 0;
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

6.旋转方块

函数名称:transform

函数功能:旋转当前方块。具体实现如下:

//旋转当前方块
void transform()
{
    //如果是田字形的方块则不需要旋转变化
    if(current == SquareShape)
    {
        return ;
    }
    //默认顺时针旋转
    turn_right();
    //如果顺时针旋转出现越界情况则
    //进行逆时针旋转
    if(out_of_table())
    {
        turn_left();
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }7.判断方块能否向左移动

函数名称:leftable

函数功能:判断当前方块能否向左移动,能移动则返回1,否则返回0.具体实现如下:

//判断能否向左移动
int leftable()
{
    int i = 0;
    int x , y;
    for(i = 0; i < 4; i++)
    {
        //shapex,shapey 是初始化生成方形时最左边的x,y坐标值
        //current_coordinate是方形的相对偏移位置
        //x,y为目前方形中某个方块的实际坐标值
        x = shapex + current_coordinate[i][0];
        y = shapey + current_coordinate[i][1];
        //如果发生越界则返回0,否则返回1
        //x <= 0 判断x是否越过左边界
        //判断table[y][x-1] == 1,表示以当前坐标值x,y所在点为基准
        //向左探测一小方块,看这个小方块是否被已有的方形占用了
        //如果占用了则它的值为1,否则为0,如果为1,则表示目前方形已经
        //落到了另一个已经存在的方形上了,发生了重叠,那这也是越界了
        if(x <= 0 || table[y][x-1]  == 1)
        {
            return 0;
        }
    }
    return 1;
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }8.判断方块能否向右移动

函数名称:rightable

函数功能:判断当前方块能否向右移动,能移动则返回1,否则返回0.具体实现如下:

//判断能否向右移动
int rightable()
{
    int i = 0; 
    int x, y ;
    for(i = 0; i < 4; i++)
    {
        x = shapex + current_coordinate[i][0];
        y = shapey + current_coordinate[i][1];
        //x>=(COLUMS -1)判断是否越过右边界
        //判断table[y][x+1]    表示以当前坐标值x,y所在点为基准
        //向右探测一小方块,看这个小方块是否被已有的方形占用了
        //如果占用了则它的值为1,否则为0,如果为1,则表示目前方形已经
        //落到了另一个已经存在的方形上了,发生了重叠,那这也是越界了
        if(x >= (COLUMS -1) || table[y][x+1] == 1)
        {
            return 0;
        }
    }
    return 1;
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

9.判断方块能否向下移动

函数名称:downable

函数功能:判断当前方块能否向下移动,能移动则返回1,否则返回0.具体实现如下:

int downable()
{
    int i = 0;
    int x , y;
    for(i = 0; i<4;i++)
    {
        x = shapex + current_coordinate[i][0];
        y = shapey + current_coordinate[i][1];
        if(y >= (ROWS -1) || table[y+1][x] == 1)
        {
            return 0;
        }
    }
    return 1;
}

10.向左或右移动当前方块

函数名称:move_left /move_right

函数功能:向左/右移动当前方块。具体实现如下:

void move_left()
{
    if(leftable())
    {
        //shapex是方块最左边的坐标位置点x的值,--表示
        //当前方形整体左移一个单位
        shapex--;
    }
}
 
void move_right()
{
    if(rightable())
    {
        //shapex是方块最左边的坐标位置点x的值,++表示
        //当前方形整体右移一个单位
        shapex++;
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }11.向下移动当前方块

函数名称:move_down

函数功能:向下移动当前方块。具体实现如下:

void move_down()
{
    //如果可以向下移动,则
    //shapey++,表示当前方形整体下移一个单位
    if(downable())
    {
        shapey++;
    }else{ //如果不能往下移动则继续判断
        //如果是可以添加到当前游戏桌面上,则自己添加
        if(add_to_table())
        {  //添加完毕之后调用remove_full()函数来检测是否
            //有满行的,如果有则清除掉
            //然后继续生成下一个方形,将其作为当前方形
            remove_full();
            next_shape();
        }else{
            //如果既不能往下移动,也不能把他添加到游戏桌面上
            //则表示越界,那就结束游戏
            state = game_over;
        }
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }12.将当前方块固定到桌面上

函数名称:add_to_table

函数功能:将当前方块固定到桌面上,若返回0,则表示游戏结束。具体实现如下:

//将当前方块添加到桌面上,若返回0则表示游戏结束,否则添加成功
int add_to_table()
{
    int i = 0; 
    int x ,y;
    for(i = 0; i< 4; i++)
    {
        //获取当前方形其中的小方块的具体坐标点值
        x = shapex + current_coordinate[i][0];
        y = shapey + current_coordinate[i][1];
        //判断是否越界或已经有其它方块存在,如果没有则置1表示添加成功
        if(y < 0 || table[y][x] == 1)
        {
            return 0;
        }
        table[y][x] = 1; 
    }
 
    return 1;
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }13.删除填满的行

函数名称:remove_full

函数功能:删除桌面上填满的行。具体实现如下:

//删除桌面上填满的行
void remove_full()
{
    int c = 0;
    int i,j;
    //首先定位到最底下那一行
    for(i = ROWS -1; i>0;i--)
    {
        c = 0; 
        //然后将当前行的所有值相加
        for(j = 0; j < COLUMS;j++)
        {
            c += table[i][j];
        }
        //如果所有值相加的结果等于列数目,则表示当前行是满行
        if(c == COLUMS)
        {
            //void *memmove( void* dest, void* src,count );
            //memmove是从src所指内存区域复制count个字节到dest所指内存区域
            //memmove有个特性,如果目标区域dest和源区域src有重叠的话,memmove
            //能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中
           //这里我们正是利用了memmove的这个特性完成满行的删除工具和删除后其上面的未满行
            //向下移动的工作
            memmove(table[1],table[0],sizeof(int)*COLUMS*i);
            //将table[0]清空
            memset(table[0],0,sizeof(int)*COLUMS);
            score++;//分数加1
            speed = (score /100)%MAX_SPEED; //变速
            i++;
        }
        else if(c == 0)
        {
            break;
        }
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

删除满行中利用了<windows.h>中的库函数memmove保证叠加区域复制正确的特性,例如

#define ROWS 5

#define COLUMS 2

int table[ROWS][COLUMS] = {

{0,0},

{1,1},

{0,1},

{1,1},

{1,0}

};

利用memmove(table[1],table[0],sizeof(int)*COLUMS*i)它完成的功能是,是从table[0]地址处开始复制COLUMS*i个int字节个数据将其放到以table[1]地址开始的地方,具体操作如图:

memset(table[0],0,sizeof(int)*COLUMS); 即把table[0]那一行清空,因为它是最上面那一行,所以只要有满行被删除,那么最上面那一行永远都应该是空的。

14.创建新游戏

函数名称:new_game

函数功能:创建一个新游戏。具体实现如下:

//创建新游戏
void new_game()
{
    //将桌面表格全部置零,清空桌面上残余的方形
    memset(table,0,sizeof(int)*COLUMS*ROWS);
    start = clock(); //初始化时钟
    next = (shape)random(SHAPE_COUNT); //初始化下一个方形
    score = 0; //得分初始化为0
    speed = 0; //速度初始化为0
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }15.运行游戏

函数名称:run_game

函数功能:运行游戏。具体实现如下:

//运行游戏
void run_game()
{
    finish = clock();
    if((finish - start) > (MAX_SPEED-speed)*100)//设定方形跳动的时间间隔
    {
        move_down(); //向下移动
        start = clock();//重新记录下一次跳动的开始时间
        InvalidateRect(gameWND,NULL,TRUE);//刷新整个客户区,重新跳动之后的图形
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }16.操作当前方块

函数名称:next_shape

函数功能:将下一个方形设为当前的方块,并随机生成下一个方块。具体实现如下:

void next_shape()
{
    current = next;
    //将下一个方形设置为当前方形
    memcpy(current_coordinate,shape_coordinate[next],sizeof(int)*BLOCK_COUNT*2);
    //shapex的值为方块放在游戏桌面正中央位置时其最左边的x值
    //shapey值为0
    shapex = (COLUMS - ((maxX()-minX())))/2;
    shapey = 0;
    //随机生成下一个方块
    next = (shape)random(SHAPE_COUNT);
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }17.取随机数

函数名称:random

函数功能:取得一个随机数,例如,random(7)将返回一个0-6之间的随机数。具体实现如下:

int random(int seed)
{
    if( 0 == seed)
    {
        return 0;
    }
    srand((unsigned)time(NULL));//以当前时间作为srand的seed
    //srand()函数就是给rand()提供种子seed。如果srand()中的形参每次都是同一个值
    //那么每次运行rand()产生的随机数也是一样的, 所以为了rand()每次产生不一样的随机数
    //通常把当前时间作为srand()的参数,这样就可以得到不一样的srand()参数,从而rand()也
    //就产生不一样的种子
    return (rand() % seed);
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }18.绘图

函数名称:paint

函数功能:将内存位图输出到窗口上。具体实现如下:

//paint func
void paint()
{
    PAINTSTRUCT ps;
    HDC hdc;
    draw_table();
    /*
    *BeginPaint函数为指定窗口做绘图工作前的相关准备工作,将绘图有关的信息填充到
    *一个PAINTSTRUCT结构中。原型如下:
    *   HDC BeginPaint(
    *      HWND hwnd, //[输入]被重绘的窗口句柄
    *      LPPAINTSTRUCT lpPaint  //[输出]指向一个用来接收绘图信息的PAINTSTRUCT结构
    *      )
    *   返回值: 如果函数成功,则返回值是指定窗口的"显示设备描述表"句柄。
    *            否则返回值为NULL,表明没有得到显示设备的内容。
    *   [注]
    *   BeginPaint函数自动设置显示设备内容的剪切区域,而排除任何更新区域外的区域。该
    * 更新区域可以通过 InvalidateRect 或  InvalidateRgn 函数来设置,也可以是系统在改
    * 变大小,移动,创建,滚动后设置的。如果更新区域被标记为可擦除的,BeginPaint将发
    * 生一个WM_ERASEBKGND消息给窗口。如果窗口类有一个自己的背景刷,那么BeginPaint将
    * 使用这个刷子来擦除更新区域的背景。
    * BeginPaint与EndPaint只能配对使用,不能单独使用,BeginPaint返回一个用来绘图的客
    * 户区的显示设备内容的HANDLE, 而EndPaint则终止绘画请求,并释放设备内容。
    * 需要注意的是BeginPaint和 EndPaint只能在响应WM_PAINT消息时使用,而且只能调用一次
    * 
    * 如果被绘画的客户区中有一个caret(caret:插入符。是窗口客户区中的一个闪烁的线,块,
    * 或位图。插入符通常表示文本或图形将被插入的地方。即一闪一闪的光标),BeginPaint将
    * 自动隐藏该符号,而保证它不被擦除。
    */
    hdc = BeginPaint(gameWND,&ps);
    //BOOL BitBlt(HDC hdcDest, int nXDest,int nYDest, int nWidth,int nHeight, HDC hdcSrc
    //          ,int nXSrc, int nYSrc,DWORD dwRop); 如果dwRop 是SRCCOPY 则表示将源hdcSrc
    //内存位图原样拷贝到目标hdcDest处。
    BitBlt(hdc,clientRC.left,clientRC.top,clientRC.right,clientRC.bottom,memDC,0,0,SRCCOPY);
    EndPaint(gameWND,&ps);
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }19.绘制游戏桌面

函数名称:draw_table

函数功能:绘制游戏桌面

处理流程:首先用黑色矩形填充桌面背景区,接着判断游戏的状态,如果是开始状态,用黄色字显示游戏开始画面,如果是结束状态,用红色字显示 GAME OVER ; 如果是游戏运行状态,则依次绘制游戏桌面,当前方块,下一个方块,得分和游戏帮助等。具体实现如下:

//绘制游戏桌面
void draw_table()
{
    HBRUSH hBrushOld;
    HPEN hPenOld;
    HFONT hFontOld;
    RECT rc;
    int x0,y0,w;
    int x,y,i,j;
    char* str;
 
    w = clientRC.bottom /(ROWS + 2); //设置一个方块的宽度
    x0 = y0 = w;
    //用黑色矩形填充桌面背景区
    FillRect(memDC,&clientRC,blackBrush);
    if(state == game_start || state == game_over)
    {
        // 绘制游戏开始或结束的标题界面
        memcpy(&rc,&clientRC,sizeof(RECT));
        rc.bottom = rc.bottom / 2;
        hFontOld = (HFONT)SelectObject(memDC,bigFont);
        SetBkColor(memDC,BLACK);
        //如果游戏是开始状态,则用黄色字显示游戏开始界面
        if(state == game_start)
        {
            str = APP_TITLE;
            SetTextColor(memDC,YELLOW);
        }else{ //如果游戏是结束状态,则用红色字显示 GAME OVER 
            str = GAMEOVER;
            SetTextColor(memDC,RED);
        }
 
        DrawText(memDC,str,strlen(str),&rc,DT_SINGLELINE | DT_CENTER| DT_BOTTOM);
        SelectObject(memDC,hFontOld);
 
        //提示信息
        hFontOld = (HFONT)SelectObject(memDC,smallFont);
        rc.top = rc.bottom;
        rc.bottom = rc.bottom*2;
        if(state == game_over)
        {
            SetTextColor(memDC,YELLOW);
            sprintf(score_char,SCORE,score);
            DrawText(memDC,score_char,strlen(score_char),&rc,DT_SINGLELINE|DT_CENTER|DT_TOP);
        }
 
        SetTextColor(memDC,STONE);
        DrawText(memDC,press_enter,strlen(press_enter),&rc,DT_SINGLELINE|DT_CENTER|DT_VCENTER);
        SelectObject(memDC,hFontOld);
 
        return;
    }
 
    //画桌面上残留的方块
    hBrushOld = (HBRUSH)SelectObject(memDC,stoneBrush);
    for(i = 0; i < ROWS; i++)
    {
        for(j = 0; j < COLUMS; j++)
        {
            if(table[i][j] == 1)
            {
                x = x0 + j*w;
                y = y0 + i*w;
                Rectangle(memDC,x,y,x+w+1,y+w+1);
            }
        }
    }
 
    SelectObject(memDC,hBrushOld);
 
    //画当前的方块
    hBrushOld = (HBRUSH)SelectObject(memDC,shapeBrush[current]);
    for(i = 0; i < 4; i++)
    {    //取得当前方形中某一单元在桌面上的实际坐标值
        x = x0 + (current_coordinate[i][0] + shapex)*w;
        y = y0 + (current_coordinate[i][1] + shapey)*w;
        if(x < x0 || y < y0)
        {
            continue;
        }
        //绘制实心矩形
        Rectangle(memDC,x,y,x+w+1,y+w+1);
    }
    SelectObject(memDC,hBrushOld);
    //画桌面上的表格线条
    hPenOld = (HPEN)SelectObject(memDC,grayPen);
    for(i = 0; i<= ROWS; i++)
    {
        /*MoveToEx(memDC,x0+i*w,y0,NULL);*/
        MoveToEx(memDC,x0,y0+i*w,NULL);
        LineTo(memDC,x0+COLUMS*w,y0+i*w);
    }
    for(i = 0; i <= COLUMS; i++)
    {
        MoveToEx(memDC,x0+i*w,y0,NULL);
        LineTo(memDC,x0+i*w,y0+ROWS*w);
    }
    SelectObject(memDC,hPenOld);
 
    //画玩家得分
    x0= x0+COLUMS*w +3*w;//设置得分字样在桌面上的偏移位置
    y0=y0+w;
    hFontOld = (HFONT)SelectObject(memDC,smallFont);//选择字体
    SetTextColor(memDC,YELLOW);//设置字体颜色
    sprintf(score_char,SCORE,score);
    /*sprintf(score_char,SCORE,shapex);*/
    /*sprintf(score_char,SCORE,table[29][0]);*/
    TextOut(memDC,x0,y0,score_char,strlen(score_char));//绘制得分
    //画下一个方块
    y0 += w;
    SetTextColor(memDC,STONE);
    TextOut(memDC,x0,y0,"NEXT",4);
    x0 = x0 + w;
    y0 += 2*w;
    hBrushOld = (HBRUSH)SelectObject(memDC,shapeBrush[next]);
    for(i = 0; i < 4; i++)
    {
        x = x0 + shape_coordinate[next][i][0]*w;
        y = y0 + shape_coordinate[next][i][1]*w;
        Rectangle(memDC,x,y,x+w+1,y+w+1);
    }
 
    SelectObject(memDC,hBrushOld);
    //打印帮助信息
    x0 = (COLUMS + 2)*w;
    y0 += 4*w;
    SetTextColor(memDC,GRAY);
    i = 0;
    while(help[i])
    {
        TextOut(memDC,x0,y0,help[i],strlen(help[i]));
        y0 += w;
        i++;
    }
    SelectObject(memDC,hFontOld);
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

20.处理按键

函数名称:key_down

函数功能:处理键盘按下事件。

处理流程:1.如果游戏状态不是运行状态,按回车键是进行暂停/开始游戏的切换键。2.游戏运行状态,按向上键或空格键旋转当前方块,按向左键左移当前方块,按向右键右移当前方块,按向下键下移当前方块。按回车键,来回切换暂停/开始游戏。具体实现如下:

//按键事件处理
void key_down(WPARAM wParam)
{
    //如果游戏状态处理非运行状态,按下回车键,则开始游戏
    if(state != game_run)
    {
        if(wParam == VK_RETURN)
        {
            switch(state)
            {
            case game_start:
                next_shape();
                state = game_run;
                break;
            case game_pause:
                state = game_run;
                break;
            case game_over:
                new_game();
                next_shape();
                state = game_run;
                break;
            }
        }
    }
    else //在游戏运行状态下,响应键盘事件
    {
        switch(wParam)
        {
        case VK_SPACE:  //空格键/向上键则旋转当前方块
        case VK_UP:
            transform();
            break;
        case VK_LEFT:  //左向键左移当前方块
            move_left();
            break;
        case VK_RIGHT: //右向键右移当前方块
            move_right();
            break;
        case VK_DOWN: //下向键下移当前方块
            move_down();
            break;
        case VK_RETURN://回车键暂停当前游戏
            state = game_pause;
            break;
        }
    }
    //重绘客户区
    InvalidateRect(gameWND,NULL,TRUE);
}

21.改变窗口大小

函数名称:resize

函数功能:改变窗口大小时调用的函数。具体实现如下:

void resize()
{
    HDC hdc;
    LOGFONT lf;
    hdc = GetDC(gameWND);
    GetClientRect(gameWND,&clientRC);
    SelectObject(memDC,memBMOld);
    DeleteObject(memBM);
    //重新创建合适的内存位图
    memBM = CreateCompatibleBitmap(hdc,clientRC.right,clientRC.bottom);
    memBMOld = (HBITMAP)SelectObject(memDC,memBM);
    //重新创建合适的大字体和小字体
    DeleteObject(bigFont);
    memset(&lf,0,sizeof(LOGFONT));
    lf.lfWidth = (clientRC.right - clientRC.left) / CHARS_IN_LINE;
    lf.lfHeight = (clientRC.bottom - clientRC.top) /4;
    lf.lfItalic = 1;
    lf.lfWeight = FW_BOLD;
    bigFont = CreateFontIndirect(&lf);
 
    DeleteObject(smallFont);
    lf.lfHeight = clientRC.bottom / (ROWS + 2);
    lf.lfWidth = lf.lfHeight / 2;
    lf.lfItalic = 0;
    lf.lfWeight = FW_NORMAL;
    smallFont = CreateFontIndirect(&lf);
 
    ReleaseDC(gameWND, hdc);
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }22.处理消息

函数名称:WndProc

函数功能:回调函数,用来处理Windows消息。具体实现如下:

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
    case WM_SIZE: //响应改变窗口大小的消息
        resize();
        return 0;
    case WM_ERASEBKGND: //响应重绘背景的消息
        return 0;
    case WM_PAINT: //相应绘制消息
        paint();
        return 0;
    case WM_KEYDOWN: //相应按键消息
        key_down(wParam);
        return 0;
    case WM_DESTROY: //响应销毁窗口的消息
        PostQuitMessage(0);
        return 0;
    }
    //其它消息用Windows默认的消息处理函数处理
    return DefWindowProc(hwnd, message,wParam,lParam);
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }23.初始化

函数名称:initialize

函数功能:初始化内存位图,画笔,字体等资源。具体实现如下:

void initialize()
{
    LOGFONT lf;
    HDC hdc;
    int i;
 
    hdc = GetDC(gameWND);
    GetClientRect(gameWND,&clientRC); //取得窗口客户区大小
    memDC = CreateCompatibleDC(hdc);  //创建内存DC
    //创建内存位图
    memBM = CreateCompatibleBitmap(hdc, clientRC.right,clientRC.bottom);
    //将内存位图选入到内存DC中
    memBMOld = (HBITMAP)SelectObject(memDC,memBM);
    //创建黑色画笔和深灰色画笔
    blackBrush = CreateSolidBrush(BLACK);
    stoneBrush = CreateSolidBrush(STONE);
    //创建每个方块所对应颜色的画笔
    for(i = 0; i < SHAPE_COUNT;i++)
    {
        shapeBrush[i] = CreateSolidBrush(shape_color[i]);
    }
    grayPen = CreatePen(PS_SOLID,1,GRAY);//创建灰色画笔
    memset(&lf,0,sizeof(LOGFONT));
    //创建大字体
    lf.lfWidth = (clientRC.right - clientRC.left) / CHARS_IN_LINE;
    lf.lfHeight = (clientRC.bottom - clientRC.top) /4;
    lf.lfItalic = 1;
    lf.lfWeight = FW_BOLD;
    bigFont = CreateFontIndirect(&lf);
    //创建小字体
    lf.lfHeight = clientRC.bottom / (ROWS + 2);
    lf.lfWidth = lf.lfHeight / 2;
    lf.lfItalic = 0;
    lf.lfWeight = FW_NORMAL;
    smallFont = CreateFontIndirect(&lf);
    
    ReleaseDC(gameWND,hdc);
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }24.释放资源

函数名称:finalize

函数功能:游戏结束时调用该函数释放initialize中创建的资源。具体实现如下:

void finalize()
{
    int i = 0;
    DeleteObject(blackBrush);
    DeleteObject(stoneBrush);
    for(i = 0; i < SHAPE_COUNT; i++)
    {
        DeleteObject(shapeBrush[i]);
    }
 
    DeleteObject(grayPen);
    DeleteObject(bigFont);
    DeleteObject(smallFont);
    SelectObject(memDC,memBMOld);
    DeleteObject(memBM);
    DeleteDC(memDC);
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }25.系统入口函数

函数名称:WinMain

函数功能:Windows程序入口,类似于DOS程序的main函数。具体实现如下:

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance,PSTR szCmdLine,int iCmdShow)
{
    MSG msg; //声明一个消息结构体变量
    WNDCLASS wndclass; //声明一个窗口类变量
 
    //初始化窗口信息
    wndclass.style  = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = WndProc;
    wndclass.cbClsExtra = wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInstance;
    wndclass.hIcon = LoadIcon(NULL,IDI_APPLICATION);
    wndclass.hCursor = LoadCursor(NULL,IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wndclass.lpszMenuName = NULL;
    wndclass.lpszClassName = APP_NAME;
    //注册窗口类
    RegisterClass(&wndclass);
    //创建窗口
    gameWND = CreateWindow(APP_NAME,
        APP_TITLE,
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,NULL,
        hInstance,NULL);
    //初始化游戏基本资源
    initialize();
    //显示窗口
    ShowWindow(gameWND, iCmdShow);
    UpdateWindow(gameWND); //刷新窗口
    //创建游戏
    new_game();
    for(;;)//进入消息循环
    {
        if(state == game_run)
        {
            run_game();
        }
 
        if(PeekMessage(&msg,NULL,0,0,PM_NOREMOVE))
        {
            if(GetMessage(&msg,NULL,0,0))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }else{
                break;
            }
        }
    }
 
    finalize();
 
    return msg.wParam;
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }至此游戏的基本功能已经实现。

5.系统操作过程

F5游戏运行后,首先进入欢迎主界面,如图:

在欢迎主界面中按任意键进入游戏,游戏界面如图:

游戏结束界面如图:

6.总结与Bug记录

Bug.1

//画桌面上的表格线条

    hPenOld = (HPEN)SelectObject(memDC,grayPen);

    for(i = 0; i<= ROWS; i++)

    {

        /*MoveToEx(memDC,x0+i*w,y0,NULL);*/

        MoveToEx(memDC,x0,y0+i*w,NULL);

        LineTo(memDC,x0+COLUMS*w,y0+i*w);

    }

Bug.2

显示出了SCORE字样但是没有显示得分情况,

将#define SCORE "SCORE  %4"改成:#define SCORE "SCORE  %4d"

Bug.3

方块旋转有问题

void turn_right()

{

    int i = 0;

    int x, y;

    for(i = 0; i< 4; i++)

    {

        x = current_coordinate[i][0];

        y = current_coordinate[i][1];

        current_coordinate[i][0] = -y; //没有写

        current_coordinate[i][1] = x;

    }

}

C项目实践--俄罗斯方块(2)的更多相关文章

  1. C项目实践--俄罗斯方块(1)

    俄罗斯方块游戏是由前苏联科学院计算机中心的工程师阿列克谢.帕基特诺夫发明的一款小游戏. 1.功能需求分析 1.1主要功能 实现三个功能:1.游戏欢迎界面:2.游戏执行功能,包括计算得分:3.游戏结束界 ...

  2. Hangfire项目实践分享

    Hangfire项目实践分享 目录 Hangfire项目实践分享 目录 什么是Hangfire Hangfire基础 基于队列的任务处理(Fire-and-forget jobs) 延迟任务执行(De ...

  3. Windows on Device 项目实践 3 - 火焰报警器制作

    在前两篇<Windows on Device 项目实践 1 - PWM调光灯制作>和<Windows on Device 项目实践 2 - 感光灯制作>中,我们学习了如何利用I ...

  4. Windows on Device 项目实践 2 - 感光灯制作

    在上一篇<Windows on Device 项目实践 1 - PWM调光灯制作>中,我们学习了如何利用Intel Galileo开发板和Windows on Device来设计并完成一个 ...

  5. Windows on Device 项目实践 1 - PWM调光灯制作

    在前一篇文章<Wintel物联网平台-Windows IoT新手入门指南>中,我们讲解了Windows on Device硬件准备和软件开发环境的搭建,以及Hello Blinky项目的演 ...

  6. Hangfire项目实践

    Hangfire项目实践分享 Hangfire项目实践分享 目录 Hangfire项目实践分享 目录 什么是Hangfire Hangfire基础 基于队列的任务处理(Fire-and-forget ...

  7. MVC项目实践,在三层架构下实现SportsStore,从类图看三层架构

    在"MVC项目实践,在三层架构下实现SportsStore-02,DbSession层.BLL层"一文的评论中,博友浪花一朵朵建议用类图来理解本项目的三层架构.于是就有了本篇: I ...

  8. MVC项目实践,在三层架构下实现SportsStore-02,DbSession层、BLL层

    SportsStore是<精通ASP.NET MVC3框架(第三版)>中演示的MVC项目,在该项目中涵盖了MVC的众多方面,包括:使用DI容器.URL优化.导航.分页.购物车.订单.产品管 ...

  9. MVC项目实践,在三层架构下实现SportsStore-01,EF Code First建模、DAL层等

    SportsStore是<精通ASP.NET MVC3框架(第三版)>中演示的MVC项目,在该项目中涵盖了MVC的众多方面,包括:使用DI容器.URL优化.导航.分页.购物车.订单.产品管 ...

随机推荐

  1. 在html借助元素特性存储信息

    背景:比如存在学生选择的CheckBox,希望在CheckBox中同时存储学生的姓名及其所在的城市,比如选择Lily所对应的CheckBox以后,可以获得Lily所在的城市“NewYork”. htm ...

  2. 【06】sass编译工具(弃)

    [06]编译工具(弃) SASS转译工具 除了使用sass命令来转译SASS文件之外,还可以借助第三方工具完成,目前世面上较为流行的转译工具主要有: Compass.app Scout Codekit ...

  3. 大数据学习——有两个海量日志文件存储在hdfs

    有两个海量日志文件存储在hdfs上, 其中登陆日志格式:user,ip,time,oper(枚举值:1为上线,2为下线):访问之日格式为:ip,time,url,假设登陆日志中上下线信息完整,切同一上 ...

  4. 【UTR #2】[UOJ#278]题目排列顺序 [UOJ#279]题目交流通道 [UOJ#280]题目难度提升

    [UOJ#278][UTR #2]题目排列顺序 试题描述 “又要出题了.” 宇宙出题中心主任 —— 吉米多出题斯基,坐在办公桌前策划即将到来的 UOI. 这场比赛有 n 道题,吉米多出题斯基需要决定这 ...

  5. [codevs2495]水叮当的舞步

    [codevs2495]水叮当的舞步 试题描述 水叮当得到了一块五颜六色的格子形地毯作为生日礼物,更加特别的是,地毯上格子的颜色还能随着踩踏而改变. 为了讨好她的偶像虹猫,水叮当决定在地毯上跳一支轻盈 ...

  6. 【01背包变形】Robberies HDU 2955

    http://acm.hdu.edu.cn/showproblem.php?pid=2955 [题意] 有一个强盗要去几个银行偷盗,他既想多抢点钱,又想尽量不被抓到.已知各个银行 的金钱数和被抓的概率 ...

  7. asp.net 引发类型为“System.OutOfMemoryException”的异常

    asp.net 引发类型为“System.OutOfMemoryException”的异常通常发生在IIS进程获取不到内存时. 临时解决方法是: 回收IIS的应用程序池. 如果要比较好的解决办法是: ...

  8. vscode安装插件

    十分简单,知道名字叫啥后,直接搜索,安装,就完了,还可以查看自己已经安装了哪些插件. step1 如图.png step2 image.png step 3 去网上查找想要安装的插件的名字 step ...

  9. Codeforces917C. Pollywog

    $n \leq 1e8$个石头,$x \leq 8$个蝌蚪一开始在最左边$x$个石子,要跳到最右的$x$个,每次只能最左边的蝌蚪跳一次,一个石头不能站两个蝌蚪,跳可以跳$1到k,x \leq k \l ...

  10. 汕头市赛srm8 C-3

    n<=100000个点m<=300000条边有权无向联通图,给出K<=10000个特殊点求K个点中任意两点最短路的最小值. 方法一:K小,随便搞.先构造最短路树,在最短路树上Dijk ...