每天一個(gè)C語(yǔ)言小項(xiàng)目,提升你的編程能力!
俄羅斯方塊是童年的經(jīng)典游戲~~
由小方塊組成的不同形狀的板塊陸續(xù)從屏幕上方落下來(lái),玩家通過(guò)調(diào)整板塊的位置和方向,使它們?cè)谄聊坏撞科闯鐾暾囊粭l或幾條。這些完整的橫條會(huì)隨即消失,給新落下來(lái)的板塊騰出空間,與此同時(shí),玩家得到分?jǐn)?shù)獎(jiǎng)勵(lì)。沒(méi)有被消除掉的方塊不斷堆積起來(lái),一旦堆到屏幕頂端,玩家便告輸,游戲結(jié)束。
本次用C語(yǔ)言來(lái)實(shí)現(xiàn)!這個(gè)程序在界面上沒(méi)有做額外的修飾,重點(diǎn)放在游戲手感和消息處理過(guò)程。游戲手感上做了一些細(xì)節(jié)處理,仔細(xì)體驗(yàn)感受一下。
效果如圖:
其中也是加入了按鍵操作功能:"A"左移一格;"D"右移一格;"W"旋轉(zhuǎn)方塊;S 下移一格;空格鍵讓方塊下落到底),"ESC"健退出游戲,用戶(hù)還可以自己根據(jù)自定義習(xí)慣的按鍵來(lái)操作游戲。
編譯環(huán)境:Visual Studio 2019/2022,EasyX插件
代碼展示:
#include#include #include #include ///////////////////////////////////////////// // 定義常量、枚舉量、結(jié)構(gòu)體、全局變量 ///////////////////////////////////////////// #define WIDTH 10 // 游戲區(qū)寬度 #define HEIGHT 22 // 游戲區(qū)高度 #define UNIT 20 // 每個(gè)游戲區(qū)單位的實(shí)際像素 // 定義操作類(lèi)型 enum CMD { CMD_ROTATE, // 方塊旋轉(zhuǎn) CMD_LEFT, CMD_RIGHT, CMD_DOWN, // 方塊左、右、下移動(dòng) CMD_SINK, // 方塊沉底 CMD_QUIT // 退出游戲 }; // 定義繪制方塊的方法 enum DRAW { SHOW, // 顯示方塊 CLEAR, // 擦除方塊 FIX // 固定方塊 }; // 定義七種俄羅斯方塊 struct BLOCK { WORD dir[4]; // 方塊的四個(gè)旋轉(zhuǎn)狀態(tài) COLORREF color; // 方塊的顏色 }; BLOCK g_Blocks[7] = { {0x0F00, 0x4444, 0x0F00, 0x4444, RED}, // I {0x0660, 0x0660, 0x0660, 0x0660, BLUE}, // 口 {0x4460, 0x02E0, 0x0622, 0x0740, MAGENTA}, // L {0x2260, 0x0E20, 0x0644, 0x0470, YELLOW}, // 反 L {0x0C60, 0x2640, 0x0C60, 0x2640, CYAN}, // Z {0x0360, 0x4620, 0x0360, 0x4620, GREEN}, // 反 Z {0x4E00, 0x4C40, 0x0E40, 0x4640, BROWN}}; // T // 定義當(dāng)前方塊、下一個(gè)方塊的信息 struct BLOCKINFO { byte id; // 方塊 ID char x, y; // 方塊在游戲區(qū)中的坐標(biāo) byte dir:2; // 方向 } g_CurBlock, g_NextBlock; // 定義游戲區(qū) BYTE g_World[WIDTH][HEIGHT] = {0}; ///////////////////////////////////////////// // 函數(shù)聲明 ///////////////////////////////////////////// void Init(); // 初始化游戲 void Quit(); // 退出游戲 void NewGame(); // 開(kāi)始新游戲 void GameOver(); // 結(jié)束游戲 CMD GetCmd(); // 獲取控制命令 void DispatchCmd(CMD _cmd); // 分發(fā)控制命令 void NewBlock(); // 生成新的方塊 bool CheckBlock(BLOCKINFO _block); // 檢測(cè)指定方塊是否可以放下 void DrawUnit(int x, int y, COLORREF c, DRAW _draw); // 畫(huà)單元方塊 void DrawBlock(BLOCKINFO _block, DRAW _draw = SHOW); // 畫(huà)方塊 void OnRotate(); // 旋轉(zhuǎn)方塊 void OnLeft(); // 左移方塊 void OnRight(); // 右移方塊 void OnDown(); // 下移方塊 void OnSink(); // 沉底方塊 ///////////////////////////////////////////// // 函數(shù)定義 ///////////////////////////////////////////// // 主函數(shù) void main() { Init(); CMD c; while(true) { c = GetCmd(); DispatchCmd(c); // 按退出時(shí),顯示對(duì)話(huà)框咨詢(xún)用戶(hù)是否退出 if (c == CMD_QUIT) { HWND wnd = GetHWnd(); if (MessageBox(wnd, _T("您要退出游戲嗎?"), _T("提醒"), MB_OKCANCEL | MB_ICONQUESTION) == IDOK) Quit(); } } } // 初始化游戲 void Init() { initgraph(640, 480); srand((unsigned)time(NULL)); setbkmode(TRANSPARENT); // 設(shè)置圖案填充的背景色為透明 // 顯示操作說(shuō)明 settextstyle(14, 0, _T("宋體")); outtextxy(20, 330, _T("操作說(shuō)明")); outtextxy(20, 350, _T("上:旋轉(zhuǎn)")); outtextxy(20, 370, _T("左:左移")); outtextxy(20, 390, _T("右:右移")); outtextxy(20, 410, _T("下:下移")); outtextxy(20, 430, _T("空格:沉底")); outtextxy(20, 450, _T("ESC:退出")); // 設(shè)置坐標(biāo)原點(diǎn) setorigin(220, 20); // 繪制游戲區(qū)邊界 rectangle(-1, -1, WIDTH * UNIT, HEIGHT * UNIT); rectangle((WIDTH + 1) * UNIT - 1, -1, (WIDTH + 5) * UNIT, 4 * UNIT); // 開(kāi)始新游戲 NewGame(); } // 退出游戲 void Quit() { closegraph(); exit(0); } // 開(kāi)始新游戲 void NewGame() { // 清空游戲區(qū) setfillcolor(BLACK); solidrectangle(0, 0, WIDTH * UNIT - 1, HEIGHT * UNIT - 1); ZeroMemory(g_World, WIDTH * HEIGHT); // 生成下一個(gè)方塊 g_NextBlock.id = rand() % 7; g_NextBlock.dir = rand() % 4; g_NextBlock.x = WIDTH + 1; g_NextBlock.y = HEIGHT - 1; // 獲取新方塊 NewBlock(); } // 結(jié)束游戲 void GameOver() { HWND wnd = GetHWnd(); if (MessageBox(wnd, _T("游戲結(jié)束。 您想重新來(lái)一局嗎?"), _T("游戲結(jié)束"), MB_YESNO | MB_ICONQUESTION) == IDYES) NewGame(); else Quit(); } // 獲取控制命令 DWORD m_oldtime; CMD GetCmd() { // 獲取控制值 while(true) { // 如果超時(shí),自動(dòng)下落一格 DWORD newtime = GetTickCount(); if (newtime - m_oldtime >= 500) { m_oldtime = newtime; return CMD_DOWN; } // 如果有按鍵,返回按鍵對(duì)應(yīng)的功能 if (_kbhit()) { switch(_getch()) { case 'w': case 'W': return CMD_ROTATE; case 'a': case 'A': return CMD_LEFT; case 'd': case 'D': return CMD_RIGHT; case 's': case 'S': return CMD_DOWN; case 27: return CMD_QUIT; case ' ': return CMD_SINK; case 0: case 0xE0: switch(_getch()) { case 72: return CMD_ROTATE; case 75: return CMD_LEFT; case 77: return CMD_RIGHT; case 80: return CMD_DOWN; } } } // 延時(shí) (降低 CPU 占用率) Sleep(20); } } // 分發(fā)控制命令 void DispatchCmd(CMD _cmd) { switch(_cmd) { case CMD_ROTATE: OnRotate(); break; case CMD_LEFT: OnLeft(); break; case CMD_RIGHT: OnRight(); break; case CMD_DOWN: OnDown(); break; case CMD_SINK: OnSink(); break; case CMD_QUIT: break; } } // 生成新的方塊 void NewBlock() { g_CurBlock.id = g_NextBlock.id, g_NextBlock.id = rand() % 7; g_CurBlock.dir = g_NextBlock.dir, g_NextBlock.dir = rand() % 4; g_CurBlock.x = (WIDTH - 4) / 2; g_CurBlock.y = HEIGHT + 2; // 下移新方塊直到有局部顯示 WORD c = g_Blocks[g_CurBlock.id].dir[g_CurBlock.dir]; while((c & 0xF) == 0) { g_CurBlock.y--; c >>= 4; } // 繪制新方塊 DrawBlock(g_CurBlock); // 繪制下一個(gè)方塊 setfillcolor(BLACK); solidrectangle((WIDTH + 1) * UNIT, 0, (WIDTH + 5) * UNIT - 1, 4 * UNIT - 1); DrawBlock(g_NextBlock); // 設(shè)置計(jì)時(shí)器,用于判斷自動(dòng)下落 m_oldtime = GetTickCount(); } // 畫(huà)單元方塊 void DrawUnit(int x, int y, COLORREF c, DRAW _draw) { // 計(jì)算單元方塊對(duì)應(yīng)的屏幕坐標(biāo) int left = x * UNIT; int top = (HEIGHT - y - 1) * UNIT; int right = (x + 1) * UNIT - 1; int bottom = (HEIGHT - y) * UNIT - 1; // 畫(huà)單元方塊 switch(_draw) { case SHOW: // 畫(huà)普通方塊 setlinecolor(0x006060); roundrect(left + 1, top + 1, right - 1, bottom - 1, 5, 5); setlinecolor(0x003030); roundrect(left, top, right, bottom, 8, 8); setfillcolor(c); setlinecolor(LIGHTGRAY); fillrectangle(left + 2, top + 2, right - 2, bottom - 2); break; case FIX: // 畫(huà)固定的方塊 setfillcolor(RGB(GetRValue(c) * 2 / 3, GetGValue(c) * 2 / 3, GetBValue(c) * 2 / 3)); setlinecolor(DARKGRAY); fillrectangle(left + 1, top + 1, right - 1, bottom - 1); break; case CLEAR: // 擦除方塊 setfillcolor(BLACK); solidrectangle(x * UNIT, (HEIGHT - y - 1) * UNIT, (x + 1) * UNIT - 1, (HEIGHT - y) * UNIT - 1); break; } } // 畫(huà)方塊 void DrawBlock(BLOCKINFO _block, DRAW _draw) { WORD b = g_Blocks[_block.id].dir[_block.dir]; int x, y; for(int i = 0; i < 16; i++, b <<= 1) if (b & 0x8000) { x = _block.x + i % 4; y = _block.y - i / 4; if (y < HEIGHT) DrawUnit(x, y, g_Blocks[_block.id].color, _draw); } } // 檢測(cè)指定方塊是否可以放下 bool CheckBlock(BLOCKINFO _block) { WORD b = g_Blocks[_block.id].dir[_block.dir]; int x, y; for(int i = 0; i < 16; i++, b <<= 1) if (b & 0x8000) { x = _block.x + i % 4; y = _block.y - i / 4; if ((x < 0) || (x >= WIDTH) || (y < 0)) return false; if ((y < HEIGHT) && (g_World[x][y])) return false; } return true; } // 旋轉(zhuǎn)方塊 void OnRotate() { // 獲取可以旋轉(zhuǎn)的 x 偏移量 int dx; BLOCKINFO tmp = g_CurBlock; tmp.dir++; if (CheckBlock(tmp)) { dx = 0; goto rotate; } tmp.x = g_CurBlock.x - 1; if (CheckBlock(tmp)) { dx = -1; goto rotate; } tmp.x = g_CurBlock.x + 1; if (CheckBlock(tmp)) { dx = 1; goto rotate; } tmp.x = g_CurBlock.x - 2; if (CheckBlock(tmp)) { dx = -2; goto rotate; } tmp.x = g_CurBlock.x + 2; if (CheckBlock(tmp)) { dx = 2; goto rotate; } return; rotate: // 旋轉(zhuǎn) DrawBlock(g_CurBlock, CLEAR); g_CurBlock.dir++; g_CurBlock.x += dx; DrawBlock(g_CurBlock); } // 左移方塊 void OnLeft() { BLOCKINFO tmp = g_CurBlock; tmp.x--; if (CheckBlock(tmp)) { DrawBlock(g_CurBlock, CLEAR); g_CurBlock.x--; DrawBlock(g_CurBlock); } } // 右移方塊 void OnRight() { BLOCKINFO tmp = g_CurBlock; tmp.x++; if (CheckBlock(tmp)) { DrawBlock(g_CurBlock, CLEAR); g_CurBlock.x++; DrawBlock(g_CurBlock); } } // 下移方塊 void OnDown() { BLOCKINFO tmp = g_CurBlock; tmp.y--; if (CheckBlock(tmp)) { DrawBlock(g_CurBlock, CLEAR); g_CurBlock.y--; DrawBlock(g_CurBlock); } else OnSink(); // 不可下移時(shí),執(zhí)行“沉底方塊”操作 } // 沉底方塊 void OnSink() { int i, x, y; // 連續(xù)下移方塊 DrawBlock(g_CurBlock, CLEAR); BLOCKINFO tmp = g_CurBlock; tmp.y--; while (CheckBlock(tmp)) { g_CurBlock.y--; tmp.y--; } DrawBlock(g_CurBlock, FIX); // 固定方塊在游戲區(qū) WORD b = g_Blocks[g_CurBlock.id].dir[g_CurBlock.dir]; for(i = 0; i < 16; i++, b <<= 1) if (b & 0x8000) { if (g_CurBlock.y - i / 4 >= HEIGHT) { // 如果方塊的固定位置超出高度,結(jié)束游戲 GameOver(); return; } else g_World[g_CurBlock.x + i % 4][g_CurBlock.y - i / 4] = 1; } // 檢查是否需要消掉行,并標(biāo)記 BYTE remove = 0; // 低 4 位用來(lái)標(biāo)記方塊涉及的 4 行是否有消除行為 for(y = g_CurBlock.y; y >= max(g_CurBlock.y - 3, 0); y--) { i = 0; for(x = 0; x < WIDTH; x++) if (g_World[x][y] == 1) i++; if (i == WIDTH) { remove |= (1 << (g_CurBlock.y - y)); setfillcolor(LIGHTGREEN); setlinecolor(LIGHTGREEN); setfillstyle(BS_HATCHED, HS_DIAGCROSS); fillrectangle(0, (HEIGHT - y - 1) * UNIT + UNIT / 2 - 5, WIDTH * UNIT - 1, (HEIGHT - y - 1) * UNIT + UNIT / 2 + 5); setfillstyle(BS_SOLID); } } if (remove) // 如果產(chǎn)生整行消除 { // 延時(shí) 300 毫秒 Sleep(300); // 擦掉剛才標(biāo)記的行 IMAGE img; for(i = 0; i < 4; i++, remove >>= 1) { if (remove & 1) { for(y = g_CurBlock.y - i + 1; y < HEIGHT; y++) for(x = 0; x < WIDTH; x++) { g_World[x][y - 1] = g_World[x][y]; g_World[x][y] = 0; } getimage(&img, 0, 0, WIDTH * UNIT, (HEIGHT - (g_CurBlock.y - i + 1)) * UNIT); putimage(0, UNIT, &img); } } } // 產(chǎn)生新方塊 NewBlock(); }
大家趕緊去動(dòng)手試試吧!
審核編輯:湯梓紅
-
游戲
+關(guān)注
關(guān)注
2文章
742瀏覽量
26317 -
C語(yǔ)言
+關(guān)注
關(guān)注
180文章
7604瀏覽量
136827 -
源碼
+關(guān)注
關(guān)注
8文章
641瀏覽量
29213
原文標(biāo)題:C語(yǔ)言零基礎(chǔ)項(xiàng)目:俄羅斯方塊游戲!詳細(xì)思路+源碼分享
文章出處:【微信號(hào):cyuyanxuexi,微信公眾號(hào):C語(yǔ)言編程學(xué)習(xí)基地】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論