效果圖
用C語言實現俄羅斯方塊,需要先解決下面幾個問題:
1、如何用C語言繪制圖形界面
EasyX圖形庫(http://www.easyx.cn)即TC的圖形庫在VC下的移植。
包含庫#include
先初始化圖形窗口
initgraph(WINDOW_WIDTH, WINDOW_HIGH) ;WINDOW_WIDTH為窗口的寬帶,WINDOW_HIGH為窗口的高度。
清空繪圖設備
cleardevice();
設置畫筆顏色
setcolor(RED) ;
設置線條風格
setlinestyle(PS_SOLID, NULL, 0);
畫矩形
rectangle
還有畫線、顯示文字等函數,可以參照其幫助文檔。
注意:由於我們用的是EasyX圖形庫,故源文件後綴要為.cpp,但其中內容都是C的語法。
2、如何存儲表示出俄羅斯方塊的形狀
在計算機中如何讓一串的01數字,代表俄羅斯方塊?
一、我們可以用編號,不同的編號代表不同的俄羅斯方塊,根據編號把不同方塊的畫法寫在代碼中,這樣19種
方塊就得有19種相應的代碼來描繪。而且這樣擴展性不好,若以後設計了新的方塊,則需要更改大量源代碼。
二、我們很自然的想到可用字模點陣的形式來表示,即設置一個4行4列的數組,元素置1即代表這個位置有小
方塊,元素置0即代表這個位置無小方塊,這個整個的4*4的數組組成俄羅斯方塊的形狀。
1000
1000
1100
0000
這個方法挺靠譜,但我們還可以優化一下:不用4*4的數組,而是用16個bit位來表示這個點陣。這樣存儲起來比較方便,故我們用unsigned int 的低16位來表示方塊的點陣。
我們可以用掩碼與表示俄羅斯方塊的位進行操作,來識別並在屏幕上畫出方塊。
詳情見GUI.cpp中的DrawRock函數。
//逐位掃描由unsigned int的低2字節 //16個位組成的俄羅斯方塊形狀點陣(其代表4*4的方塊形狀) mask = (unsigned int)1 << 15 ; for (i=1; i<=16; i++) { //與掩碼相與為1的 即為方塊上的點 if ((rockArray[rockIndex].rockShapeBits & mask) != 0) { //在屏幕上畫出此方塊 rectangle(rockX+2, rockY+2, rockX+ROCK_SQUARE_WIDTH-2, rockY+ROCK_SQUARE_WIDTH-2) ; } //每4次 換行 轉到下一行繼續畫 i%4 == 0 ? (rockY += ROCK_SQUARE_WIDTH, rockX = currentLocatePtr->left) : rockX += ROCK_SQUARE_WIDTH ; mask >>= 1 ; }
我們把俄羅斯方塊點陣的數位存在rockArray中,我們可以事先把這19種方塊的字模點陣自己轉化成十六進制,然後在rockArray數組的初始化時賦值進去。
但這樣做未免有點太費力,且擴展性也不太好,若以後設計的新方塊種類加入,要改變數組rockArray中的值。
我們可以考慮把所有俄羅斯方塊的點陣存儲在配置文件中,在程序初始化時讀取文件,把這些點陣轉換成unsigned int的變量存儲在rockArray中。
這樣,以後我們增添新的方塊形狀只需要在配置文件中增加新的點陣即可。
@###
@###
@@##
#### (為使得看起來更醒目,我們用@表示1,用#表示0)
3、如何讓圖形動起來
若沒有按鍵的情況下,方塊是自動下落的。
如何實現自動下落?在某位置處用函數DrawRock在屏幕上畫出俄羅斯方塊,然後再擦除掉(即用背景色在原位置處重繪一次方塊),最後在下落的下一個位置處用函數DrawRock在屏幕上畫出俄羅斯方塊,如此循環,中間用計時器間隔一段時間以控制下落的速度。
同理,按下屏幕的左右鍵也是如此,只是在按下鍵盤時把方塊的位置重新計算了。
那麼按下上方向鍵時,如何讓方塊翻轉呢?
我們在配置文件中就把方塊的順時針翻轉形態放在了一起:
@###
@###
@@##
####
@@@#
@###
####
####
@@##
#@##
#@##
####
##@#
@@@#
####
####
我們每按一次上方向鍵改變一次方塊的形狀即可。若一直按上鍵,形狀應該是循環地翻滾。
我們想到了循環鏈表的數據結構可實現這個效果。
可是我們若把這些一種類的方塊的各種形態串成循環鏈表形式,那麼每次重新生成方塊時我們就難以隨機地生成方塊了。
故還是得用數組來存儲,但又要有循環鏈表的功能,於是我們想到了靜態循環鏈表。
我們用結構體來作為一個方塊在rockArray中的元素
typedef struct ROCK
{ //用來表示方塊的形狀(每一個字節是8位,用每4位表示方塊中的一行)
unsigned int rockShapeBits ;
int nextRockIndex ; //下一個方塊,在數組中的下標
} RockType ;
這樣,當我們按下上方向鍵時,把傳入函數DrawRock中的rockIndex變為當前方塊結構體中的nextRockIndex即可。
詳情見play.cpp中的ProccessUserHit函數。
4、如何判斷方塊什麼時候停止什麼時候滿行得分
方塊一直下落,最終是要停下來的,我們要設置一個邊界來約束方塊的移動范圍。我們把當前游戲界面劃分成以俄羅斯方塊中的小方格為單位的格子,用一個二維數組g_gameBoard來表示這些小方格的狀態,1表示此位置有方塊,0表示此位置為空。
我們按照界面的大小和方格的大小來計算此二維數組時,再多設置一圈“圍牆”,即多加兩行兩列,並把它們的值初始化為1。
當方塊准備下落或是左右移動的時候,前提前檢查其即將落下的位置是否為空,若不為空,則停止下落,並把當前俄羅斯方塊占用的方格都設置為1。
詳情見play.cpp中的moveAbled函數。
判斷滿行:
從最後一行開始往上檢查g_gameBoard,若有一行全為1,則說明此行滿行,將此行擦出,把此行上面的所有行向下移動一個單位。
詳情見play.cpp中的ProcessFullRow函數
5、其他細節問題:
如何快速下落
詳情見play.cpp中的FastFall函數
如何暫停
詳情見play.cpp中的ProccessUserHit函數
此游戲程序的主要邏輯在play.cpp中的PlayGame函數
源代碼點擊這裡