1. 程式人生 > >俄羅斯方塊小遊戲

俄羅斯方塊小遊戲

俄羅斯方塊小遊戲

版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/qq_26239525/article/details/82431921

研一時寫的俄羅斯方塊小遊戲,C++程式,程式碼還有很多值得優化的地方,歡迎交流。

程式設計環境:Based on Qt 5.9.1 (MSVC 2015)。

首先要了解俄羅斯方塊的遊戲規則:   

  1. 俄羅斯方塊的遊戲基本規則通過左右上下按鍵來移動、旋轉和擺放遊戲自動輸出的各種方塊,使之排列成完整的一行或多行並且消除得分。
  2. 遊戲中使用鍵盤方向鍵←→控制移動,↑變形,↓加速下落,空格開始。
  3. 由小方塊組成的不同形狀的板塊陸續從螢幕上方落下來,玩家通過調整板塊的位置和方向,使它們在螢幕底部拼出完整的一條或幾條。
  4. 這些完整的橫條會隨即消失,給新落下來的板塊騰出空間,與此同時,玩家得到分數獎勵。
  5. 沒有被消除掉的方塊不斷堆積起來, 一旦堆到螢幕頂端,玩家便告輸,遊戲結束。

然後此程式中俄羅斯方塊實現的基本思路:

  1. 俄羅斯方塊是一張表格,很容易想到用一個二維陣列來實現。然後通過陣列元素判斷此處是否有方塊,1代表有方塊,0代表沒有方塊(為了好看,兩邊和底是1的方塊沒有顯示出來)。
    這裡寫圖片描述
  2. 然後二維陣列在定義的時候定義成如下型別,陣列元素中的1代表兩邊和底邊,代表方塊移動到此處後便不能再移動。
    這裡寫圖片描述
  3. 七種具體的小方塊用4*4的二維陣列表示,當按下上的變換按鍵之後就會按如下方式變換(其他類似):
    這裡寫圖片描述
    這裡寫圖片描述
    這裡寫圖片描述
    這裡寫圖片描述
  4. 判斷下降到底或者撞牆,是通過不斷計算4*4的二維陣列和二維陣列所在的maps陣列中對應位置的數字之和,如果相加之後有值為2,則肯定有方塊重疊了,視為重合。

    int Tetris::Judge_Hit_Wall(int tempArray[][4])//判斷是否撞到牆
    {
       for (int
    i=widthOfLocation; i<4+widthOfLocation; i++) { for (int j=heightOfLocation; j<4+heightOfLocation; j++) { //如果現在移動的俄羅斯方塊和maps陣列重元素相加為2,則表示重合,那麼返回1 if (tempArray[j-heightOfLocation][i-widthOfLocation] + maps[j][i] == 2) return 1; } } return 0; }
  5. 另外maps定義成了20*12的陣列,主要是表格上面預留了三行,要達到的效果就是讓方塊從上面緩緩降下來,然後通過表格上一行判斷是否有1,來判斷遊戲是否結束(圖很難看,湊活著看吧)。

    這裡寫圖片描述

  6. 總共19種方塊:

    int gra[19][4][4] =
    {
       {0, 1, 0, 0,
        0, 1, 0, 0,
        0, 1, 0, 0,
        0, 1, 0, 0},
    
       {0, 0, 0, 0,
        1, 1, 1, 1,
        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, 1, 1, 0,
        1, 1, 0, 0,
        0, 0, 0, 0},
    
       {0, 0, 1, 0,
        0, 1, 1, 0,
        0, 1, 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, 1, 1, 0,
        0, 1, 1, 0,
        0, 0, 0, 0},
    
       {0, 1, 0, 0,
        1, 1, 1, 0,
        0, 0, 0, 0,
        0, 0, 0, 0},
    
       {0, 1, 0, 0,
        0, 1, 1, 0,
        0, 1, 0, 0,
        0, 0, 0, 0},
    
       {0, 0, 0, 0,
        1, 1, 1, 0,
        0, 1, 0, 0,
        0, 0, 0, 0},
    
       {0, 1, 0, 0,
        1, 1, 0, 0,
        0, 1, 0, 0,
        0, 0, 0, 0},
    
       {1, 0, 0, 0,
        1, 0, 0, 0,
        1, 1, 0, 0,
        0, 0, 0, 0},
    
       {0, 0, 0, 0,
        1, 1, 1, 0,
        1, 0, 0, 0,
        0, 0, 0, 0},
    
       {1, 1, 0, 0,
        0, 1, 0, 0,
        0, 1, 0, 0,
        0, 0, 0, 0},
    
       {0, 0, 0, 0,
        0, 0, 1, 0,
        1, 1, 1, 0,
        0, 0, 0, 0},
    
       {0, 1, 0, 0,
        0, 1, 0, 0,
        1, 1, 0, 0,
        0, 0, 0, 0},
    
       {0, 0, 0, 0,
        1, 0, 0, 0,
        1, 1, 1, 0,
        0, 0, 0, 0},
    
       {0, 1, 1, 0,
        0, 1, 0, 0,
        0, 1, 0, 0,
        0, 0, 0, 0},
    
       {0, 0, 0, 0,
        1, 1, 1, 0,
        0, 0, 1, 0,
        0, 0, 0, 0}
    };
  7. 通過以下兩個變數來定位二維4*4陣列在maps中的位置
    這裡寫圖片描述

然後俄羅斯方塊遊戲的流程:

  1. 先看主要流程。

    void Widget::dealSignal()
    {
        tetris.Deal_Key();//按鍵處理
        tetris.Tetris_Move();//方塊向下移動一步
        tetris.Line_Full();//判斷一行是否滿
        tetris.setKey(0);
    
        if (tetris.GameOver())//遊戲結束自動按下停止按鍵
            on_buttonStop_clicked();
    
        ui->lcdNumber->display( tetris.getScore() );//顯示分數
        update();//重新整理
    }
  2. Deal_Key(),先判斷按鍵是否按下,如果按下,執行相應的操作。

    key = 1 變換方塊

    key = 2 延時變短,加速下降(這塊程式沒寫,有時間再寫吧)

    key = 3 向左移動一步

    key = 4 想右移動一步

    void Tetris::Deal_Key(void)//處理按鍵函式
    {
        if (key == 1)
        {
            switch ( index )
            {
                case 0:
                    Change_Array( gra[++index] );//先將俄羅斯方塊的索引值改變,然後通過索引值改變方塊的形狀
                    break;
                case 1:
                    Change_Array( gra[--index] );
                    break;
                case 2:
                    Change_Array( gra[++index] );
                    break;
                case 3:
                    Change_Array( gra[--index] );
                    break;
                case 4:
                    Change_Array( gra[++index] );
                    break;
                case 5:
                    Change_Array( gra[--index] );
                    break;
                case 6:
                    break;
    
                case 7:
                    Change_Array( gra[++index] );
                    break;
                case 8:
                    Change_Array( gra[++index] );
                    break;
                case 9:
                    Change_Array( gra[++index] );
                    break;
                case 10:
                    index -= 3;
                    Change_Array( gra[index] );
                    break;
    
                case 11:
                    Change_Array( gra[++index] );
                    break;
                case 12:
                    Change_Array( gra[++index] );
                    break;
                case 13:
                    Change_Array( gra[++index] );
                    break;
                case 14:
                    index -= 3;
                    Change_Array( gra[index] );
                    break;
    
                case 15:
                    Change_Array( gra[++index] );
                    break;
                case 16:
                    Change_Array( gra[++index] );
                    break;
                case 17:
                    Change_Array( gra[++index] );
                    break;
                case 18:
                    index -= 3;
                    Change_Array( gra[index] );
                    break;
                default:
                    break;
            }
        }
        if (key == 2)//延時變短,加速下降
        {
    
        }
        if (key == 3)//向左移動
        {
            widthOfLocation--;
            if ( Judge_Hit_Wall(array) )//如果有重合,就不移動
            {
                widthOfLocation++;
            }
        }
        if (key == 4)//向右移動
        {
            widthOfLocation++;
            if ( Judge_Hit_Wall(array) )//如果有重合,就不移動
            {
                widthOfLocation--;
            }
        }
    }

    Change_Array(此函式是更新二維的4*4陣列,按下變換的按鍵後,需要將變換之後的陣列更新到移動的陣列中,從而達到更新顯示方塊的目的)。

    當然更新陣列之前,需要先判斷更新陣列之後,會不會出現和已有的方塊重合的情況,如果重合,那麼就不變換,直接退出。

    void Tetris::Change_Array(int temp_maps[4][4])//複製陣列
    {
        if ( Judge_Hit_Wall(temp_maps) )//變換之後判斷是否撞牆,如果撞牆就不變換
            return;
    
        for (int i=0; i<4; i++)//把按下按鍵之後變換的方塊的陣列座標賦值到正在下降的陣列座標中
            for (int j=0; j<4; j++)
                array[i][j] = temp_maps[i][j];
    
        return ;
    }
  3. Tetris_Move(),方塊向下移動一步(這個過程比較複雜,需要拿著紙筆一點一點的耐心計算),下降過程中如果碰到底部,就不再下降。

    void Tetris::Tetris_Move()
    {
          heightOfLocation++;//方塊下降一格
    
          for (int i=widthOfLocation; i<4+widthOfLocation; i++)
          {
              for (int j=heightOfLocation; j<4+heightOfLocation; j++)
              {
                  //下降過程中,迴圈比對16個方塊,如果有等於2的,就表示已經下降到底部
                  if (array[j-heightOfLocation][i-widthOfLocation] + maps[j][i] == 2)
                  {
                      heightOfLocation--;//heightOfLocation-- 把降下來的一個在升回去
    
                      //for迴圈,將現在的方塊座標複製到maps陣列中
                      for (int i=widthOfLocation; i<4+widthOfLocation; i++)
                      {
                          for (int j=heightOfLocation; j<4+heightOfLocation; j++)
                          {
                              if (array[j-heightOfLocation][i-widthOfLocation] == 1)
                              {
                                  maps[j][i] = 1;
                              }
                          }
                      }
    
                      for (int i=0; i<20; i++)
                      {
                          for (int j=0; j<12; j++)
                          {
                              mapTemp[i][j] = maps[i][j];
                          }
                      }
    
                      heightOfLocation = 0;//一系列初始化
                      widthOfLocation = 4;
                      index = indexNext;//隨機生成俄羅斯方塊;
                      indexNext = random(19);
    
                      key = 0;//方塊下降到底部之後,按鍵值初始化為零
                      Change_Array( gra[index] );//通過索引值將陣列賦值到下降的陣列中
                      qDebug() << "index = " << index;
    
                      return ;//下降到底部,終止函式
                  }
    
                  if (array[j-heightOfLocation][i-widthOfLocation] == 1)//顯示正在下落的方塊
                      mapTemp[j][i] = 1;
              }
          }
      }
  4. Line_Full(),判斷每行是否滿,如果滿就消行。

       void Tetris::Line_Full(void)
       {
           static int Line = 0;
           for (int j=18; j>0; j--)//十四行
           {
               int sum = 0;
               for (int i=1; i<=10; i++)//判斷每一行是否都為1
                   sum += maps[j][i];//10個數組元素的和
    
               if (sum == 10)//
               {
                   Line++;
                   int k = j;
                   for (; k>0; k--)//從滿的那一行開始通過迴圈將以上的方格資料全部複製下來
                       for (int i=1; i<=10; i++)
                           maps[k][i] = maps[k-1][i];
    
                   if (k == 0)//最上面一行全部賦值為零
                       for (int i=1; i<=10; i++)
                           maps[k][i] = 0;
    
                   j++;
               }
           }
    
           switch(Line)
           {
               case 1:
                   score += 10;
                   break;
               case 2:
                   score += 30;
                   break;
               case 3:
                   score += 50;
                   break;
               case 4:
                   score += 100;
                   break;
               default:
                   break;
           }
           Line = 0;
    
           return ;//函式終止
       }
    
  5. 判斷遊戲是否結束。

    bool Tetris::GameOver(void)
    {
        for (int n=1; n<=10; n++)
            if (maps[2][n] == 1)
            {
                qDebug() << "Game Over";
                return 1;
            }
    
        return 0;
    }
  6. 更新分數,更新顯示。

    ui->lcdNumber->display( tetris.getScore() );//顯示分數
    update();//重新整理

遊戲介面:

這裡寫圖片描述

歡迎交流