我羅斯方塊最終篇


作業描述     詳情
作業屬於     2020面向對象程序設計
作業要求
  • 代碼的 git 倉庫鏈接
  • 運行截圖/運行視頻
  • 代碼要點
  • 收獲與心得
  • 依然存在的問題
作業目標
  • 實現我羅斯的正常運行
  • 發表博客
作業正文      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的程序員”,而不是一個能夠編寫項目的、合格的程序員。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



猜您在找
 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM