C/C++實(shí)現(xiàn)俄羅斯方塊游戲
一、游戲效果展示
每個(gè)方塊的朝向使用二維數(shù)組表示
二、完整代碼
可以直接拷貝運(yùn)行
#include<graphics.h> #include<stdio.h> #include<time.h> #include<conio.h> //kbhit() int score = 0; //總分 int rank = 0; //等級(jí) #define BLOCK_COUNT 5 #define BLOCK_WIDTH 5 #define BLOCK_HEIGHT 5 #define UNIT_SIZE 20 //小方塊寬度 #define START_X 130 //方塊降落框,方塊降落起始位置 #define START_Y 30 #define KEY_UP 87 //用戶(hù)操作 #define KEY_LEFT 65 #define KEY_RIGHT 68 #define KEY_DOWN 83 #define KEY_SPACE 32 #define MinX 30 //游戲左上角位置 #define MinY 30 int speed = 500; //方塊降落速度 int NextIndex = -1; //下一個(gè)方塊 int BlockIndex = -1; //當(dāng)前方塊 typedef enum { //方塊朝向 BLOCK_UP, BLOCK_RIGHT, BLOCK_LEFT, BLOCK_DOWN }block_dir_t; typedef enum { //方塊移動(dòng)方向 MOVE_DOWN, MOVE_LEFT, MOVE_RIGHT }move_dir_t; //方塊顏色 int color[BLOCK_COUNT] = { GREEN, CYAN, MAGENTA, YELLOW, BROWN }; int visit[30][15]; //訪(fǎng)問(wèn)數(shù)組visit[i][j] = 1表示該位置有方塊 int markColor[30][15]; //對(duì)應(yīng)位置顏色 int block[BLOCK_COUNT * 4][BLOCK_WIDTH][BLOCK_HEIGHT] = { // | 形方塊 { 0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 }, // L 形方塊 { 0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,1,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,1,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 }, // 田 形方塊 { 0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 }, // T 形方塊 { 0,0,0,0,0,0,1,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0 }, // Z 形方塊 { 0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0 }, }; /*************************** * 功能:歡迎頁(yè)面 * 輸入: * 無(wú) * 返回: * 無(wú) **************************/ void welcome() { //1.初始化畫(huà)布 initgraph(550, 660); system("pause"); //2.設(shè)置窗口標(biāo)題 HWND window = GetHWnd();//獲得窗口,獲得當(dāng)前窗口 SetWindowText(window, _T("俄羅斯方塊 小明來(lái)嘍")); //設(shè)置標(biāo)題 //3.設(shè)置游戲初始頁(yè)面 setfont(40, 0, _T("微軟雅黑")); //設(shè)置文本的字體樣式(高,寬(0表示自適應(yīng)),字體) setcolor(WHITE); // 設(shè)置顏色 outtextxy(205, 200, _T("俄羅斯方法")); setfont(20, 0, _T("楷體")); setcolor(WHITE); // 設(shè)置顏色 outtextxy(175, 300, _T("編程,從俄羅斯方塊開(kāi)始")); Sleep(3000); } /*************************** * 功能:初始化游戲場(chǎng)景 * 輸入: * 無(wú) * 返回: * 無(wú) **************************/ void initGameSceen() { char str[16]; //存放分?jǐn)?shù) //1.清屏 cleardevice(); //2.畫(huà)場(chǎng)景 rectangle(27, 27, 336, 635); //方塊降落框外框 rectangle(29, 29, 334, 633); //方塊降落框內(nèi)框 rectangle(370, 50, 515, 195); //方塊提示框 setfont(24, 0, _T("楷體")); //寫(xiě)“下一個(gè)” setcolor(LIGHTGRAY); //灰色 outtextxy(405, 215, _T("下一個(gè):")); setcolor(RED); //寫(xiě)分?jǐn)?shù) outtextxy(405, 280, _T("分?jǐn)?shù):")); //按指定格式,將score寫(xiě)入str sprintf_s(str, 16, "%d", score); //這里設(shè)置字符集為多字符,保證outtextxy可以寫(xiě)出變量str outtextxy(415, 310, str); outtextxy(405, 375, _T("等級(jí):")); //等級(jí) //按指定格式,將rank寫(xiě)入str sprintf_s(str, 16, "%d", rank); //這里設(shè)置字符集為多字符,保證outtextxy可以寫(xiě)出變量str outtextxy(415, 405, str); setcolor(LIGHTBLUE); //操作說(shuō)明 outtextxy(390, 475, "操作說(shuō)明:"); outtextxy(390, 500, "↑: 旋轉(zhuǎn)"); outtextxy(390, 525, "↓: 下降"); outtextxy(390, 550, "←: 左移"); outtextxy(390, 575, "→: 右移"); outtextxy(390, 600, "空格: 暫停"); } /***************************************** * 功能:清空方塊提示框里的方塊 * 輸入: * 無(wú) * 返回: * 無(wú) ****************************************/ void clearBlock() { setcolor(BLACK); setfont(23, 0, "楷體"); for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { int x = 391 + j * UNIT_SIZE; int y = 71 + i * UNIT_SIZE; outtextxy(x, y, "■"); } } } /***************************************** * 功能:清除降落過(guò)程中的方塊 * 輸入: * x,y - 方塊的坐標(biāo)(二維數(shù)組左上角位置) * block_dir_t - 方塊方向 * 返回: * 無(wú) ****************************************/ void clearBlock(int x, int y, block_dir_t blockDir) { setcolor(BLACK); // setfont(23, 0, "楷體"); int id = BlockIndex * 4 + blockDir; for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { if (block[id][i][j] == 1) { int x2 = x + j * UNIT_SIZE; int y2 = y + i * UNIT_SIZE; outtextxy(x2, y2, "■"); } } } } /***************************************** * 功能:在提示框 與 降落框的起始位置畫(huà)方塊 * 輸入: * x,y - 方塊的坐標(biāo)(二維數(shù)組左上角位置) * 返回: * 無(wú) ****************************************/ void drawBlock(int x, int y) { setcolor(color[NextIndex]); setfont(23, 0, "楷體"); for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { if (block[NextIndex * 4][i][j] == 1) { int x2 = x + j * UNIT_SIZE; int y2 = y + i * UNIT_SIZE; outtextxy(x2, y2, "■"); } } } } /***************************************** *功能:繪制下降過(guò)程中的方塊 *輸入: * x,y - 方塊的坐標(biāo)(二維數(shù)組左上角位置) * block_dir_t - 方塊方向 * 返回: * 無(wú) ****************************************/ void drawBlock(int x, int y, block_dir_t dir) { setcolor(color[BlockIndex]); setfont(23, 0, "楷體"); int id = BlockIndex * 4 + dir; for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { if (block[id][i][j] == 1) { //擦除該方塊的第i行第j列 outtextxy(x + j * UNIT_SIZE, y + i * UNIT_SIZE, "■"); } } } } /***************************************** *功能:方塊提示框中產(chǎn)生新方塊 *輸入: * 無(wú) *返回: * 無(wú) ****************************************/ void nextblock() { clearBlock(); //產(chǎn)生隨機(jī)數(shù),隨機(jī)選擇方塊 srand((unsigned)time(NULL)); //使用時(shí)間函數(shù)的返回值,來(lái)作為隨機(jī)種子 NextIndex = rand() % BLOCK_COUNT; //產(chǎn)生0~5的隨機(jī)數(shù) drawBlock(391, 71); } /***************************************** *功能:判斷在指定位置向指定方向是否可以移動(dòng) *輸入: * x,y - 方塊位置 * moveDir - 下一步想要移動(dòng)的方向 * blockDir - 當(dāng)前方塊的方向 * 返回: * true - 可以移動(dòng) * false - 不可以移動(dòng) ****************************************/ bool moveable(int x0, int y0, move_dir_t moveDir, block_dir_t blockDir) { //計(jì)算方塊左上角在30×15的游戲區(qū)位置(第多少行, 第多少列) int x = (y0 - MinY) / UNIT_SIZE; int y = (x0 - MinX) / UNIT_SIZE; int ret = 1; int id = BlockIndex * 4 + blockDir; if (moveDir == MOVE_DOWN) { for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { //向下不能運(yùn)動(dòng)的條件:實(shí)心方塊已經(jīng)達(dá)到底部(x+i+1==30),或者底部已有方塊 if (block[id][i][j] == 1 && (x + i + 1 == 30 || visit[x + i + 1][y + j] == 1)) { ret = 0; } } } } else if (moveDir == MOVE_LEFT) { for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { //向左不能運(yùn)動(dòng)的條件:實(shí)心方塊已經(jīng)達(dá)到左邊界(y+j==0),或者左邊已有方塊 if (block[id][i][j] == 1 && (y + j <= 0 || visit[x + i][y + j - 1] == 1)) { ret = 0; } } } } else if (moveDir == MOVE_RIGHT) { for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { //向下不能運(yùn)動(dòng)的條件:實(shí)心方塊已經(jīng)達(dá)到右邊界(y+j+1>=15),或者右邊已有方塊 if (block[id][i][j] == 1 && (y + j + 1 >= 15 || visit[x + i][y + j + 1] == 1)) { ret = 0; } } } } return ret; } /***************************** *功能:檢測(cè)游戲是否結(jié)束 *輸入: * 無(wú) * 返回: * 無(wú) *****************************/ void failCheck() { //游戲結(jié)束條件是頂部新被繪制出的方塊就要“固化”,頂部新繪制的方塊方向朝上,運(yùn)動(dòng)方向朝下 if (!moveable(START_X, START_Y, MOVE_DOWN, (block_dir_t)BLOCK_UP)) { setcolor(WHITE); setfont(45, 0, "隸體"); outtextxy(75, 300, "Game Over!"); Sleep(1000); system("pause"); closegraph(); exit(0); } } /************************** * 功能:延時(shí)等待 * 輸入: * * 返回: * 無(wú) *************************/ void wait(int interval) { int count = interval / 10; for (int i = 0; i < count; ++i) { Sleep(10); //如果休眠期間用戶(hù)有按鍵,則結(jié)束休眠 if (_kbhit()) { return; } } } /***************************************** * 功能:判斷當(dāng)前方塊是否可以向指定方向旋轉(zhuǎn) * 輸入: * x,y - 方塊位置(二維數(shù)組坐標(biāo)) * dir - 方塊旋轉(zhuǎn)方向 * 返回: * true - 可以旋轉(zhuǎn) * false - 不可以旋轉(zhuǎn) ****************************************/ bool rotatable(int x, int y, block_dir_t dir) { //首先判斷是否可以繼續(xù)向下移動(dòng) if (!moveable(x, y, MOVE_DOWN, dir)) { return false; } int x2 = (y - MinY) / UNIT_SIZE; int y2 = (x - MinX) / UNIT_SIZE; int id = BlockIndex * 4 + dir; for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { //不能旋轉(zhuǎn)條件:左右邊界越界或者已有方塊“阻擋” if (block[id][i][j] == 1 && (y2 + j < 0 || y2 + j >= 15 || visit[x2 + i][y2 + j] == 1)) { return false; } } } return true; } /***************************************** * 功能: * 輸入: * * 返回: * 無(wú) ****************************************/ void mark(int x, int y, block_dir_t dir) { int id = BlockIndex * 4 + dir; int x2 = (y - MinY) / UNIT_SIZE; int y2 = (x - MinX) / UNIT_SIZE; for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { if (block[id][i][j] == 1) { visit[x2 + i][y2 + j] = 1; markColor[x2 + i][y2 + j] = color[BlockIndex]; } } } } /***************************************** * 功能:讀取用戶(hù)操作,時(shí)時(shí)更新降落的方塊 * 輸入: * 無(wú) * 返回: * 無(wú) ****************************************/ void move() { int x = START_X; //方塊起始位置 int y = START_Y; int k = 0; block_dir_t blockDir = (block_dir_t)BLOCK_UP; int curSpeed = speed; //定義當(dāng)前方塊降落速度 //讀取用戶(hù)操作前判斷游戲是否結(jié)束 failCheck(); //持續(xù)向下降落 while (1) { int curSpeed = speed; //定義當(dāng)前方塊降落速度 //清除方塊 clearBlock(x, k + y, blockDir); //判斷選擇的方向 if (_kbhit()) { int key = _getch(); if (key == KEY_SPACE) { system("pause"); } else if (key == KEY_UP) { block_dir_t nextDir = (block_dir_t)((blockDir + 1) % 4); if (rotatable(x, y + k, nextDir)) { blockDir = nextDir; } } else if (key == KEY_LEFT) { if (moveable(x, y + k + 20, MOVE_LEFT, blockDir)) { x -= UNIT_SIZE; } } else if (key == KEY_RIGHT) { if (moveable(x, y + k + 20, MOVE_RIGHT, blockDir)) { x += UNIT_SIZE; } } else if (key == KEY_DOWN) { curSpeed = 50; } } k += 20; //繪制方塊 drawBlock(x, y + k, blockDir); //休眠 wait(curSpeed); //方塊的固化處理,方塊固定后結(jié)束循環(huán),當(dāng)前一個(gè)方塊的move執(zhí)行完畢 if (!moveable(x, y + k, MOVE_DOWN, blockDir)) { mark(x, y + k, blockDir); break; } } } /***************************************** *功能:繪制剛從頂部降落的方塊,更新提示框內(nèi)的方塊,調(diào)用方塊降落函數(shù)move() *輸入: * 無(wú) * 返回: * 無(wú) ****************************************/ void newblock() { BlockIndex = NextIndex; //繪制剛從頂部下降的方塊 drawBlock(START_X, START_Y); //讓新出現(xiàn)方塊暫停一會(huì) Sleep(200); //右上角區(qū)域繪制下一個(gè)方塊 nextblock(); //方塊降落 move(); } /***************************************** * 功能:消除第i行,并把上面的行都往下移 * 輸入: * 無(wú) * 返回: * 無(wú) ****************************************/ void down(int x) { for (int i = x; i > 0; --i) { for (int j = 0; j < 15; ++j) { if (visit[i - 1][j] == 1) { visit[i][j] = 1; markColor[i][j] = markColor[i - 1][j]; setcolor(markColor[i][j]); outtextxy(20 * j + MinX, 20 * i + MinY, "■"); } else { visit[i][j] = 0; setcolor(BLACK); outtextxy(20 * j + MinX, 20 * i + MinY, "■"); } } } //清除最頂層方格 setcolor(BLACK); for (int j = 0; j < 15; ++j) { visit[0][j] = 0; outtextxy(20 * j + MinX, MinY, "■"); } } /***************************************** * 功能:更新分?jǐn)?shù) * 輸入: * 無(wú) * 返回: * 無(wú) ****************************************/ void addScore(int lines) { char str[32]; score += lines * 10; sprintf_s(str, 32, "%d", score); setcolor(RED); outtextxy(415, 310, str); } /************************* * 功能:更新等級(jí) * 輸入: * 無(wú) * 返回: * 無(wú) *************************/ void updateGrade() { //更新等級(jí) //假設(shè)50分一級(jí) rank = score / 50; char str[32]; sprintf_s(str, 32, "%d", rank); setcolor(RED); outtextxy(415, 405, str); //更新速度 if (speed <= 100) { speed = 100; } else { speed = 500 - rank * 20; } } /************************* * 功能:檢查是否有滿(mǎn)行方塊 * 輸入: * 無(wú) * 返回: * 無(wú) *************************/ void check() { int i, j; int clearLines = 0; for (i = 29; i >= 0; i--) { // 檢查第i行有沒(méi)有滿(mǎn) for (j = 0; j < 15 && visit[i][j]; j++); //執(zhí)行到此處時(shí),有兩種情況: // 1. 第i行沒(méi)有滿(mǎn),即表示有空位 此時(shí) j<15 // 2. 第i行已滿(mǎn)了,此時(shí) j>=15 if (j >= 15) { // 此時(shí),第i行已經(jīng)滿(mǎn)了,就需要消除第i行 down(i); //消除第i行,并把上面的行都下移 i++; // 因?yàn)樽钔鈱拥难h(huán)中有 i--, 所以我們先i++, 使得下次循環(huán)時(shí),再把這一行檢查一下 clearLines++; } } // 更新分?jǐn)?shù) addScore(clearLines); // 更新等級(jí)(更新等級(jí)提示,更新速度) updateGrade(); } int main() { welcome(); initGameSceen(); //產(chǎn)生新方塊 nextblock(); // system("pause"); Sleep(800); //初始化訪(fǎng)問(wèn)數(shù)組 memset(visit, 0, sizeof(visit)); while (1) { newblock(); //消除滿(mǎn)行,并更新分?jǐn)?shù)和速度 check(); } system("pause"); closegraph(); return 0; }
三、所需開(kāi)發(fā)環(huán)境
1)安裝VS2019,或VS其他版本
2)安裝easyX圖形庫(kù)
四、具體項(xiàng)目實(shí)現(xiàn)
①游戲歡迎界面 welcome( )
在游戲開(kāi)始前會(huì)有一個(gè)游戲歡迎頁(yè)面,整個(gè)頁(yè)面會(huì)持續(xù)大概三秒,之后便進(jìn)入游戲場(chǎng)景。在游戲歡迎頁(yè)面中,除了將顯示游戲名稱(chēng)外,還要修改窗口標(biāo)題。
/*************************** * 功能:歡迎頁(yè)面 * 輸入: * 無(wú) * 返回: * 無(wú) **************************/ void welcome() { //1.初始化畫(huà)布 initgraph(550, 660); //2.設(shè)置窗口標(biāo)題 HWND window = GetHWnd();//獲得窗口,獲得當(dāng)前窗口 SetWindowText(window, _T("俄羅斯方塊 小明來(lái)嘍")); //設(shè)置標(biāo)題 //3.設(shè)置游戲初始頁(yè)面 setfont(40, 0, _T("微軟雅黑")); //設(shè)置文本的字體樣式(高,寬(0表示自適應(yīng)),字體) setcolor(WHITE); // 設(shè)置顏色 outtextxy(205, 200, _T("俄羅斯方法")); setfont(20, 0, _T("楷體")); setcolor(WHITE); // 設(shè)置顏色 outtextxy(175, 300, _T("編程,從俄羅斯方塊開(kāi)始")); Sleep(3000); }
②游戲背景 initGameScreen( )
繪制游戲場(chǎng)景
/*************************** * 功能:初始化游戲場(chǎng)景 * 輸入: * 無(wú) * 返回: * 無(wú) **************************/ void initGameSceen() { char str[16]; //存放分?jǐn)?shù) //1.清屏 cleardevice(); //2.畫(huà)場(chǎng)景 rectangle(27, 27, 336, 635); //方塊降落框外框 rectangle(29, 29, 334, 633); //方塊降落框內(nèi)框 rectangle(370, 50, 515, 195); //方塊提示框 setfont(24, 0, _T("楷體")); //寫(xiě)“下一個(gè)” setcolor(LIGHTGRAY); //灰色 outtextxy(405, 215, _T("下一個(gè):")); setcolor(RED); //寫(xiě)分?jǐn)?shù) outtextxy(405, 280, _T("分?jǐn)?shù):")); //按指定格式,將score寫(xiě)入str sprintf_s(str, 16, "%d", score); //這里設(shè)置字符集為多字符,保證outtextxy可以寫(xiě)出變量str outtextxy(415, 310, str); outtextxy(405, 375, _T("等級(jí):")); //等級(jí) //按指定格式,將rank寫(xiě)入str sprintf_s(str, 16, "%d", rank); //這里設(shè)置字符集為多字符,保證outtextxy可以寫(xiě)出變量str outtextxy(415, 405, str); setcolor(LIGHTBLUE); //操作說(shuō)明 outtextxy(390, 475, "操作說(shuō)明:"); outtextxy(390, 500, "W: 旋轉(zhuǎn)"); outtextxy(390, 525, "S: 下降"); outtextxy(390, 550, "A: 左移"); outtextxy(390, 575, "D: 右移"); outtextxy(390, 600, "空格: 暫停"); system("pause"); }
③方塊表示 int block[ ][ ][ ]
將每個(gè)方塊的朝向用二維數(shù)組表示。此次共設(shè)計(jì)了五種方塊,每個(gè)方塊四種朝向,使用三維數(shù)組存儲(chǔ)每個(gè)剛快及其朝向。1代表該點(diǎn)是方格。
#define BLOCK_COUNT 5 #define BLOCK_WIDTH 5 #define BLOCK_HEIGHT 5 int block[BLOCK_COUNT * 4][BLOCK_WIDTH][BLOCK_HEIGHT] = { // | 形方塊 { 0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 }, // L 形方塊 { 0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,1,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,1,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 }, // 田 形方塊 { 0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 }, // T 形方塊 { 0,0,0,0,0,0,1,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0 }, // Z 形方塊 { 0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0 }, };
④新方塊表示nextBlock( )
在方塊提示框中每次生成新方塊由兩個(gè)動(dòng)作組成,首先是擦除方塊,接著是繪制新方塊。
/***************************************** * 功能:清空方塊提示框里的方塊 * 輸入: * 無(wú) * 返回: * 無(wú) ****************************************/ void clearBlock() { setcolor(BLACK); setfont(23, 0, "楷體"); for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { int x = 391 + j * UNIT_SIZE; int y = 71 + i * UNIT_SIZE; outtextxy(x, y, "■"); } } } /***************************************** * 功能:在提示框 與 降落框的起始位置畫(huà)方塊 * 輸入: * x,y - 方塊的坐標(biāo)(二維數(shù)組左上角位置) * 返回: * 無(wú) ****************************************/ void drawBlock(int x, int y) { setcolor(color[NextIndex]); setfont(23, 0, "楷體"); for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { if (block[NextIndex * 4][i][j] == 1) { int x2 = x + j * UNIT_SIZE; int y2 = y + i * UNIT_SIZE; outtextxy(x2, y2, "■"); } } } } /***************************************** *功能:方塊提示框中產(chǎn)生新方塊 *輸入: * 無(wú) *返回: * 無(wú) ****************************************/ void nextblock() { clearBlock(); //產(chǎn)生隨機(jī)數(shù),隨機(jī)選擇方塊 srand((unsigned)time(NULL)); //使用時(shí)間函數(shù)的返回值,來(lái)作為隨機(jī)種子 NextIndex = rand() % BLOCK_COUNT; //產(chǎn)生0~5的隨機(jī)數(shù) drawBlock(391, 71); }
⑤設(shè)計(jì)游戲循環(huán)main( )
在游戲框中每次生成新方塊會(huì)進(jìn)入對(duì)新方塊降落處理,等處理完后便會(huì)循環(huán)
int main() { welcome(); initGameSceen(); //產(chǎn)生新方塊 nextblock(); Sleep(800); //初始化訪(fǎng)問(wèn)數(shù)組 memset(visit, 0, sizeof(visit)); while (1) { newblock(); } system("pause"); closegraph(); return 0; } /***************************************** *功能:繪制剛從頂部降落的方塊,更新提示框內(nèi)的方塊,調(diào)用方塊降落函數(shù)move() *輸入: * 無(wú) * 返回: * 無(wú) ****************************************/ void newblock() { BlockIndex = NextIndex; //繪制剛從頂部下降的方塊 drawBlock(START_X, START_Y); //讓新出現(xiàn)方塊暫停一會(huì) Sleep(200); //右上角區(qū)域繪制下一個(gè)方塊 nextblock(); //方塊降落 move(); }
⑥搭建用戶(hù)操作框架move( )Ⅰ
用戶(hù)操作框架:斷游戲是否結(jié)束 → 擦除當(dāng)前方塊 → 用戶(hù)按鍵操作 → 繪制新的方塊 → 延時(shí)等待 → 方塊是否要固化(固化則則表明對(duì)當(dāng)前方塊操作結(jié)束)。
#define KEY_UP 87 //用戶(hù)操作 #define KEY_LEFT 65 #define KEY_RIGHT 68 #define KEY_DOWN 83 #define KEY_SPACE 32 /***************************************** * 功能:讀取用戶(hù)操作,時(shí)時(shí)更新降落的方塊 * 輸入: * 無(wú) * 返回: * 無(wú) ****************************************/ void move() { //讀取用戶(hù)操作前判斷游戲是否結(jié)束 failCheck(); //持續(xù)向下降落 while (1) { //清除方塊 //to do //判斷選擇的方向 if (_kbhit()) { int key = _getch(); if (key == KEY_SPACE) { //to do } else if (key == KEY_UP) { //to do } else if (key == KEY_LEFT) { //to do } else if (key == KEY_RIGHT) { //to do } else if (key == KEY_DOWN) { //to do } } //繪制方塊 //to do //休眠 //to do //方塊的固化處理,方塊固定后結(jié)束循環(huán),當(dāng)前一個(gè)方塊的move執(zhí)行完畢 //to do } }
⑦判斷方塊能否向指定方向移動(dòng) moveable( )
當(dāng)新方塊剛從頂部繪制時(shí)就碰到了“固化”方塊時(shí)則表明游戲結(jié)束,因此我們只需判斷方塊能否向下移動(dòng)即可。這里先實(shí)現(xiàn)判斷方塊能否向指定方向移動(dòng)功能。
/***************************************** *功能:判斷在指定位置向指定方向是否可以移動(dòng) *輸入: * x,y - 方塊位置 * moveDir - 下一步想要移動(dòng)的方向 * blockDir - 當(dāng)前方塊的方向 * 返回: * true - 可以移動(dòng) * false - 不可以移動(dòng) ****************************************/ bool moveable(int x0, int y0, move_dir_t moveDir, block_dir_t blockDir) { //計(jì)算方塊左上角在30×15的游戲區(qū)位置(第多少行, 第多少列) int x = (y0 - MinY) / UNIT_SIZE; int y = (x0 - MinX) / UNIT_SIZE; int ret = 1; int id = BlockIndex * 4 + blockDir; if (moveDir == MOVE_DOWN) { for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { //向下不能運(yùn)動(dòng)的條件:實(shí)心方塊已經(jīng)達(dá)到底部(x+i+1==30),或者底部已有方塊 if (block[id][i][j] == 1 && (x + i + 1 == 30 || visit[x + i + 1][y + j] == 1)) { ret = 0; } } } } else if (moveDir == MOVE_LEFT) { for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { //向左不能運(yùn)動(dòng)的條件:實(shí)心方塊已經(jīng)達(dá)到左邊界(y+j==0),或者左邊已有方塊 if (block[id][i][j] == 1 && (y + j <= 0 || visit[x + i][y + j - 1] == 1)) { ret = 0; } } } } else if (moveDir == MOVE_RIGHT) { for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { //向下不能運(yùn)動(dòng)的條件:實(shí)心方塊已經(jīng)達(dá)到右邊界(y+j+1>=15),或者右邊已有方塊 if (block[id][i][j] == 1 && (y + j + 1 >= 15 || visit[x + i][y + j + 1] == 1)) { ret = 0; } } } } return ret; }
⑧游戲失敗檢查 failCheck( )
游戲失敗檢測(cè),當(dāng)新繪制方塊無(wú)法向下移動(dòng)時(shí)則表明游戲失敗。
/***************************** *功能:檢測(cè)游戲是否結(jié)束 *輸入: * 無(wú) * 返回: * 無(wú) *****************************/ void failCheck() { //游戲結(jié)束條件是頂部新被繪制出的方塊就要“固化”,頂部新繪制的方塊方向朝上,運(yùn)動(dòng)方向朝下 if (!moveable(START_X, START_Y, MOVE_DOWN, (block_dir_t)BLOCK_UP)) { setcolor(WHITE); setfont(45, 0, "隸體"); outtextxy(75, 300, "Game Over!"); Sleep(1000); system("pause"); closegraph(); exit(0); } }
⑨清除下降過(guò)程中的方塊 clearBlock( )
如果游戲未失敗,則表明用戶(hù)可以繼續(xù)操作,在讀取用戶(hù)操作前要先將降落框內(nèi)的方塊清除。
/***************************************** * 功能:清除降落過(guò)程中的方塊 * 輸入: * x,y - 方塊的坐標(biāo)(二維數(shù)組左上角位置) * block_dir_t - 方塊方向 * 返回: * 無(wú) ****************************************/ void clearBlock(int x, int y, block_dir_t blockDir) { setcolor(BLACK); // setfont(23, 0, "楷體"); int id = BlockIndex * 4 + blockDir; for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { if (block[id][i][j] == 1) { int x2 = x + j * UNIT_SIZE; int y2 = y + i * UNIT_SIZE; outtextxy(x2, y2, "■"); } } } }
⑩判斷方塊旋轉(zhuǎn) rotatable( )
如果方塊在待轉(zhuǎn)方向可以向下運(yùn)動(dòng)則表明方塊可以旋轉(zhuǎn),因此這里只需少加利用moveable函數(shù)即可實(shí)現(xiàn)。
/***************************************** * 功能:判斷當(dāng)前方塊是否可以向指定方向旋轉(zhuǎn) * 輸入: * x,y - 方塊位置(二維數(shù)組坐標(biāo)) * dir - 方塊旋轉(zhuǎn)方向 * 返回: * true - 可以旋轉(zhuǎn) * false - 不可以旋轉(zhuǎn) ****************************************/ bool rotatable(int x, int y, block_dir_t dir) { //首先判斷是否可以繼續(xù)向下移動(dòng) if (!moveable(x, y, MOVE_DOWN, dir)) { return false; } int x2 = (y - MinY) / UNIT_SIZE; int y2 = (x - MinX) / UNIT_SIZE; int id = BlockIndex * 4 + dir; for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { //不能旋轉(zhuǎn)條件:左右邊界越界或者已有方塊“阻擋” if (block[id][i][j] == 1 && (y2 + j < 0 || y2 + j >= 15 || visit[x2 + i][y2 + j] == 1)) { return false; } } } return true; }
①①繪制下降過(guò)程中的方塊 drawBlock( )
每次根據(jù)用戶(hù)操作繪制新的方塊
/***************************************** *功能:繪制下降過(guò)程中的方塊 *輸入: * x,y - 方塊的坐標(biāo)(二維數(shù)組左上角位置) * block_dir_t - 方塊方向 * 返回: * 無(wú) ****************************************/ void drawBlock(int x, int y, block_dir_t dir) { setcolor(color[BlockIndex]); setfont(23, 0, "楷體"); int id = BlockIndex * 4 + dir; for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { if (block[id][i][j] == 1) { //擦除該方塊的第i行第j列 outtextxy(x + j * UNIT_SIZE, y + i * UNIT_SIZE, "■"); } } } }
①②延時(shí)等待 wait ( )
每次 處理完用戶(hù)操作后會(huì)進(jìn)入延時(shí)等待,等待時(shí)長(zhǎng)會(huì)根據(jù)當(dāng)前方塊降落速度而定,在延時(shí)等待期間如果檢測(cè)到用戶(hù)有按鍵操作時(shí)則會(huì)結(jié)束等待。
/************************** * 功能:延時(shí)等待 * 輸入: * * 返回: * 無(wú) *************************/ void wait(int interval) { int count = interval / 10; for (int i = 0; i < count; ++i) { Sleep(10); //如果休眠期間用戶(hù)有按鍵,則結(jié)束休眠 if (_kbhit()) { return; } } }
①③固定方塊 mark( )
每次繪制出新方塊后判斷方塊是否還能繼續(xù)移動(dòng),如果不能移動(dòng)則表明方塊需要固化。
/***************************************** * 功能:方塊固定 * 輸入: * x,y - 方塊坐標(biāo) * dir - 方塊朝向 * 返回: * 無(wú) ****************************************/ void mark(int x, int y, block_dir_t dir) { int id = BlockIndex * 4 + dir; int x2 = (y - MinY) / UNIT_SIZE; int y2 = (x - MinX) / UNIT_SIZE; for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { if (block[id][i][j] == 1) { visit[x2 + i][y2 + j] = 1; markColor[x2 + i][y2 + j] = color[BlockIndex]; } } } }
①④用戶(hù)操作框架完善Ⅱ mov( )
將上述實(shí)現(xiàn)功能補(bǔ)充到操作框架中
void move() { int x = START_X; //方塊起始位置 int y = START_Y; int k = 0; block_dir_t blockDir = (block_dir_t)BLOCK_UP; int curSpeed = speed; //定義當(dāng)前方塊降落速度 //讀取用戶(hù)操作前判斷游戲是否結(jié)束 failCheck(); //持續(xù)向下降落 while (1) { int curSpeed = speed; //定義當(dāng)前方塊降落速度 //清除方塊 clearBlock(x, k + y, blockDir); //判斷選擇的方向 if (_kbhit()) { int key = _getch(); if (key == KEY_SPACE) { system("pause"); } else if (key == KEY_UP) { block_dir_t nextDir = (block_dir_t)((blockDir + 1) % 4); if (rotatable(x, y + k, nextDir)) { blockDir = nextDir; } } else if (key == KEY_LEFT) { if (moveable(x, y + k + 20, MOVE_LEFT, blockDir)) { x -= UNIT_SIZE; } } else if (key == KEY_RIGHT) { if (moveable(x, y + k + 20, MOVE_RIGHT, blockDir)) { x += UNIT_SIZE; } } else if (key == KEY_DOWN) { curSpeed = 50; } } k += 20; //繪制方塊 drawBlock(x, y + k, blockDir); //休眠 wait(curSpeed); //方塊的固化處理,方塊固定后結(jié)束循環(huán),當(dāng)前一個(gè)方塊的move執(zhí)行完畢 if (!moveable(x, y + k, MOVE_DOWN, blockDir)) { mark(x, y + k, blockDir); break; } } }
①⑤消除方塊 check( ) + down( )
當(dāng)對(duì)一個(gè)方塊下降操作結(jié)束后,在已固化方塊數(shù)組里查找“滿(mǎn)行”方塊,如果存在“滿(mǎn)行”方塊則要進(jìn)行清除操作,接著更新用戶(hù)分?jǐn)?shù)和等級(jí)。
/************************ * 功能:檢查是否有滿(mǎn)行方塊 * 輸入: * 無(wú) * 返回: * 無(wú) *************************/ void check() { int i, j; int clearLines = 0; for (i = 29; i >= 0; i--) { // 檢查第i行有沒(méi)有滿(mǎn) for (j = 0; j < 15 && visit[i][j]; j++); //執(zhí)行到此處時(shí),有兩種情況: // 1. 第i行沒(méi)有滿(mǎn),即表示有空位 此時(shí) j<15 // 2. 第i行已滿(mǎn)了,此時(shí) j>=15 if (j >= 15) { // 此時(shí),第i行已經(jīng)滿(mǎn)了,就需要消除第i行 down(i); //消除第i行,并把上面的行都下移 i++; // 因?yàn)樽钔鈱拥难h(huán)中有 i--, 所以我們先i++, 使得下次循環(huán)時(shí),再把這一行檢查一下 clearLines++; } } // 更新分?jǐn)?shù) addScore(clearLines); // 更新等級(jí)(更新等級(jí)提示,更新速度) updateGrade(); } /***************************************** * 功能:消除第i行,并把上面的行都往下移 * 輸入: * 無(wú) * 返回: * 無(wú) ****************************************/ void down(int x) { for (int i = x; i > 0; --i) { for (int j = 0; j < 15; ++j) { if (visit[i - 1][j] == 1) { visit[i][j] = 1; markColor[i][j] = markColor[i - 1][j]; setcolor(markColor[i][j]); outtextxy(20 * j + MinX, 20 * i + MinY, "■"); } else { visit[i][j] = 0; setcolor(BLACK); outtextxy(20 * j + MinX, 20 * i + MinY, "■"); } } } //清除最頂層方格 setcolor(BLACK); for (int j = 0; j < 15; ++j) { visit[0][j] = 0; outtextxy(20 * j + MinX, MinY, "■"); } }
①⑥更新分?jǐn)?shù)和等級(jí) addScore( ) + updateGrade( )
根據(jù)清除方塊行數(shù)更新用戶(hù)分?jǐn)?shù)和等級(jí)。
/***************************************** * 功能:更新分?jǐn)?shù) * 輸入: * 無(wú) * 返回: * 無(wú) ****************************************/ void addScore(int lines) { char str[32]; score += lines * 10; sprintf_s(str, 32, "%d", score); setcolor(RED); outtextxy(415, 310, str); } /************************* * 功能:更新等級(jí) * 輸入: * 無(wú) * 返回: * 無(wú) *************************/ void updateGrade() { //更新等級(jí) //假設(shè)50分一級(jí) rank = score / 50; char str[32]; sprintf_s(str, 32, "%d", rank); setcolor(RED); outtextxy(415, 405, str); //更新速度 if (speed <= 100) { speed = 100; } else { speed = 500 - rank * 20; } }
代碼整合運(yùn)行
五 、不足之處
使用easyX繪圖,導(dǎo)入游戲圖片,從而使得游戲效果更為逼真
游戲戰(zhàn)績(jī)的保存
操作控制略有卡頓
以上就是C/C++實(shí)現(xiàn)俄羅斯方塊游戲的詳細(xì)內(nèi)容,更多關(guān)于C/C++ 俄羅斯方塊的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C++?實(shí)現(xiàn)單鏈表創(chuàng)建、插入和刪除
這篇文章主要介紹了C++?實(shí)現(xiàn)單鏈表創(chuàng)建、插入和刪除方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07C語(yǔ)言的字符函數(shù)和字符串函數(shù)詳解
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言的字符函數(shù)和字符串函數(shù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-03-03約瑟夫環(huán)問(wèn)題(數(shù)組法)c語(yǔ)言實(shí)現(xiàn)
這篇文章主要介紹了約瑟夫環(huán)問(wèn)題(數(shù)組法)c語(yǔ)言實(shí)現(xiàn),有需要的朋友可以參考一下2013-12-12Visual?Studio2022報(bào)錯(cuò)無(wú)法打開(kāi)源文件?"openssl/conf.h"解決方法
這篇文章主要介紹了Visual?Studio2022報(bào)錯(cuò)無(wú)法打開(kāi)源文件"openssl/conf.h"解決方式,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07C++11智能指針中的 unique_ptr實(shí)例詳解
unique是獨(dú)特的、唯一的意思,故名思議,unique_ptr可以“獨(dú)占”地?fù)碛兴赶虻膶?duì)象,它提供一種嚴(yán)格意義上的所有權(quán)。這篇文章主要介紹了C++11智能指針中的 unique_ptr實(shí)例詳解,需要的朋友可以參考下2020-06-06