本程序在 VS2003 下调试通过。
建立工程
打开 VS2003 选择 文件->新建->项目->Visual C++ 项目->Win32->Win32 项目;项目名称:Tetris,点击确定。
在弹出的 “Win32 应用程序向导” 对话框中,选择 “应用程序设置” -> “附加选项” -> “空项目”,点击完成。新建 tetris.cpp 文件
选择 “项目” -> “添加新项”,选择 “C++ 文件(.cpp)”,名称:tetris,点击打开。编写 WinMain 入口函数
#include
int WINAPI WinMain(HINSTANCE, HINSTANCE, char *, int cmdShow)
{
}
- 建立程序框架
#include
#include
/////////////////全局变量/////////////////////////////
HWND hwnd; // 窗口句柄
///////////////// 函数 /////////////////////////////
LRESULT CALLBACK WndProc ( HWND,UINT,WPARAM,LPARAM );
int WINAPI WinMain(HINSTANCE, HINSTANCE, char *, int cmdShow)
{
HINSTANCE hInstance=GetModuleHandle ( NULL );
TCHAR szAppName[]=TEXT ( "teris" );
MSG msg;
WNDCLASS wc;
wc.style=CS_HREDRAW|CS_VREDRAW;
wc.lpfnWndProc=WndProc;
wc.cbClsExtra=0;
wc.cbWndExtra=0;
wc.hInstance=hInstance;
wc.hIcon=LoadIcon ( NULL,IDI_APPLICATION );
wc.hCursor=LoadCursor ( NULL,IDC_ARROW );
wc.hbrBackground= ( HBRUSH ) GetStockObject ( WHITE_BRUSH );
wc.lpszMenuName=NULL;
wc.lpszClassName=szAppName;
if ( !RegisterClass ( &wc ) )
{
printf ( "RegisterClass occur errors!" );
return 0;
}
hwnd=CreateWindow ( szAppName,TEXT ( "Teris Demo" ),
WS_OVERLAPPEDWINDOW,
0,0,0,0,
NULL,
NULL,
hInstance,
NULL );
ShowWindow ( hwnd,SW_SHOW );
UpdateWindow ( hwnd );
while ( GetMessage ( &msg,NULL,0,0 ) )
{
TranslateMessage ( &msg );
DispatchMessage ( &msg );
}
return msg.wParam;
}
LRESULT CALLBACK WndProc ( HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam )
{
HDC hdc;
PAINTSTRUCT ps;
switch ( message )
{
case WM_CREATE:
MoveWindow ( hwnd,400,10,800,600,FALSE );
return 0;
case WM_PAINT:
hdc=BeginPaint ( hwnd,&ps );
EndPaint ( hwnd,&ps );
return 0;
case WM_DESTROY:
PostQuitMessage ( 0 );
return 0;
}
return DefWindowProc ( hwnd,message,wParam,lParam );
}
- 绘制面板
#define CELL 20
#define ROWS 25
#define COLS 15
void DrawPanel ( HDC hdc ); //绘制面板
LRESULT CALLBACK WndProc ( HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam )
{
HDC hdc;
PAINTSTRUCT ps;
switch ( message )
{
case WM_CREATE:
MoveWindow ( hwnd,400,10,CELL*COLS+8,CELL*ROWS+32,FALSE ); //补齐宽度和高度
srand ( time ( NULL ) );
ExportBlock();
return 0;
case WM_PAINT:
hdc=BeginPaint ( hwnd,&ps );
DrawPanel ( hdc ); //绘制面板
RefreshPanel ( hdc ); //刷新
EndPaint ( hwnd,&ps );
return 0;
case WM_DESTROY:
PostQuitMessage ( 0 );
return 0;
}
return DefWindowProc ( hwnd,message,wParam,lParam );
}
void DrawPanel ( HDC hdc ) //绘制面板
{
int x,y;
RECT rect;
for ( y=0; y
- 绘制方块
#include
int cur_left,cur_top; //记录方块当前的位置
int width_block,height_block; //方块的宽带和高度
static byte *block=NULL; //方块,方块为随机大小,采用动态分配内存方式,所以这里是指针变量
byte g_panel[ROWS][COLS]={0};
void RefreshPanel ( HDC hdc ); //刷新面板
bool ExportBlock(); //输出方块
void RefreshPanel ( HDC hdc ) //刷新面板
{
int x,y;
RECT rect;
HBRUSH h_bSolid= ( HBRUSH ) GetStockObject ( GRAY_BRUSH ),
h_bEmpty= ( HBRUSH ) GetStockObject ( WHITE_BRUSH );
if ( NULL==block ) return;
//先刷屏
for ( y=0; y
- 变换方块
void DoRedirection ( HDC hdc ); //改变方向
LRESULT CALLBACK WndProc ( HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam )
{
HDC hdc;
PAINTSTRUCT ps;
switch ( message )
{
case WM_CREATE:
MoveWindow ( hwnd,400,10,CELL*COLS+8,CELL*ROWS+32,FALSE ); //补齐宽度和高度
srand ( time ( NULL ) );
ExportBlock();
return 0;
case WM_PAINT:
hdc=BeginPaint ( hwnd,&ps );
DrawPanel ( hdc ); //绘制面板
RefreshPanel ( hdc ); //刷新
EndPaint ( hwnd,&ps );
return 0;
case WM_KEYDOWN:
hdc=GetDC ( hwnd );
switch ( wParam )
{
case VK_UP: //转向
if ( !isPause ) DoRedirection ( hdc );
break;
}
ReleaseDC ( hwnd,hdc );
return 0;
case WM_DESTROY:
PostQuitMessage ( 0 );
return 0;
}
return DefWindowProc ( hwnd,message,wParam,lParam );
}
void DoRedirection ( HDC hdc ) //改变方向
{
int i,j;
byte * temp=NULL;
if ( NULL==block ) return;
if ( cur_top<0 ) return; //方块完整显示前不能转向
temp= ( byte * ) malloc ( sizeof ( byte ) *width_block*height_block );
for ( i=0; i=ROWS||temp_cur_left<0||temp_cur_left+max_len-1>=COLS )
{
free ( temp ); //退出前必须先释放内存
return;
}
for ( i=0; i
- 控制方向
void DoDownShift ( HDC hdc ); //下移
void DoLeftShift ( HDC hdc ); //左移
void DoRightShift ( HDC hdc ); //右移
void DoAccelerate ( HDC hdc ); //加速
LRESULT CALLBACK WndProc ( HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam )
{
HDC hdc;
PAINTSTRUCT ps;
switch ( message )
{
case WM_CREATE:
MoveWindow ( hwnd,400,10,CELL*COLS+8,CELL*ROWS+32,FALSE ); //补齐宽度和高度
srand ( time ( NULL ) );
ExportBlock();
return 0;
case WM_PAINT:
hdc=BeginPaint ( hwnd,&ps );
DrawPanel ( hdc ); //绘制面板
RefreshPanel ( hdc ); //刷新
EndPaint ( hwnd,&ps );
return 0;
case WM_KEYDOWN:
hdc=GetDC ( hwnd );
switch ( wParam )
{
case VK_LEFT: //左移
DoLeftShift ( hdc );
break;
case VK_RIGHT: //右移
DoRightShift ( hdc );
break;
case VK_UP: //转向
DoRedirection ( hdc );
break;
case VK_DOWN: //加速
DoAccelerate ( hdc );
break;
}
ReleaseDC ( hwnd,hdc );
return 0;
case WM_DESTROY:
PostQuitMessage ( 0 );
return 0;
}
return DefWindowProc ( hwnd,message,wParam,lParam );
}
void DoDownShift ( HDC hdc ) //下移
{
if ( NULL==block ) return;
cur_top++;
RefreshPanel ( hdc );
}
void DoLeftShift ( HDC hdc ) //左移
{
int x,y;
if ( NULL==block ) return;
if ( 0==cur_left ) return;
if ( cur_top<0 ) return; //方块没有完整显示前,不能左移
for ( y=0; y=0; x-- ) //从右边开始扫描,获取该行最右边的实心方格块
{
if ( * ( block+y*width_block+x ) )
{
//判断当前方格在面板上右边一个方格是否为实心,是就代表不能再右移
if ( g_panel[cur_top+y][cur_left+x+1] ) return;
break; //只判断最右边的一个实心方格
}
}
}
cur_left++;
RefreshPanel ( hdc );
}
void DoAccelerate ( HDC hdc ) //加速
{
if ( NULL==block ) return;
cur_top++;
RefreshPanel ( hdc );
}
- 让方块往下落
ExportBlock函数里,最后面cur_top = 10;
这句代码去掉。
#define ID_TIMER 1
UINT timer_id=0; //保存计时器ID
int level=0; //级数
int interval_unit=25; //随级数递增的时间间隔增量
int interval_base=300; //时间间隔基量
LRESULT CALLBACK WndProc ( HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam )
{
HDC hdc;
PAINTSTRUCT ps;
switch ( message )
{
case WM_CREATE:
MoveWindow ( hwnd,400,10,CELL*COLS+8,CELL*ROWS+32,FALSE ); //补齐宽度和高度
srand ( time ( NULL ) );
ExportBlock();
timer_id=SetTimer ( hwnd,ID_TIMER,interval_base-level*interval_unit,NULL );
return 0;
case WM_TIMER:
hdc=GetDC ( hwnd );
DoDownShift ( hdc );
ReleaseDC ( hwnd,hdc );
return 0;
...
}
- 让方块触底后停住,并生成新的方块落下
bool IsTouchBottom ( HDC hdc ); //判断是否到达底部
void DoDownShift ( HDC hdc ) //下移
{
if ( NULL==block ) return;
//判断是否到达底部
if ( IsTouchBottom ( hdc ) ) //到底部
{
//消行处理
ClearRow ( hdc );
ExportBlock(); //输出下一个方块
}
cur_top++;
RefreshPanel ( hdc );
}
void DoAccelerate ( HDC hdc ) //加速
{
if ( NULL==block ) return;
if ( IsTouchBottom ( hdc ) )
{
//消行处理
ClearRow ( hdc );
ExportBlock();
}
cur_top++;
RefreshPanel ( hdc );
}
bool IsTouchBottom ( HDC hdc )
{
int x,y;
int i,j;
if ( NULL==block ) return false;
if ( ROWS==cur_top+height_block )
{
//固定方块
for ( i=0; i=0; y-- ) //从底行开始扫描
{
//判断第一个实心方块在面板上邻接的下方方格是否为实心,是就代表已经到达底部
for ( x=0; x
- 消除方块,累积积分
#define SCORE_LEVEL_INC 80 //升级所需分数值
int score=0; //分数
void ClearRow ( HDC hdc ); //消行
void DoDownShift ( HDC hdc ) //下移
{
if ( NULL==block ) return;
//判断是否到达底部
if ( IsTouchBottom ( hdc ) ) //到底部
{
//消行处理
ClearRow ( hdc );
ExportBlock(); //输出下一个方块
}
cur_top++;
RefreshPanel ( hdc );
}
void DoAccelerate ( HDC hdc ) //加速
{
if ( NULL==block ) return;
if ( IsTouchBottom ( hdc ) )
{
//消行处理
ClearRow ( hdc );
ExportBlock();
}
cur_top++;
RefreshPanel ( hdc );
}
void ClearRow ( HDC hdc ) //消行
{
int i,j,k;
int count=0; //消行次数
bool isFilled;
//消行处理
for ( i=ROWS-1; i>=0; i-- )
{
isFilled=true;
for ( j=0; j=0; k-- )
{
for ( j=0; j=10*SCORE_LEVEL_INC-1 ) return;
//加分规则:消除行数,1行加10分,2行加15分,3行加20分,4行加30分
switch ( count )
{
case 1:
score+=10;
break;
case 2:
score+=15;
break;
case 3:
score+=20;
break;
case 4:
score+=30;
break;
}
int temp_level=score/SCORE_LEVEL_INC;
if ( temp_level>level )
{
level=temp_level;
//撤销当前计时器,然后重设
if ( timer_id ) KillTimer ( hwnd,ID_TIMER );
timer_id=SetTimer ( hwnd,ID_TIMER,interval_base-level*interval_unit,NULL );
}
}