MFC 實現 俄羅斯方塊

 
  1. 很經典的遊戲.簡單又讓人着迷.
    今天就用mfc來實現個自己的俄羅斯方塊.運行截圖如下:

看起來還行吧..
網上有很多版本的實現代碼.但是看了幾個都是用一個很大的數組記錄這些下落的物體的形狀.然後寫一個很大的switch case來實現變形.感覺真麻煩.今天提出一個容易的實現方法.下面切入正題


思路是這樣的:

一:隨機生成物體

二:控制

    1.變形

        圍繞一個方塊向右旋轉90度.以變形

    2.左右下移動

        1>物體左右移動的時候不要過界.

        2>物體落定後.

            (1).設定它落下的位置.

            (2).看是否滿了一行.滿了一行消去

            (3).看是否方塊壘到頂了.到了game over

三:重複以上步驟


約定:
下落的會變形的那個小東東,我們叫 物體Object (--!!.大家忍受下這個名字.我是想不出其他的名字了.)
左側那個 物體在裏面移動的區域叫 遊戲區域 Block


實現這個遊戲的思路跟玩這個遊戲一樣簡單...
1,隨機生成一個物體.
2,響應鍵盤的輸入,做左右下移動和變形
3,物體落到底部,判斷是否滿一行,物塊壘滿一行消除之.壘到頂部game over

但是實現起來貌似又不是那麼簡單了.

首先看起來隨機生成一個不同形狀的物體就是挑戰
怎麼在視圖上畫一個個的不同形狀的物體呢?
我們看下這個遊戲.每個物體都是由更小的方塊組成.這些基本的小方塊沒有什麼不同,
再看看這個二維的遊戲界面,左邊的遊戲區域,一個個的小方塊合在一起是否就是我們所熟悉的二維數組?
假如我們把這個遊戲區域映射到一個二維數組,當這個數組元素有物體佔據的時候就在上面塗顏色,沒有則塗背景色.不就實現了麼?.

好了可以定義遊戲的區域的數據結構了.這個遊戲中由於要畫不同的顏色,因此數據結構可以如下定義:

  1. struct tagBlock
  2. {
  3.     bSet;
  4.     COLORREF color;
  5. }block[18][10];  // 我們將這個遊戲區域格式化爲 18*10 (row*col);

        那麼畫一個物體又該如何畫呢?
        假如我們先用數組定義好物體的形狀,然後移動變化,那就太麻煩了.可以看到這些物體都是更小的方塊組成的.(我們用四個小方塊組成一個大的物體).用四個小方塊,通過相對位置的不同可以變化成各種各樣的不同形狀.
我們最終是要在那個二維數組中畫不同形狀的物體.那麼在畫的時候就要直到當前的物體應該畫到哪兒.假如我們對二維數組的每個元素再抽象出來一個數據結構.在這個數據結構中保存當前它在二維數組中的位置(行列值).那就容易多了.


 

  1. typedef struct tagPane
  2. {
  3.  int row; // 在block[18][10]的行
  4.  int col;  // 列
  5. }Pane; 

這是一個小方格的定義,那每個物體要四個小方格,於是

  1. struct tagObject
  2. {
  3.  Pane object[4];
  4.  COLORREF color; // 每個物體有自己的顏色
  5.   int t; // 對應於最上面的一個方塊的行值row
  6.   int b; // 對應於最下面一個方塊行值
  7.   int l; // 對應於最左面一個方塊的列值col
  8.   int r; // 對應於最右面一個方塊列值
  9. };

由於我們要在畫一個當前的下落的物體和一個下次將出現的物體.因此定義兩個物體的對象

  1. tagObject Object,NextObject;

每個物體都是有邊界的(把四個小方格全部盛下的最小矩形邊界),我們把這個物體的四個小方塊當前在block中的行列值,記錄下來作爲邊界,放置在 tblr中.下面很多地方是用到這幾個值的

我們規定每個小方塊的實際大小是 (24*24)像素
那這個遊戲區域實際上就是 height = 18*24 ,width = 10*24大小的矩形內(要注意數組的行列和矩形寬高的對應,col 對應的是width, row 對應的是height)

 

  1. // 定義方格大小和遊戲區域的矩形
  2. const int PANE_WIDTH = 24; // 每個方格大小時24 * 24 pixel
  3. const CRect BLOCK_RECT = CRect(29,31,271,463); // 遊戲區域矩形

現在的問題是,你怎麼知道具體應該在視圖的哪個地方畫這些小方塊呢?
太簡單了.我們可以由行列值(row,col)立即得到它在視圖中所對應的矩形區域
CRect(col*PANE_WIDTH,row*PANE_WIDTH,(col+1)*PANE_WIDTH,(row+1)*PANE_WIDTH);
假如我們 要畫一個 物體,物體的四個方塊的行列值都知道的.比如是在二維數組中
(<0,0> <0,1> <1,0> <1,1>) 那
<0,0>對應的視圖的座標矩形是(0,0,24,24)
<0,1> -----(24,0,48,24)
....
自己那麼對比幾下就明白了.
可以寫一個函數專門獲取小方塊對應的實際視圖中的矩形區域(實際上,我們的遊戲區域不是從視圖的0,0開始畫的,而是上和左都有空白,左邊空白LEFT_MARGIN個像素,上邊空白是TOP_MARGIN個像素,因此是以(LEFT_MARGIN,TOP_MARGIN)點處作爲遊戲區域的左上角 .方塊的長和寬相等,定義爲PANE_WIDTH = 24)

 

  1. // 根據座標取得客戶區矩形
  2. CRect CRBlock::GetPaneRect(int row, int col)
  3. {
  4.     if (row < 0 || col < 0)
  5.     {
  6.         return CRect(0,0,0,0);
  7.     }
  8.     return CRect(col*PANE_WIDTH+LEFT_MARGIN,row*PANE_WIDTH+TOP_MARGIN,
  9.         (col+1)*PANE_WIDTH+LEFT_MARGIN,(row+1)*PANE_WIDTH+TOP_MARGIN);
  10. }
  11. //爲方便.給出另一個重載的版本
  12. // 由Pane的座標取得對應的客戶區的矩形
  13. CRect CRBlock::GetPaneRect(Pane *p)
  14. {
  15.     return GetPaneRect(p->row,p->col);
  16. }

現在再繪製一個物體還難麼???張飛吃豆芽.!

  1. #define RANDOM_COLOR RGB(rand()%256,rand()%256,rand()%256)
  2. // 繪製當前下落的物體
  3. void CRBlock::DrawFallingObject(CDC *pDC)
  4. {
  5.     CBrush brush(Object.color);
  6.     CBrush brBorder(RANDOM_COLOR);
  7.     CBrush *pOld = pDC->SelectObject(&brush);
  8.     for (int i = 0; i < 4; i++)
  9.     {
  10.         CRect rc = GetPaneRect(&(Object.object[i]));
  11.         if (!rc.IsRectNull())  //這個組成物體的小方塊 還未出現在遊戲區域內
  12.         {
  13.             pDC->Rectangle(&rc);
  14.             pDC->FrameRect(&rc,&brBorder); // 繪製邊界,看起來會漂亮點哦.
  15.         }
  16.     }
  17.     pDC->SelectObject(pOld);
  18. }

咦.鬱悶了...吃豆芽的時候遇到點problem.....

需要繪製下一個將出現的物體.這個物體不是繪製在遊戲區域內的.而是在遊戲區域的右側.

沒關係.假如我們在遊戲區域和這下個物體繪製的區域之間隔一個 方塊的寬度.所有的問題都又解決了,原來的函數還照樣可以用... 

 ...繼續吃豆芽

 

  1. const CRect RECT_NEXTOBJECT = CRect(294,30,438,174); // 在這個矩形局域畫下一個物體 4*6 *PANE_WIDTH 大小
  2.                             //294 = LEFT_MARGIN + 10 * PANE_WIDTH + 1*PANE_WIDTH; 
  3. 現在這個4*6的區域的 左上角,即是第一個方格的列座標就是  10+1 = 11 了.至於行座標,由於這個區域是和原來的遊戲區域上對齊的,所以依然是0;
  4. // 繪製下一個將出現的物體
  5. void CRBlock::DrawNextObject(CDC *pDC)
  6. {
  7.     int l = NextObject.l;
  8.     int t = NextObject.t;
  9.     int r = NextObject.r;
  10.     int b = NextObject.b;
  11.     
  12.     // 把 NextObject 的行列映射到 要畫在的矩形中
  13.     int offsetRow = (0+(6-(b-t+1))/2)-t;     /// 這只是要把物體畫到這個區域的中心
  14.     int offsetCol = (11+(6-(r-l+1))/2)-l; // 
  15.     
  16.     // 這一段只是美化
  17.     CBrush brBkgnd(COLOR_BKGND);
  18.     CBrush *pOld = pDC->SelectObject(&brBkgnd);
  19.     CPen pen;
  20.     pen.CreatePen(PS_SOLID,1,RANDOM_COLOR);
  21.     CPen *pOldPen = pDC->SelectObject(&pen);
  22.     pDC->Rectangle(&RECT_NEXTOBJECT);
  23.     pDC->SelectObject(pOldPen);
  24.     pDC->SelectObject(pOld);
  25.     
  26.     CBrush brush(NextObject.color);
  27.     CBrush brBorder(RANDOM_COLOR);
  28.     pDC->SelectObject(&brush);
  29.     // 畫下一個物體
  30.     for (int i = 0; i < 4; i++)
  31.     {
  32.         int row = NextObject.object[i].row;
  33.         int col = NextObject.object[i].col;
  34.         CRect rc = GetPaneRect(row+offsetRow,col+offsetCol);
  35.         if (!rc.IsRectNull())
  36.         {
  37.             pDC->Rectangle(&rc);
  38.             pDC->FrameRect(&rc,&brBorder);
  39.         }
  40.     }
  41. }

好了.萬事具備了.下次講如何生成不同形狀的物體物體

編程羣C,C++,MFC,Java 58698324.正招人.歡迎加入

發佈了39 篇原創文章 · 獲贊 6 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章