您好,登錄后才能下訂單哦!
這篇文章主要介紹了C++怎么實現(xiàn)俄羅斯方塊游戲,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
編譯器是code::block + MinGW ,具體代碼如下:
#include <windows.h> #include <iostream> #include <cstdlib> #include <ctime> using namespace std; #define CellWidth 20 #define MAP_WIDTH 12 #define MAP_HEIGHT 18 #define ID_TIMER 1 class map_floor; class Block; LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM); /* Make the class name into a global variable */ char szClassName[ ] = "CodeBlocksWindowsApp"; int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow) { HWND hwnd; /* This is the handle for our window */ MSG messages; /* Here messages to the application are saved */ WNDCLASSEX wincl; /* Data structure for the windowclass */ /* The Window structure */ wincl.hInstance = hThisInstance; wincl.lpszClassName = szClassName; wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */ wincl.style = CS_DBLCLKS|CS_HREDRAW | CS_VREDRAW; /* Catch double-clicks */ wincl.cbSize = sizeof (WNDCLASSEX); /* Use default icon and mouse-pointer */ wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION); wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION); wincl.hCursor = LoadCursor (NULL, IDC_ARROW); wincl.lpszMenuName = NULL; /* No menu */ wincl.cbClsExtra = 0; /* No extra bytes after the window class */ wincl.cbWndExtra = 0; /* structure or the window instance */ /* Use Windows's default colour as the background of the window */ wincl.hbrBackground =(HBRUSH) GetStockObject(WHITE_BRUSH);//COLOR_BACKGROUND; /* Register the window class, and if it fails quit the program */ if (!RegisterClassEx (&wincl)) return 0; /* The class is registered, let's create the program*/ hwnd = CreateWindowEx ( 0, /* Extended possibilites for variation */ szClassName, /* Classname */ "Code::Blocks Template Windows App", /* Title Text */ WS_OVERLAPPEDWINDOW, /* default window */ CW_USEDEFAULT, /* Windows decides the position */ CW_USEDEFAULT, /* where the window ends up on the screen */ CW_USEDEFAULT, /* The programs width */ CW_USEDEFAULT, /* and height in pixels */ NULL, /* The window is a child-window to desktop */ NULL, /* No menu */ hThisInstance, /* Program Instance handler */ NULL /* No Window Creation data */ ); /* Make the window visible on the screen */ ShowWindow (hwnd, nCmdShow); /* Run the message loop. It will run until GetMessage() returns 0 */ while (GetMessage (&messages, NULL, 0, 0)) { /* Translate virtual-key messages into character messages */ TranslateMessage(&messages); /* Send message to WindowProcedure */ DispatchMessage(&messages); } /* The program return-value is 0 - The value that PostQuitMessage() gave */ return messages.wParam; } enum{e_LINE,e_CORNER,e_STAIR,e_TANCK,e_TIAN}; const int TOTAL_BLOCK_STYLE = 5;//方塊類型有4種 class Block { public: Block(int x = 100, int y = 100); Block(const Block & rh)//復制構(gòu)造函數(shù),可能沒什么用,但是還是定義它吧 { this->m_style = rh.m_style; this->m_direct = rh.m_direct; for(int i = 0 ; i < 4 ; i ++) this->m_block[i] = rh.m_block[i]; } Block & operator = (const Block& rh)//重載=號,實現(xiàn)方塊的賦值 { this->m_style = rh.m_style; this->m_direct = rh.m_direct; for(int i = 0 ; i < 4 ; i ++) this->m_block[i] = rh.m_block[i]; return *this; } ~Block(){} int create_block(int x = 100 , int y = 100); //顯示在游戲區(qū)內(nèi)移動的方塊 int show_block(HDC hdc,const POINT& GameLeftTop); //顯示將要出現(xiàn)的方塊,即游戲區(qū)左邊的方塊 int show_next_block(HDC hdc); //旋轉(zhuǎn),該函數(shù)比較難實現(xiàn),代碼量也比較大,以后有時間在慢慢優(yōu)化,關(guān)于解析看定義處 int rotate(); //產(chǎn)生隨機方塊 int random_block(); //下面為方塊移動的成員函數(shù) int get_block_height(){ return m_block[1].y;} int move_down(const RECT& GameClient); int move_left(const RECT& GameClient); int move_right(const RECT& GameClient); int move_up(const RECT& GameClient); int move_to(int x , int y); //檢測方塊是否在游戲區(qū)內(nèi) // int check_block(const map_floor& map, const RECT& GameClent); int check_block(const map_floor& map, const POINT& LeftTopScrCdnt); int print_to_map(map_floor& map , const POINT& LeftTopScrCdnt); private: int m_style;//方塊的風格樣式,具體看定義的枚舉變量 int m_direct;//方塊的方向,是對m_style的具體數(shù)據(jù) POINT m_block[4];//下標為1的方塊是中心坐標,旋轉(zhuǎn)都是圍繞著該方塊進行,這樣可以利于旋轉(zhuǎn)和邏輯清晰 }; class map_floor { public: map_floor() { ZeroMemory(m_block_bar,sizeof(int )*12*18); } ~map_floor(){} void show_block_bar(HDC hdc , const POINT& LeftTopScrCdnt) { for(int i = 0 ; i < MAP_HEIGHT ; ++ i) { for(int j = 0 ; j < MAP_WIDTH ; ++ j) { if(m_block_bar[i][j]) { Rectangle(hdc,LeftTopScrCdnt.x + j*CellWidth , LeftTopScrCdnt.y + i*CellWidth, LeftTopScrCdnt.x + (j+1)*CellWidth , LeftTopScrCdnt.y + (i+1)*CellWidth); } } } } friend class Block; protected: private: int m_block_bar[MAP_HEIGHT][MAP_WIDTH];//游戲區(qū)的地板,用18*12的二維數(shù)組表示 }; Block::Block(int x , int y) { // ZeroMemory(m_block_bar,sizeof(int )*12*18); srand( (unsigned)time( NULL ) );//初始化隨機數(shù),用于產(chǎn)生方塊 // POINT pt = {100,100}; create_block(x,y); } int Block::random_block() { m_style = rand()%TOTAL_BLOCK_STYLE; // m_style = e_CORNER; //測試之用 // m_style = e_LINE; //測試之用 if(m_style == e_STAIR || m_style == e_TANCK) m_direct = rand()%4; else if(m_style == e_LINE) m_direct = rand()%2; else if(m_style == e_CORNER) m_direct = rand()%8; else if(m_style == e_TIAN) m_direct = 0; m_direct = 1; } int Block::check_block(const map_floor& map, const POINT& LeftTopScrCdnt) { int x , y ; //x , y 為方塊相對于地圖的坐標,左上角為(0,0) for(int i = 0 ; i < 4 ; i ++) { x = (m_block[i].x - LeftTopScrCdnt.x)/CellWidth; y = (m_block[i].y - LeftTopScrCdnt.y)/CellWidth; if(x < 0 || x >= MAP_WIDTH || y >= MAP_HEIGHT)//不用檢測y < 0 的情況 return 0; if(y < 0) continue; if(map.m_block_bar[y][x]) return 0; } return 1; } int Block::move_down(const RECT& GameClient)//下移,由計時器消息調(diào)用 { int i; // for (i = 0 ; i < 4 ; i ++ ) // { // if(m_block[i].y == GameClient.bottom - CellWidth) // return 0; // } for (i = 0; i < 4 ;i ++ ) { m_block[i].y += CellWidth; } return 1; } int Block::move_up(const RECT& GameClient) { move_to(m_block[1].x,m_block[1].y - CellWidth); return 1; } int Block::move_left(const RECT& GameClient) { move_to(m_block[1].x - CellWidth,m_block[1].y); return 1; } int Block::move_right(const RECT& GameClient) { move_to(m_block[1].x + CellWidth , m_block[1].y); return 1; } int Block::create_block(int x , int y) { m_block[1].x = x; m_block[1].y = y; random_block(); rotate(); return 1; } int Block::move_to(int x , int y) { int Vx = x - m_block[1].x; int Vy = y - m_block[1].y; for(int i = 0 ; i < 4 ; i ++) { m_block[i].x += Vx; m_block[i].y += Vy; } } int Block::print_to_map(map_floor& map , const POINT& LeftTopScrCdnt) { int x , y; int i , j; for(i = 0 ; i < 4 ; i ++ ) { x = (m_block[i].x - LeftTopScrCdnt.x)/CellWidth; y = (m_block[i].y - LeftTopScrCdnt.y)/CellWidth; if(x<0 || x >= MAP_WIDTH || y <0 || y >= MAP_HEIGHT)//為保安全 ,測試之用,完成后將被注釋掉 return 0; map.m_block_bar[y][x] = 1 ; for(j = 0 ; j < MAP_WIDTH ; j ++) { if(map.m_block_bar[y][j] != 1) break; } if(MAP_WIDTH == j) { for(j = 0 ; j < MAP_WIDTH ; j ++) { map.m_block_bar[y][j] = 5;//數(shù)字5代表要消掉的行 } } } int idx; for(i = 0 ; i < MAP_WIDTH ; i ++) { for(idx = j = MAP_HEIGHT - 1 ; j >= 0 ; j --) { if(map.m_block_bar[j][i] != 5) { map.m_block_bar[idx--][i] = map.m_block_bar[j][i]; } } while(idx >= 0) { map.m_block_bar[idx--][i] = 0; } } return 1; } //下面該函數(shù)功能是實現(xiàn)方塊旋轉(zhuǎn),可以說是整個【俄羅斯方塊】的難點所在,也是其核心部分 //方塊用以數(shù)組block【4】表示,其余3個方格都將圍繞block【1】旋轉(zhuǎn),方塊由于有不對稱方塊 //存在,我原本是要分7種,但是后面代碼量太大了,所以我將方塊根據(jù)樣式歸為了四種,分別是: // //e_LINE 線形 就是一條線的那個,這個是對稱的方塊,只需分兩個方向分別為橫向和縱向,方向 // 用m_direct保持,其他的方塊一樣 // //e_TANCK 坦克形 這個是方塊是對稱的,分四種方向,根據(jù)m_direct對4進行求余的方法可以大大縮減 // 代碼量,對于下面兩種方塊也是利用了求余的方式化簡許多,才使得代碼不會那么冗余, // 這是后面我才想到的方法。 // //e_STAIR 樓梯形 這個方塊相對前面兩種來說有點難度,主要是因為它不是對稱的,但是相對下面的這種 // 來說比較簡單,原本我沒用對m_direct求余的方法時,我將它分為了e_STAIR_BACK和e_STAIR_FRONT // 兩類來討論的,后面發(fā)現(xiàn)代碼可以縮減才將其歸為一類只要記住block【0】和block【1】的位置不會 // 變化,變化的是block【2】和block【3】,block【2】相對block【1】上移或下移,x坐標與block【1】 // 相同,block【3】.y一直在block【1】下面一行,相對其左右變化 // //e_CORNER 角形 這個方塊個人覺得是最難旋轉(zhuǎn)的方塊,與上面一種異樣,原本我將它分為e_CORNER_FRONT , e_CORNER_BACK // 兩類,每類有四個方向的變化,后來根據(jù)求余可以將同一個方向的變化變?yōu)橐环N,只是block【3】號方塊要 // 根據(jù)m_direct方向來進行調(diào)整 int Block::rotate() { switch (m_style) { case e_LINE: { switch(m_direct) { case 0://橫向轉(zhuǎn)為縱向 { for(int i = 0 ; i < 4 ; i ++) { m_block[i].x = m_block[1].x; m_block[i].y = m_block[1].y + (1-i)*CellWidth; } m_direct = 1; } break; case 1://縱向轉(zhuǎn)為橫向 { for(int i = 0 ; i < 4 ; i ++) { m_block[i].y = m_block[1].y; m_block[i].x = m_block[1].x + (1-i)*CellWidth; } m_direct = 0; } break; } } break; //下面為樓梯風格的方塊,由于其不是對稱的分類為正反兩種,正反種風格各有兩種變化, //m_direct% == 0是正反兩面的同種變化 case e_STAIR: { int flag; flag = m_direct < 2 ? 1 : -1; m_block[0].x = m_block[1].x + flag*CellWidth; m_block[0].y = m_block[1].y; m_block[2].x = m_block[1].x; m_block[3].y = m_block[1].y + CellWidth; if(m_direct%2 == 0) { m_block[2].y = m_block[1].y - CellWidth; m_block[3].x = m_block[1].x + flag*CellWidth; m_direct++; } else { m_block[2].y = m_block[1].y + CellWidth; m_block[3].x = m_block[1].x - flag*CellWidth; if(m_direct < 2) m_direct = 0; else m_direct = 2; } } break; //角形方塊,與樓梯形方塊一樣非對稱,有正反倆個種,每種有四種變化, //下面根據(jù)m_direct%4的值將這些變化歸類解決,對于正,反面對應的相同 //變化的方向,只有block【3】方格位置不一樣,可以看我畫的圖對比即可了解 case e_CORNER: { switch (m_direct%4) { case 0: { m_block[0].x = m_block[1].x+CellWidth; m_block[0].y = m_block[2].y = m_block[1].y; m_block[2].x = m_block[1].x-CellWidth; m_block[3].x = m_block[1].x-CellWidth; if(m_direct>=4) m_block[3].y = m_block[1].y-CellWidth; else m_block[3].y = m_block[1].y+CellWidth; m_direct ++; } break; case 1: { m_block[0].x = m_block[2].x = m_block[1].x; m_block[0].y = m_block[1].y+CellWidth; m_block[2].y = m_block[1].y-CellWidth; if(m_direct>=4) m_block[3].x = m_block[1].x+CellWidth; else m_block[3].x = m_block[1].x-CellWidth; m_block[3].y = m_block[1].y-CellWidth; m_direct ++; } break; case 2: { m_block[0].x = m_block[1].x-CellWidth; m_block[0].y = m_block[2].y = m_block[1].y; m_block[2].x = m_block[1].x+CellWidth; m_block[3].x = m_block[1].x+CellWidth; if (m_direct>=4) m_block[3].y = m_block[1].y+CellWidth; else m_block[3].y = m_block[1].y-CellWidth; m_direct ++; } break; case 3: { m_block[0].x = m_block[2].x = m_block[1].x; m_block[0].y = m_block[1].y-CellWidth; m_block[2].y = m_block[1].y+CellWidth; if(m_direct>=4) { m_block[3].x = m_block[1].x-CellWidth; m_direct = 4;} else { m_block[3].x = m_block[1].x+CellWidth; m_direct = 0;} m_block[3].y = m_block[1].y+CellWidth; } break; default: break; } } break; case e_TANCK://坦克形方塊,與線形方塊一樣是對稱的,分四種變化 { switch (m_direct%2) { case 0: { m_block[0].x = m_block[2].x = m_block[1].x; m_block[0].y = m_block[1].y - CellWidth; m_block[2].y = m_block[1].y + CellWidth; int flag = m_direct == 0 ? 1 : -1; m_block[3].x = m_block[1].x + flag*CellWidth; m_block[3].y = m_block[1].y; m_direct++; } break; case 1: { m_block[0].y = m_block[2].y = m_block[1].y; m_block[0].x = m_block[1].x - CellWidth; m_block[2].x = m_block[1].x + CellWidth; m_block[3].x = m_block[1].x; int flag = m_direct == 3 ? -1:1; m_block[3].y = m_block[1].y + flag*CellWidth; if(m_direct == 3) m_direct = 0; else m_direct++; } break; default: break; } } break; case e_TIAN: { m_block[0].y = m_block[1].y; m_block[0].x = m_block[1].x + CellWidth; m_block[2].x = m_block[1].x; m_block[2].y = m_block[1].y + CellWidth; m_block[3].x = m_block[1].x + CellWidth; m_block[3].y = m_block[1].y + CellWidth; } break; default: break; } return 0; } int Block::show_block(HDC hdc,const POINT& GameLeftTop) { for (int i = 0 ; i < 4 ; i ++ ) { if(m_block[i].y >= GameLeftTop.y) Rectangle(hdc,m_block[i].x,m_block[i].y,m_block[i]. x+CellWidth,m_block[i].y+CellWidth); if(i==0)//測試所用,完成后將會被注釋掉 {MoveToEx(hdc,m_block[i].x,m_block[i].y,NULL); LineTo(hdc,m_block[i].x+CellWidth,m_block[i].y+CellWidth);} } return 1; } int Block::show_next_block(HDC hdc) { for (int i = 0 ; i < 4 ; i ++ ) { Rectangle(hdc,m_block[i].x,m_block[i].y,m_block[i]. x+CellWidth,m_block[i].y+CellWidth); } return 1; } Block block , next_block , try_block; map_floor map;int d = 0; LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc ; PAINTSTRUCT ps ; //游戲客戶區(qū) static RECT GameClient; //一個方格的像素為CellWidth = 20 游戲區(qū)寬 12 個方格 高 18 個方格 const int Width = 240 ,Height = 360; static POINT LeftTopScrCdnt;//游戲區(qū)得左上角坐標 switch (message) { case WM_CREATE: SetTimer(hwnd,ID_TIMER,500,NULL); return 0 ; case WM_SIZE: GetClientRect(hwnd,&GameClient); LeftTopScrCdnt.x = (GameClient.right-GameClient.left)/2 - Width/2; LeftTopScrCdnt.y = GameClient.top + 50; GameClient.left = LeftTopScrCdnt.x; GameClient.top = LeftTopScrCdnt.y; GameClient.right = LeftTopScrCdnt.x + Width; GameClient.bottom = LeftTopScrCdnt.y + Height; //創(chuàng)建下一個將要出現(xiàn)的方塊 next_block.create_block(GameClient.right+2*CellWidth,(GameClient.bottom+GameClient.top)/2-3*CellWidth); block.move_to((GameClient.right+GameClient.left)/2,GameClient.top-CellWidth); break; case WM_TIMER: block.move_down(GameClient); if(!block.check_block(map,LeftTopScrCdnt))//檢測方塊的碰撞,如果則說明方塊到底底部,將其上移然后打印進地圖 { block.move_up(GameClient); if(!block.check_block(map,LeftTopScrCdnt) || block.get_block_height() <= LeftTopScrCdnt.y )//檢測游戲是否結(jié)束 { KillTimer(hwnd,ID_TIMER); d = 4; } block.print_to_map(map,LeftTopScrCdnt); SendMessage(hwnd,WM_KEYDOWN,VK_ESCAPE,0); } InvalidateRect(hwnd,NULL,true); break; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; MoveToEx(hdc,LeftTopScrCdnt.x,LeftTopScrCdnt.y,NULL); Rectangle(hdc,GameClient.left,GameClient.top,GameClient.right,GameClient.bottom);//游戲區(qū)邊框 SelectObject(hdc,GetStockObject(BLACK_BRUSH)); map.show_block_bar(hdc,LeftTopScrCdnt); block.show_block(hdc,LeftTopScrCdnt); next_block.show_next_block(hdc); EndPaint (hwnd, &ps); break; case WM_KEYDOWN: InvalidateRect(hwnd,NULL,true); switch (wParam) { case VK_SPACE: { try_block = block; try_block.rotate(); if(try_block.check_block(map ,LeftTopScrCdnt)) block = try_block; break; } case VK_LEFT: { block.move_left(GameClient); if(!block.check_block(map ,LeftTopScrCdnt)) block.move_right(GameClient); } break; case VK_RIGHT: { block.move_right(GameClient); if (!block.check_block(map ,LeftTopScrCdnt)) block.move_left(GameClient); } break; case VK_DOWN: { // block.move_down(GameClient); SendMessage(hwnd,WM_TIMER,0,0); } break; case VK_ESCAPE://測試用,完成后將會被注釋掉 { block = next_block; next_block.create_block(GameClient.right+2*CellWidth,(GameClient.bottom+GameClient.top)/2-3*CellWidth); block.move_to((GameClient.right+GameClient.left)/2,GameClient.top-CellWidth); } break; default: break; } break; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }
感謝你能夠認真閱讀完這篇文章,希望小編分享的“C++怎么實現(xiàn)俄羅斯方塊游戲”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識等著你來學習!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。