本程序在 VS2003 下調(diào)試通過(guò)晾匠。
建立工程
打開(kāi) VS2003 選擇 文件->新建->項(xiàng)目->Visual C++ 項(xiàng)目->Win32->Win32 項(xiàng)目珍促;項(xiàng)目名稱:Tetris提岔,點(diǎn)擊確定封救。
在彈出的 “Win32 應(yīng)用程序向?qū)А?對(duì)話框中找默,選擇 “應(yīng)用程序設(shè)置” -> “附加選項(xiàng)” -> “空項(xiàng)目”,點(diǎn)擊完成留储。新建 tetris.cpp 文件
選擇 “項(xiàng)目” -> “添加新項(xiàng)”,選擇 “C++ 文件(.cpp)”咙轩,名稱:tetris获讳,點(diǎn)擊打開(kāi)。編寫 WinMain 入口函數(shù)
#include <windows.h>
int WINAPI WinMain(HINSTANCE, HINSTANCE, char *, int cmdShow)
{
}
- 建立程序框架
#include <windows.h>
#include <stdio.h>
/////////////////全局變量/////////////////////////////
HWND hwnd; // 窗口句柄
///////////////// 函數(shù) /////////////////////////////
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 ); //補(bǔ)齊寬度和高度
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<ROWS; y++ )
{
for ( x=0; x<COLS; x++ )
{
//計(jì)算方塊的邊框范圍
rect.top=y*CELL+1;
rect.bottom= ( y+1 ) *CELL-1;
rect.left=x*CELL+1;
rect.right= ( x+1 ) *CELL-1;
FrameRect ( hdc,&rect, ( HBRUSH ) GetStockObject ( BLACK_BRUSH ) );
}
}
}
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 );
DrawPanel ( hdc ); //繪制面板
EndPaint ( hwnd,&ps );
return 0;
case WM_DESTROY:
PostQuitMessage ( 0 );
return 0;
}
return DefWindowProc ( hwnd,message,wParam,lParam );
}
- 繪制方塊
#include <time.h>
int cur_left,cur_top; //記錄方塊當(dāng)前的位置
int width_block,height_block; //方塊的寬帶和高度
static byte *block=NULL; //方塊活喊,方塊為隨機(jī)大小丐膝,采用動(dòng)態(tài)分配內(nèi)存方式,所以這里是指針變量
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<ROWS; y++ )
{
for ( x=0; x<COLS; x++ )
{
//為避免刷掉方塊的邊框钾菊,rect范圍必須比邊框范圍小1
rect.top=y*CELL+2;
rect.bottom= ( y+1 ) *CELL-2;
rect.left=x*CELL+2;
rect.right= ( x+1 ) *CELL-2;
if ( g_panel[y][x] )
FillRect ( hdc,&rect,h_bSolid );
else
FillRect ( hdc,&rect,h_bEmpty );
}
}
//再定位方塊
for ( y=0; y<height_block; y++ )
{
for ( x=0; x<width_block; x++ )
{
if ( * ( block+y*width_block+x ) ) //實(shí)心
{
rect.top= ( y+cur_top ) *CELL+2;
rect.bottom= ( y+cur_top+1 ) *CELL-2;
rect.left= ( x+cur_left ) *CELL+2;
rect.right= ( x+cur_left+1 ) *CELL-2;
FillRect ( hdc,&rect,h_bSolid );
}
}
}
}
bool ExportBlock() //輸出方塊
{
int sel;
if ( block )
{
free ( block ); //釋放之前分配的內(nèi)存
block=NULL;
}
sel=rand() %7;
switch ( sel )
{
case 0: //水平條
width_block=4;
height_block=1;
block= ( byte * ) malloc ( sizeof ( byte ) *width_block*height_block );
* ( block+0 ) =1; //可以理解為*(block+0*width_block+0)=1帅矗,即第一行的第一個(gè)方格,下面同理
* ( block+1 ) =1; //*(block+0*width_block+1)=1
* ( block+2 ) =1; //*(block+0*width_block+2)=1
* ( block+3 ) =1; //*(block+0*width_block+3)=1
cur_top=0-height_block;
cur_left= ( COLS-width_block ) /2;
break;
case 1: //三角
width_block=3;
height_block=2;
block= ( byte * ) malloc ( sizeof ( byte ) *width_block*height_block );
* ( block+0 ) =0; //可以理解為*(block+0*width_block+0)=0煞烫,即第一行的第一個(gè)方格浑此,下面同理
* ( block+1 ) =1; //*(block+0*width_block+1)=1
* ( block+2 ) =0; //*(block+0*width_block+2)=0
* ( block+3 ) =1; //*(block+1*width_block+0)=1,第二行開(kāi)始
* ( block+4 ) =1; //*(block+1*width_block+1)=1
* ( block+5 ) =1; //*(block+1*width_block+2)=1
cur_top=0-height_block;
cur_left= ( COLS-width_block ) /2;
break;
case 2: //左橫折
width_block=3;
height_block=2;
block= ( byte * ) malloc ( sizeof ( byte ) *width_block*height_block );
* ( block+0 ) =1; //可以理解為*(block+0*width_block+0)=1滞详,下面同理
* ( block+1 ) =0; //*(block+0*width_block+1)=0
* ( block+2 ) =0; //*(block+0*width_block+2)=0
* ( block+3 ) =1; //*(block+1*width_block+0)=1
* ( block+4 ) =1; //*(block+1*width_block+1)=1
* ( block+5 ) =1; //*(block+1*width_block+2)=1
cur_top=0-height_block;
cur_left= ( COLS-width_block ) /2;
break;
case 3: //右橫折
width_block=3;
height_block=2;
block= ( byte * ) malloc ( sizeof ( byte ) *width_block*height_block );
* ( block+0 ) =0; //可以理解為*(block+0*width_block+0)=0凛俱,下面同理
* ( block+1 ) =0; //*(block+0*width_block+1)=0
* ( block+2 ) =1; //*(block+0*width_block+2)=1
* ( block+3 ) =1; //*(block+1*width_block+0)=1
* ( block+4 ) =1; //*(block+1*width_block+1)=1
* ( block+5 ) =1; //*(block+1*width_block+2)=1
cur_top=0-height_block;
cur_left= ( COLS-width_block ) /2;
break;
case 4: //左閃電
width_block=3;
height_block=2;
block= ( byte * ) malloc ( sizeof ( byte ) *width_block*height_block );
* ( block+0 ) =1; //可以理解為*(block+0*width_block+0)=1,下面同理
* ( block+1 ) =1; //*(block+0*width_block+1)=1
* ( block+2 ) =0; //*(block+0*width_block+2)=0
* ( block+3 ) =0; //*(block+1*width_block+0)=0
* ( block+4 ) =1; //*(block+1*width_block+1)=1
* ( block+5 ) =1; //*(block+1*width_block+2)=1
cur_top=0-height_block;
cur_left= ( COLS-width_block ) /2;
break;
case 5: //右閃電
width_block=3;
height_block=2;
block= ( byte * ) malloc ( sizeof ( byte ) *width_block*height_block );
* ( block+0 ) =0; //可以理解為*(block+0*width_block+0)=0料饥,下面同理
* ( block+1 ) =1; //*(block+0*width_block+1)=1
* ( block+2 ) =1; //*(block+0*width_block+2)=1
* ( block+3 ) =1; //*(block+1*width_block+0)=1
* ( block+4 ) =1; //*(block+1*width_block+1)=1
* ( block+5 ) =0; //*(block+1*width_block+2)=0
cur_top=0-height_block;
cur_left= ( COLS-width_block ) /2;
break;
case 6: //石頭
width_block=2;
height_block=2;
block= ( byte * ) malloc ( sizeof ( byte ) *width_block*height_block );
* ( block+0 ) =1; //可以理解為*(block+0*width_block+0)=1蒲犬,下面同理
* ( block+1 ) =1; //*(block+0*width_block+1)=1
* ( block+2 ) =1; //*(block+1*width_block+0)=1
* ( block+3 ) =1; //*(block+1*width_block+1)=1
cur_top=0-height_block;
cur_left= ( COLS-width_block ) /2;
break;
}
cur_top=10;
return block!=NULL;
}
- 變換方塊
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 ); //補(bǔ)齊寬度和高度
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: //轉(zhuǎn)向
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; //方塊完整顯示前不能轉(zhuǎn)向
temp= ( byte * ) malloc ( sizeof ( byte ) *width_block*height_block );
for ( i=0; i<width_block; i++ )
{
for ( j=0; j<height_block; j++ )
{
* ( temp+i*height_block+j ) =* ( block+ ( height_block-j-1 ) *width_block+i );
}
}
//給方塊重新定位
int incHeight=width_block-height_block;
int incWidth=height_block-width_block;
int temp_cur_top=cur_top-incHeight/2;
int temp_cur_left=cur_left-incWidth/2;
//判斷當(dāng)前空間是否足夠讓方塊改變方向
int max_len=max ( width_block,height_block );
//防止下標(biāo)訪問(wèn)越界
if ( temp_cur_top+max_len-1>=ROWS||temp_cur_left<0||temp_cur_left+max_len-1>=COLS )
{
free ( temp ); //退出前必須先釋放內(nèi)存
return;
}
for ( i=0; i<max_len; i++ )
{
for ( j=0; j<max_len; j++ )
{
//轉(zhuǎn)向所需的空間內(nèi)有已被占用的實(shí)心方格存在,即轉(zhuǎn)向失敗
if ( g_panel[temp_cur_top+i][temp_cur_left+j] )
{
free ( temp ); //退出前必須先釋放內(nèi)存
return;
}
}
}
//把臨時(shí)變量的值賦給block岸啡,只能賦值暖哨,而不能賦指針值
for ( i=0; i<height_block; i++ )
{
for ( j=0; j<width_block; j++ )
{
* ( block+i*width_block+j ) =* ( temp+i*width_block+j );
}
}
//全局變量重新被賦值
cur_top=temp_cur_top;
cur_left=temp_cur_left;
//交換
i=width_block;
width_block=height_block;
height_block=i;
free ( temp ); //釋放為臨時(shí)變量分配的內(nèi)存
RefreshPanel ( hdc );
}
- 控制方向
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 ); //補(bǔ)齊寬度和高度
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: //轉(zhuǎn)向
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; //方塊沒(méi)有完整顯示前,不能左移
for ( y=0; y<height_block; y++ )
{
for ( x=0; x<width_block; x++ ) //從左邊開(kāi)始掃描,獲取該行最左邊的實(shí)心方格塊
{
if ( * ( block+y*width_block+x ) )
{
//判斷當(dāng)前方格在面板上面左邊一個(gè)方格是否為實(shí)心篇裁,是就代表不能再左移
if ( g_panel[cur_top+y][cur_left+x-1] ) return;
break; //只判斷最左邊的一個(gè)實(shí)心方格沛慢,之后直接掃描下一行
}
}
}
cur_left--;
RefreshPanel ( hdc );
}
void DoRightShift ( HDC hdc ) //右移
{
int x,y;
if ( NULL==block ) return;
if ( COLS-width_block==cur_left ) return;
if ( cur_top<0 ) return; //方塊完整顯示前不能右移
for ( y=0; y<height_block; y++ )
{
for ( x=width_block-1; x>=0; x-- ) //從右邊開(kāi)始掃描,獲取該行最右邊的實(shí)心方格塊
{
if ( * ( block+y*width_block+x ) )
{
//判斷當(dāng)前方格在面板上右邊一個(gè)方格是否為實(shí)心达布,是就代表不能再右移
if ( g_panel[cur_top+y][cur_left+x+1] ) return;
break; //只判斷最右邊的一個(gè)實(shí)心方格
}
}
}
cur_left++;
RefreshPanel ( hdc );
}
void DoAccelerate ( HDC hdc ) //加速
{
if ( NULL==block ) return;
cur_top++;
RefreshPanel ( hdc );
}
- 讓方塊往下落
ExportBlock函數(shù)里团甲,最后面cur_top = 10;
這句代碼去掉。
#define ID_TIMER 1
UINT timer_id=0; //保存計(jì)時(shí)器ID
int level=0; //級(jí)數(shù)
int interval_unit=25; //隨級(jí)數(shù)遞增的時(shí)間間隔增量
int interval_base=300; //時(shí)間間隔基量
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 ); //補(bǔ)齊寬度和高度
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 ); //判斷是否到達(dá)底部
void DoDownShift ( HDC hdc ) //下移
{
if ( NULL==block ) return;
//判斷是否到達(dá)底部
if ( IsTouchBottom ( hdc ) ) //到底部
{
//消行處理
ClearRow ( hdc );
ExportBlock(); //輸出下一個(gè)方塊
}
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<height_block; i++ )
{
for ( j=0; j<width_block; j++ )
{
if ( * ( block+i*width_block+j ) ) g_panel[cur_top+i][cur_left+j]=1;
}
}
return true;
}
for ( y=height_block-1; y>=0; y-- ) //從底行開(kāi)始掃描
{
//判斷第一個(gè)實(shí)心方塊在面板上鄰接的下方方格是否為實(shí)心躺苦,是就代表已經(jīng)到達(dá)底部
for ( x=0; x<width_block; x++ )
{
if ( * ( block+y*width_block+x ) )
{
if ( cur_top+y+1<0 ) return false;
if ( g_panel[cur_top+y+1][cur_left+x] )
{
//判斷是否gameover
if ( cur_top<=0 )
{
if ( timer_id )
{
KillTimer ( hwnd,ID_TIMER );
timer_id=0;
}
MessageBox ( hwnd,TEXT ( "游戲結(jié)束" ),TEXT ( "MSG" ),MB_OK|MB_ICONEXCLAMATION );
SendMessage ( hwnd,WM_CLOSE,0,0 );
}
//
//固定方塊
for ( i=0; i<height_block; i++ )
{
for ( j=0; j<width_block; j++ )
{
if ( * ( block+i*width_block+j ) ) g_panel[cur_top+i][cur_left+j]=1;
}
}
return true;
}
}
}
}
return false;
}
- 消除方塊,累積積分
#define SCORE_LEVEL_INC 80 //升級(jí)所需分?jǐn)?shù)值
int score=0; //分?jǐn)?shù)
void ClearRow ( HDC hdc ); //消行
void DoDownShift ( HDC hdc ) //下移
{
if ( NULL==block ) return;
//判斷是否到達(dá)底部
if ( IsTouchBottom ( hdc ) ) //到底部
{
//消行處理
ClearRow ( hdc );
ExportBlock(); //輸出下一個(gè)方塊
}
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; //消行次數(shù)
bool isFilled;
//消行處理
for ( i=ROWS-1; i>=0; i-- )
{
isFilled=true;
for ( j=0; j<COLS; j++ )
{
if ( !g_panel[i][j] )
{
isFilled=false;
break;
}
}
if ( isFilled )
{
for ( j=0; j<COLS; j++ )
{
g_panel[i][j]=0;
}
//所有方塊往下移
for ( k=i-1; k>=0; k-- )
{
for ( j=0; j<COLS; j++ )
{
g_panel[k+1][j]=g_panel[k][j];
}
}
i=i+1;
count++;
}
}
//最高級(jí)別為9級(jí)产还,所以分?jǐn)?shù)極限為(9+1)*SCORE_LEVEL_INC-1
if ( score>=10*SCORE_LEVEL_INC-1 ) return;
//加分規(guī)則:消除行數(shù)匹厘,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;
//撤銷當(dāng)前計(jì)時(shí)器脐区,然后重設(shè)
if ( timer_id ) KillTimer ( hwnd,ID_TIMER );
timer_id=SetTimer ( hwnd,ID_TIMER,interval_base-level*interval_unit,NULL );
}
}