作業描述 | 詳情 |
作業屬於 | 2020面向對象程序設計 |
作業要求 |
|
作業目標 |
|
作業正文 | https://www.cnblogs.com/Es-war/p/13090414.html |
小組成員 |
|
代碼倉庫 | https://github.com/Es-war/Tetris |
一、運行效果
游戲過程 |
游戲結束 |
- 玩家一獲勝
- 玩家二獲勝
- 平局
游戲運行視頻 |
二、代碼要點
界面渲染 |
- 采用Easy_x進行界面渲染,將渲染功能打包在pait類中
class paint { public: void endGame(); //繪制結束界面 void initEnviroment(); //初始化環境 void drawGameBG(); //繪制游戲背景 void drawLeftSide(); //繪制游戲左側邊 void drawRightSide(); //繪制游戲右側邊 void drawLeftMap(); //繪制玩家一的地圖 void drawRightMap(); //繪制玩家二的地圖 void drawSquareNext(int num); //繪制預覽框內的方塊 void drawItem(int x, int y, COLORREF c); //繪制方塊 void drawSquareNow(int num); //繪制當前“塵埃落定”的全體方塊 };
初始化 |
- 游戲初始環境的初始化
void paint::initEnviroment() { // 窗口設置 initgraph(1210, 540); HWND hwnd = GetHWnd(); SetWindowText(hwnd, L"我羅斯"); SetWindowPos(hwnd, HWND_TOP, 0, 20, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW); // 繪圖模式設置 setbkmode(TRANSPARENT); // 隨機數種子 srand(time(NULL)); }
- 每一輪游戲的初始化:分數、儲存方塊信息的“地圖”,結束標志重置
- 每一個新方塊的初始化:獲取方塊所需信息——顏色、類型、形狀,生成下一方塊標志的重置,預覽框方塊信息的設置
提前將各種方塊的信息儲存在數組中,通過隨機數取模的方式來隨機生成方塊
now_c_idx[num] = next_c_idx[num]; now_s_idx[num] = next_s_idx[num]; now_d_idx[num] = next_d_idx[num]; next_c_idx[num] = rand() % 7; next_s_idx[num] = rand() % 7; next_d_idx[num] = rand() % 4;
各類操作 |
- 游戲過程中的方塊左移、右移、旋轉、下降均需要事先判斷操作是否可行,若不可行,則不執行
- 長時間未執行操作,方塊將自動下降(利用GetTickCount()函數來獲取時間)
bool player::checkPut(int mp_x, int mp_y, int dir_idx) { int sq_x[4]; int sq_y[4]; for (int i = 0; i < 4; ++i) { sq_x[i] = mp_x + squares[now_s_idx[num]].dir[dir_idx][i][0]; sq_y[i] = mp_y + squares[now_s_idx[num]].dir[dir_idx][i][1]; } // 【左右越界、下方越界、重復占格】 for (int i = 0; i < 4; ++i) { if (sq_x[i] < 0 || sq_x[i] > 9 || sq_y[i] > 14) return false; if (sq_y[i] < 0) // 檢查坐標合法性 continue; if (mp[num][sq_x[i]][sq_y[i]]) return false; } return true; }
玩家類 |
- 玩家類的各種操作大同小異,但是在下降、消行、給對方增加行、檢查是否結束差異較大,所以將這部分剝離出來,構造玩家一、二類公有繼承於玩家類,分別編寫上述成員函數,便於進行相關操作
默認下降速度的改變 |
- 隨着玩家分數的增加,游戲難度需相應提高,設置了默認下降速度改變的機制,但是最終會穩定在某個最大速度值
//檢查玩家一是否長時間未執行正確指令 int speed1, speed2; if (score[0] <= 3000) speed1 = 1000 - score[0] / 10; else speed1 = 700; if (time_tmp[0] - time_now[0] >= speed1) { time_now[0] = time_tmp[0]; one.moveDown(); over = true; } //檢查玩家二是否長時間未執行正確指令 if (score[1] <= 3000) speed2 = 1000 - score[1] / 10; else speed2 = 700; if (time_tmp[1] - time_now[1] >= speed2) { time_now[1] = time_tmp[1]; two.moveDown(); over = true; }
玩家分數的計算 |
- 玩家在游戲過程中消除的行數不同所得分數也不相同,並且多行消除后的得分不是簡單的一行消除得分的多次疊加
score[num] += 100.0 * clearNum * (1 + clearNum * 0.25);
鍵盤敲擊事件監聽 |
- 利用_kbhit()來監聽鍵盤敲擊,並且在每次執行完操作后 Sleep(20)來降低CPU占用
//接受指令 if (_kbhit()) { //兼顧大小寫 switch (_getch()) { case 'W': case 'w': one.moveRotate(); break; case 'A': case 'a': one.moveLeft(); break; case 'D': case 'd': one.moveRight(); break; case 'S': case 's': one.moveDown(); break; case 72: two.moveRotate(); break; case 75: two.moveLeft(); break; case 77: two.moveRight(); break; case 80: two.moveDown(); break; } } // 降低CPU占用 Sleep(20);
游戲結束后的彈窗 |
- 每局游戲結束后在屏幕上顯示本局游戲的贏家(或者出現平局),並顯示詢問是否“再來億局”的彈窗
starpaint.endGame(); if (MessageBox(GetHWnd(), over_tips, L"再來億局?", MB_ICONQUESTION | MB_YESNO) == IDNO) break;
最終效果 |
- 可以保證游戲的正常運行,實現方塊左移、右移、旋轉、下降、長時間不操作自動下降、消行操作,並且補充消行后給對方增加相應行數的隨機方塊功能。
- 側邊方塊預覽功能正常實現。
- 隨着等級的提升方塊下降速度加快。
- 每局游戲結束后在屏幕上顯示本局游戲的贏家(或者出現平局),實現了“再來億局”的功能正常使用。
三、依然存在的問題
- 游戲流暢度的進一步優化
- 使用了較多全局變量,未能很好地實現代碼的封裝性,所以在最后未能分離出類代碼文件
四、收獲與心得
學習了之前沒有接觸過的Easy_x的操作,了解到如何利用它來進行頁面的渲染。明白了要想真正寫出這樣一個小游戲並不容易,在一開始,就應該利用流程圖、思維導圖等工具來對思路進行處理。在實現過程中會碰到各種各樣意料之外的問題,不要急於實現全部功能,應該是在已有的、正確的代碼基礎上進行功能的完善和補充。適時進行代碼功能的測試,以便及時修改,否則bug堆積多了,修改時也無從下手。在這個過程中,與組員的分工合作也十分重要,及時與組員保持聯系,確認編碼上的一些修改等問題。在這次制作“我羅斯”的過程中,慢慢體會到做項目與平常寫PTA上的作業的深刻不同,不要只滿足於完成PTA這類作業上,而不學習、實踐項目的編寫,否則將來只會成為一名“面向PTA的程序員”,而不是一個能夠編寫項目的、合格的程序員。