俄羅斯方塊程式碼——c++實現
阿新 • • 發佈:2018-12-24
用codeblock實現不了,缺少庫函式,windows的kpi。555
現在還麼解決辦法,不過程式碼挺好的,貼吧大神多。
2017-5-2
#include<windows.h>
#include<time.h>
#include<stdlib.h>
#define W 14 //遊戲區域寬度
#define H 20 //遊戲區域高度
#define W1 6 //右邊狀態列寬度
#define BSIZE 25 //遊戲方格邊長
#define Y1 6 //放置照片的縱座標
#define Y2 12 //分數顯示欄頂端縱座標
#define Y3 15 //等級顯示欄頂端縱座標
#define Y4 21 //幫助欄頂端縱座標
#define Cur_x W/2-1 //遊戲方塊初始狀態左上角橫座標
#define Cur_y 1 //初始狀態左上角縱座標
#define BgColor RGB(0xF5,0xF5,0xDC) //米色
#define FgColor RGB(255,153,204) //粉紅
#define RED RGB(255,0,0)
#define ORANGE RGB(250,128,10)
#define YELLOW RGB(255,255,0)
#define GREEN RGB(0,255,0)
#define CYAN RGB(0,255,255)
#define LIGHT_BLUE RGB(0xA6,0xCA,0xF0) //天藍色
#define PURPLE RGB(255,0,255)
#define MS_NEWBLOCK WM_USER+1 // 訊息ID,產生新的【方塊】
int score=0,level=0,level_step=100; //分數等級以及每等級所需分數的定義及初始化
int top=H-1; //最頂端的縱座標
int x,y; //方塊當前位置的橫座標及縱座標
int cur_boxnum,next_boxnum; //cur_boxnum是當前方塊編號,next_boxnum是下一個方塊編號
struct BOARD
{
int var; //狀態,1代表已被佔用,0代表未被佔用
int color; //顏色
}board[H][W]; //定義遊戲主機板,H行N列
struct BLOCK
{
int a[4][2]; //定義方塊形狀的陣列
int color; //方塊顏色
int next; //下一個方塊的號碼
};
struct BLOCK block[19]=
{ //初始化各個遊戲方塊
{1,1,1,2,1,3,2,3,RED,1},
{0,2,1,2,2,2,0,3,RED,2},
{0,1,1,1,1,2,1,3,RED,3},
{2,1,0,2,1,2,2,2,RED,0},
{1,1,1,2,0,3,1,3,ORANGE,5},
{0,1,0,2,1,2,2,2,ORANGE,6},
{1,1,2,1,1,2,1,3,ORANGE,7},
{0,2,1,2,2,2,2,3,ORANGE,4},
{1,1,0,2,1,2,2,2,YELLOW,9},
{1,1,1,2,2,2,1,3,YELLOW,10},
{0,2,1,2,2,2,1,3,YELLOW,11},
{1,1,0,2,1,2,1,3,YELLOW,8},
{1,1,1,2,2,2,2,3,GREEN,13},
{1,2,2,2,0,3,1,3,GREEN,12},
{2,1,1,2,2,2,1,3,CYAN,15},
{0,2,1,2,1,3,2,3,CYAN,14},
{1,0,1,1,1,2,1,3,LIGHT_BLUE,17},
{0,2,1,2,2,2,3,2,LIGHT_BLUE,16},
{1,1,2,1,1,2,2,2,PURPLE,18},
};
void Paint(HDC hdc,HPEN hpen) //此函式用於初始化介面
{
int i,j;
HPEN hpen1; //定義畫筆,用於繪製分隔線
HBRUSH hbrush=CreateSolidBrush(BgColor); //定義畫刷並賦初值,畫刷顏色採用背景色
hpen1=CreatePen(PS_DASHDOTDOT,3,FgColor); //給畫筆賦初值,顏色為前景色,線寬為3,雙點劃線
SelectObject(hdc,hpen1); //選擇畫筆
MoveToEx(hdc,W*BSIZE,0,NULL); //將游標移動到(W*BSIZE,0)處
LineTo(hdc,W*BSIZE,H*BSIZE); //從游標所在位置畫線到(W*BSIZE,H*BSIZE)處
DeleteObject(hpen1); //刪除之前所選用的畫筆
SelectObject(hdc,hpen); //重新選擇畫筆
SelectObject(hdc,hbrush); //選擇畫刷
for(i=1;i<H-1;i++) //繪製遊戲區域方格線
for(j=1;j<W-1;j++)
Rectangle(hdc,j*BSIZE,i*BSIZE,(j+1)*BSIZE,(i+1)*BSIZE);
for(i=1;i<5;i++) //繪製右邊狀態列遊戲預覽區域方格線
for(j=W+1;j<W+W1-1;j++)
Rectangle(hdc,j*BSIZE,i*BSIZE,(j+1)*BSIZE,(i+1)*BSIZE);
Rectangle(hdc,(W+1)*BSIZE,Y2*BSIZE,(W+W1-1)*BSIZE,(Y2+2)*BSIZE); //繪製分數欄方格線
Rectangle(hdc,(W+1)*BSIZE,Y3*BSIZE,(W+W1-1)*BSIZE,(Y3+2)*BSIZE); //繪製等級欄方格線
Rectangle(hdc,(W+1)*BSIZE,Y4*BSIZE,(W+W1-1)*BSIZE,(Y4+4)*BSIZE); //繪製幫助欄方格線
TextOut(hdc,(W+2)*BSIZE,(Y2+0.2)*BSIZE,"分 數",8); //輸出文字
TextOut(hdc,(W+2)*BSIZE,(Y3+0.2)*BSIZE,"等 級",8); //同上
DeleteObject(hpen); //刪除畫筆
DeleteObject(hbrush); //刪除畫刷
}
void ShowScore(HDC hdc) //顯示分數的函式
{
char score_str[4]; //定義字串用於儲存分數值
wsprintf(score_str,"%3d",score); //將數字score轉換成字串後儲存到score_str之中
TextOut(hdc,(W+2.5)*BSIZE,(Y2+1.2)*BSIZE,score_str,3); //在遊戲板上顯示分數
}
void ShowLevel(HDC hdc) //顯示等級的,具體同上
{
char level_str[4];
wsprintf(level_str,"%3d",level);
TextOut(hdc,(W+2.5)*BSIZE,(Y3+1.2)*BSIZE,level_str,3);
}
void ShowHelp(HDC hdc) //顯示幫助的,該函式只在初始化介面時呼叫
{
char help1[]="↑ - 旋轉",
help2[]="↓ - 下移",
help3[]="← - 左移",
help4[]="→ - 右移";
TextOut(hdc,(W+1.8)*BSIZE,(Y4+0.2)*BSIZE,help1,9);
TextOut(hdc,(W+1.8)*BSIZE,(Y4+1.2)*BSIZE,help2,9);
TextOut(hdc,(W+1.8)*BSIZE,(Y4+2.2)*BSIZE,help3,9);
TextOut(hdc,(W+1.8)*BSIZE,(Y4+3.2)*BSIZE,help4,9);
}
void EraseBox(HDC hdc,HPEN hpen,int x,int y,int num) //清除(x,y)處編號為num,的方塊
{
int i;
HBRUSH hbrush=CreateSolidBrush(BgColor);
SelectObject(hdc,hpen);
SelectObject(hdc,hbrush);
for(i=0;i<4;i++)//用背景色填充方塊所在區域,使方塊隱藏
Rectangle(hdc,
(x+block[num].a[i][0])*BSIZE,
(y+block[num].a[i][1])*BSIZE,
(x+block[num].a[i][0]+1)*BSIZE,
(y+block[num].a[i][1]+1)*BSIZE);
DeleteObject(hpen);
DeleteObject(hbrush);
}
void ShowBox(HDC hdc,HPEN hpen,int x,int y,int num) //顯示(x,y)處編號為num,的方塊
{
int i;
HBRUSH hbrush;
hbrush=CreateSolidBrush(block[num].color); //建立畫刷,顏色和方塊顏色相同
SelectObject(hdc,hpen);
SelectObject(hdc,hbrush);
for(i=0;i<4;i++) //顯示方塊的過程
Rectangle(hdc,
(x+block[num].a[i][0])*BSIZE,
(y+block[num].a[i][1])*BSIZE,
(x+block[num].a[i][0]+1)*BSIZE,
(y+block[num].a[i][1]+1)*BSIZE);
DeleteObject(hpen);
DeleteObject(hbrush);
}
void SetFullRow(HDC hdc,HPEN hpen) //滿行處理函式
{
int i,ii,j;
int org_top=top;
int flag=0;
HBRUSH hbrush;
SelectObject(hdc,hpen);
for(i=y;i<y+4;i++) //從y行開始,從上到下遍歷遊戲區域
{
if(i<=0||i>=H-1) continue; //越界了,就跳出本次迴圈
for(j=1;j<W-1;j++)
if(!board[i][j].var) break; //一旦該行有一個為空,即跳出
if(j==W-1) //找到滿行了
{
for(ii=i;ii>=top;ii--) //重置遊戲區域各個方格的狀態,top為最頂端,i為找到的滿行
for(j=1;j<W-1;j++)
board[ii][j]=board[ii-1][j];
top++;
score+=10; //分數加10
flag=1; //標誌符
}
}
if(flag) //如果有滿行,則重繪主機板
{
for(i=org_top;i<y+4;i++) //原來的最頂端
{
if(i<=0||i>=H-1) continue; //越界了,就跳出本次迴圈
for(j=1;j<W-1;j++)
{ //注意這裡繪製主機板時,每次都要選擇不同的畫刷,用完後一定要刪除
hbrush=CreateSolidBrush(board[i][j].color);
SelectObject(hdc,hbrush);
Rectangle(hdc,j*BSIZE,i*BSIZE,(j+1)*BSIZE,(i+1)*BSIZE);
DeleteObject(hbrush);
}
}
if(level!=score/level_step) //這裡是程式優化部分,也可省略
level=score/level_step;
ShowScore(hdc); //更新分數
ShowLevel(hdc); //更新等級
}
DeleteObject(hpen);
}
void ChangeVar(void) //改變遊戲主機板的狀態
{
int i;
for(i=0;i<4;i++)
{
board[y+block[cur_boxnum].a[i][1]][x+block[cur_boxnum].a[i][0]].var=1; //狀態置1,表示有方塊填充
board[y+block[cur_boxnum].a[i][1]][x+block[cur_boxnum].a[i][0]].color=block[cur_boxnum].color;
}
}
BOOL CanMove(void) //判斷方塊是否能移動
{
int i;
for(i=0;i<4;i++)
if(board[y+block[cur_boxnum].a[i][1]][x+block[cur_boxnum].a[i][0]].var) //如果該位置以及有方塊填充,則不能移動
return FALSE;
return TRUE;
}
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM); //視窗過程函式的宣告
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hprevInstance,PSTR szCmdLine,int iCmdShow) //入口函式,即主函式
{ //各個形參所代表的意思請自行查閱資料
int screenwide,screenhight; //定義變數來儲存螢幕寬度和高度
char AppName[]="Tetris"; //定義並初始化視窗類名
HWND hwnd; //定義視窗控制代碼
MSG msg; //定義訊息結構體
WNDCLASSEX wndclass; //定義視窗類
wndclass.cbSize=sizeof(wndclass); //視窗類大小
wndclass.style=CS_HREDRAW|CS_VREDRAW; //視窗類風格
wndclass.lpfnWndProc=WndProc; //視窗過程函式為WndProc
wndclass.cbClsExtra=0; //視窗類無擴充套件
wndclass.cbWndExtra=0; //視窗例項無擴充套件
wndclass.hInstance=hInstance; //當前例項控制代碼為hInstance
wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION); //採用預設圖示
wndclass.hCursor=LoadCursor(NULL,IDC_ARROW); //預設游標,游標進入視窗區域時,將顯示為箭頭
wndclass.hbrBackground=CreateSolidBrush(BgColor); //視窗背景色
wndclass.lpszMenuName=NULL; //視窗類無選單
wndclass.lpszClassName=AppName; //視窗類名
wndclass.hIconSm=LoadIcon(NULL,IDI_APPLICATION); //採用預設小圖示
if(!RegisterClassEx(&wndclass)) //視窗類的註冊
{
MessageBeep(0);
return FALSE;
}
screenwide=GetSystemMetrics(SM_CXFULLSCREEN); //獲取螢幕寬度,即橫向解析度
screenhight=GetSystemMetrics(SM_CYFULLSCREEN); //獲取螢幕高度
hwnd=CreateWindow( //建立視窗
AppName, //視窗類名
"俄羅斯方塊", //視窗例項標題名
WS_OVERLAPPEDWINDOW,
(screenwide-(W+W1)*BSIZE)/2, //視窗左上角橫座標
(screenhight-H*BSIZE)/2, //左上角縱座標
(W+W1)*BSIZE, //視窗寬度
(H+1)*BSIZE, //視窗高度,注意包含了標題欄
NULL, //視窗無父視窗
NULL, //視窗無住選單
hInstance, //當前應用程式例項控制代碼
NULL //這個值一般置NULL,具體請自行查詢
);
if(!hwnd) return FALSE; //建立失敗則退出程式
ShowWindow(hwnd,iCmdShow); //顯示視窗
UpdateWindow(hwnd); //重新整理視窗
MessageBox(hwnd," 峰哥哥製作","開始",MB_OK); //彈出框
SetTimer(hwnd,1,500,NULL); //設定一個500毫秒觸發一次的定時器,具體引數所代表意義請自行查詢
while(GetMessage(&msg,NULL,0,0)) //訊息迴圈,用於從訊息佇列中獲取訊息
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam; //注意這一步不能少
}
LRESULT CALLBACK WndProc(HWND hwnd,UINT iMsg,WPARAM wParam,LPARAM lParam)
{ //定義視窗過程函式
int i,j;
int old_boxnum; //用於儲存之前的方塊號
HDC hdc; //定義DC控制代碼,DC裡包含繪圖預設的一些屬性
PAINTSTRUCT ps; //定義繪圖結構,此結構包含的資訊應用程式使用來繪製一個擁有該應用視窗客戶區
HPEN hpen=CreatePen(PS_SOLID,1,FgColor); //選擇畫刷,用背景色作畫刷
switch(iMsg) //訊息處理的過程
{
case WM_CREATE: ////當一個應用程式使用函式CreateWindow或CreateWindowEx來建立一個視窗時,
//系統將傳送該訊息給此新建視窗過程。該訊息將在建立視窗之後,顯示視窗
//之前傳送該訊息,該訊息將在CreateWindow或CreateWindowEx函式返回之前傳送。
for(i=1;i<H;i++) //初始化狀態,講兩豎邊狀態初始化為1
board[i][0].var=board[i][W-1].var=1;
for(j=1;j<W-1;j++) //將最底部狀態置1
board[H-1][j].var=1;
for(i=1;i<H-1;i++) //將遊戲區域狀態置0
for(j=1;j<W-1;j++)
{
board[i][j].var=0;
board[i][j].color=BgColor;
}
srand((unsigned)time(NULL)); //初始化隨機數發生器
cur_boxnum=rand()%19; //賦初值
next_boxnum=rand()%19; //賦初值
x=Cur_x; //初始化方塊橫座標
y=Cur_y; //初始化方塊縱座標
return 0; //直接退出視窗過程函式
case WM_PAINT: //繪製介面,當應用程式適用UpdateWindow重新整理視窗時,第一次傳送該訊息
hdc=BeginPaint(hwnd,&ps); //給DC控制代碼賦初值
Paint(hdc,hpen); //呼叫Paint函式繪製介面
ShowScore(hdc); //顯示分數
ShowLevel(hdc); //顯示等級
ShowHelp(hdc); //顯示幫助欄
ShowBox(hdc,hpen,x,y,cur_boxnum); //顯示遊戲區域中的遊戲方塊
ShowBox(hdc,hpen,W+1,1,next_boxnum); //顯示右邊狀態列的遊戲方塊
EndPaint(hwnd,&ps); //EndPaint函式標記指定視窗的繪畫過程結束;這個函式在每次呼叫BeginPaint函式之後被請求,但僅僅在繪畫完成以後。
return 0;
case WM_TIMER: //定時器訊息,每0.5秒接受到一次
hdc=GetDC(hwnd); //該函式檢索一指定視窗的客戶區域或整個螢幕的顯示裝置上下文環境的控制代碼,以後可以在GDI函式中使用該控制代碼來在裝置上下文環境中繪圖。
y++; //y增1
if(CanMove()) //如果能移動,則擦除原來位置方塊,顯示新位置方塊,相當於是方塊下落
{
EraseBox(hdc,hpen,x,y-1,cur_boxnum);
ShowBox(hdc,hpen,x,y,cur_boxnum);
}
else //如果不能移動,則到底了,y恢復之前的值,併發送MS_NEWBLOCK產生新的方塊
{
y--;
SendMessage(hwnd,MS_NEWBLOCK,0,0);
}
ReleaseDC(hwnd,hdc); //數釋放裝置上下文環境(DC)供其他應用程式使用
return 0;
case WM_KEYDOWN: //當按下鍵時,會發送該訊息
hdc=GetDC(hwnd);
switch((int)wParam) //判斷具體按下的鍵
{
case VK_UP:
old_boxnum=cur_boxnum; //儲存當前方塊號
cur_boxnum=block[cur_boxnum].next; //方塊號變為下一個方塊號
if(CanMove())
{
EraseBox(hdc,hpen,x,y,old_boxnum);
ShowBox(hdc,hpen,x,y,cur_boxnum);
}
else
cur_boxnum=old_boxnum; //恢復之前的值
break;
case VK_DOWN:
y++;
if(CanMove())
{
EraseBox(hdc,hpen,x,y-1,cur_boxnum);
ShowBox(hdc,hpen,x,y,cur_boxnum);
}
else
{
y--;
SendMessage(hwnd,MS_NEWBLOCK,0,0); //不能下移,就到底了,產生新的方塊
}
break;
case VK_LEFT:
x--; //橫座標減小1
if(CanMove()) //如果能移動,則下移,不能移動則恢復之前座標
{
EraseBox(hdc,hpen,x+1,y,cur_boxnum);
ShowBox(hdc,hpen,x,y,cur_boxnum);
}
else
x++;
break;
case VK_RIGHT: //同上
x++;
if(CanMove())
{
EraseBox(hdc,hpen,x-1,y,cur_boxnum);
ShowBox(hdc,hpen,x,y,cur_boxnum);
}
else
x--;
break;
}
ReleaseDC(hwnd,hdc);
return 0;
case MS_NEWBLOCK:
hdc=GetDC(hwnd);
if(top>y+block[cur_boxnum].a[0][1])
top=y+block[cur_boxnum].a[0][1]; //確定最高點
ChangeVar(); //改變遊戲主機板狀態
SetFullRow(hdc,hpen); //滿行處理
cur_boxnum=next_boxnum;
x=Cur_x; //重置方塊座標
y=Cur_y;
hpen=CreatePen(PS_SOLID,1,FgColor);
srand((unsigned)time(NULL)); //初始化隨機數發生器
next_boxnum=rand()%19;
EraseBox(hdc,hpen,W+1,1,cur_boxnum); //清除右邊狀態列的方塊
ShowBox(hdc,hpen,W+1,1,next_boxnum); //顯示右邊狀態列的方塊
ShowBox(hdc,hpen,Cur_x,Cur_y,cur_boxnum); //顯示遊戲主機板頂部方塊
if(!CanMove()) //剛移開始就不能移動,則結束程式退出
{
KillTimer(hwnd,1);
MessageBox(hwnd," 退出遊戲","退出",MB_OK);
PostQuitMessage(0);
}
ReleaseDC(hwnd,hdc);
return 0;
case WM_DESTROY: //退出遊戲
KillTimer(hwnd,1);
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd,iMsg,wParam,lParam); //視窗預設處理,當訊息處理函式未處理訊息時就呼叫該函式進行處理
}