1. 程式人生 > >【C語言】實現俄羅斯方塊

【C語言】實現俄羅斯方塊

學習了C語言,寫一個俄羅斯方塊,程式碼絕對可以執行
大神勿笑,只適合初學者 本人也是菜鳥

1、考慮怎麼儲存俄羅斯方塊

俄羅斯方塊的形狀一共有19種類型,如果拿陣列來表示的話,可能會比較會浪費空間(網上有很多實現程式碼)
這裡寫圖片描述
考慮到每種方塊形狀的範圍是4 *4的小方塊,用 字模點陣的方式來儲存,即設定一個4行4列的陣列,元素置1即代表這個位置有小
方塊,元素置0即代表這個位置無小方塊,這個整個的4*4的陣列組成俄羅斯方塊的形狀。
1000
1000
1100
0000
上述4*4來表示L形狀的方塊。
4*4 =16 bit 正好為short型別,所以每一個方塊可以用一個short型別的資料來表示。

我們把俄羅斯方塊點陣的數位存在rockArray中,我們可以事先把這19種方塊的字模點陣自己轉化成十六進位制,然後在rockArray陣列的初始化時賦值進去。
但是這種方式擴充套件性不好,每當有一種新方塊時需要改動,
所以可以寫一個配置檔案來表示19種方塊。(RockShape.ini)

@###
@###
@@##
####

從配置檔案中讀取方塊的型別的程式碼在(Init.h的ReadRock函式中)在下面3中解釋下程式碼如何實現

2如何畫出方塊

可以使用EasyX庫來畫出簡單的圖形,
EasyX庫是在VC下實現TC的簡單繪圖功能的一個庫,這個庫很容易學會(直接 百度EasyX庫,裡面有詳細的教程)

那麼如何畫出方塊,方塊已經儲存到一個short型別中了
從short中讀取出,可以用一個掩碼mask = 1來與short的每個bit位相與,結果為1,則畫出一個小方塊;
函式宣告:

void DisplayRock(int rockIdx,  RockLocation_t*  LocatePtr, bool displayed)

引數1:表示在陣列中的下標,取出short型別的方塊表示資料
引數2:表示當前座標,即畫出方塊的左上角的座標x,y
引數3:true表示畫出該方塊,false 表示擦除該方塊。

//方塊在圖形視窗中的位置(即定位4*4大塊的左上角座標)  
typedef
struct LOCATE { int left; int top; } RockLocation_t;

3如何實現同一種類型方塊的翻轉,

在按‘↑’時應該翻轉同一種類型的方塊,
比如下面的橫杆和豎杆

@###
@###
@###
@###

@@@@
####
####
####

****

可以假想成靜態迴圈連結串列來實現這種方式
使同一種類型的方塊迴圈起來,
這裡寫圖片描述
用一個struct結構來表示一種方塊

typedef struct ROCK
{
    //用來表示方塊的形狀(每一個位元組是8位,用每4位表示方塊中的一行)  
    unsigned short rockShapeBits;
    int          nextRockIndex;  //下一個方塊,在陣列中的下標  
} RockType;

定義一個RockType型別的陣列來儲存19種方塊
RockType RockArray[19] = { (0, 0) };

當我們按“↑”時,把傳入畫方塊函式DrawRock中的rockIndex變為當前方塊結構體中的nextRockIndex即可。

簡單解釋下ReadRock函式的實現:當讀取到空行的時候表示 一種方塊已經讀取完畢,當讀取到**** 行時 表示同一種類型的方塊讀取完畢,具體看程式碼實現,程式碼中具體的註釋

4、主要遊戲實現的邏輯

貼一個預覽圖吧
這裡寫圖片描述

注:上述預覽圖的遊戲控制區和遊戲顯示區在Draw.h的DrawGameWindow()函式實現的

(1)在初始位置畫出方塊,在預覽區畫出下一次的方塊
(2)方塊有兩種行為:響應鍵盤命令UserHitKeyBoard(),自由下落
如果敲擊鍵盤了(w ,a ,s ,d, )空格表示暫停,如果在規定時間內沒有敲擊鍵盤的話,方塊自由下落一個單位

        if (kbhit()) //如果敲擊鍵盤了 就處理按鍵
        {
            userHit = getch();
            UserHitKeyBoard(userHit, &curRockIndex, &curRockLocation);
        }

        //沒有 就自動下移一個單位 :不能用else,因為可能按鍵不是上下左右
        DWORD newtime = GetTickCount();
        if (newtime - oldtime >= (unsigned int)(300) && moveAbled == TRUE)
        {
            oldtime = newtime;
            DisplayRock(curRockIndex, &curRockLocation, false);
            curRockLocation.top += ROCK_SQUARE_WIDTH; //下落一格  
        }

(3)當方塊落地(即不能下移了)時,判斷是否滿行,如果滿行則消除,然後再判斷遊戲是否結束,遊戲結束的話,直接退出遊戲

判斷滿行:FullLine()函式,從最底下的一行開始判斷,直到遇到一行空行,

while (count != xROCK_SQUARE_NUM ) //遇到空行 14
    {
        linefull = true;
        count = 0;
        for (int i = 1; i <= xROCK_SQUARE_NUM; ++i)
        {
            if (game_board[idx][i] == 0)
            {
                linefull = false;
                count++;
            }
        }
        if (linefull) //滿行,消除當前行,更新分數
        {
            DelCurLine(idx);//消除滿行
            game_socres += 3;
            UpdateSocres(game_socres);
            idx++;//因為下面要減1
        }
        idx--;
    }

(4)消除滿行
將要刪除的滿行擦除:即將方塊化成與背景色相同的,該程式碼為黑色
然後將上面的一行向下移,移一行刪除一行,直到遇到空行
具體看程式碼的具體實現 game.h
void DelCurLine(int rowIdx)

(4)判斷方塊是否能移動
在game.h中實現

bool MoveAble(int rockIndex, RockLocation_t* currentLocatePtr, int f_direction)

**比較當前位置的座標(左上角)開始,能否放下rockIndex的方塊。
注:f_direction為”↑”的話,則傳入的rockIndex為下一個方塊**

如果不能移動的話,給遊戲game_board設定標記表示該位置被佔有

//全域性變數-遊戲板的狀態描述(即表示當前介面哪些位置有方塊)  
//0表示沒有,1表示有(多加了兩行和兩列,形成一個圍牆,便於判斷方塊是否能夠移動)  
int game_board[yROCK_SQUARE_NUM + 2][xROCK_SQUARE_NUM + 2] = { 0 };

實現過程遇到的一些問題

(1)在快速下落的時候,可能方塊會掉出圍牆的範圍內,
快速下落是使方塊每次下落2個單位距離。
在判斷不能下落時,使當前座標的top即y減去一個單位的距離

(2)遇到多行滿行時消除不了,
在判斷滿行時,迴圈找出滿行,找出一個滿行,就消除一行,然後繼續判斷是否滿行,直到遇到空行

注:可能還在存在一些問題,歡迎各位大神來改正