本文由BlueCoder編寫 轉載請說明出處:
http://blog.csdn.net/crocodile__/article/details/11831503
我的郵箱:[email protected] 歡迎大家和我交流編程心得
我的微博:BlueCoder_黎小華 歡迎光臨^_^
今年暑假自學了Win32 SDK , 從初學到現在,還是頗有收穫。不過既然學了,就得學有所用。
我們都知道IT行業中有兩大方向:軟件和遊戲(其實網絡是嵌入在二者之中的)。
之前寫了一個集音樂、視頻播放於一身的簡易播放器——說白了就是一個小軟件。那麼下一步,就是寫一個遊戲——純Win32 C語言,不使用任何遊戲引擎——打飛機遊戲。
雖然遊戲還在創作中(今天才開始,目前我正在思考ing,完成之後必將和各位分享),不過我還是想在寫完這個遊戲之前講一個特別重要的技術——雙緩衝技術
這個技術大家應該都不陌生,不過作爲初學者的我,還是希望共享一下自己的心得,以供更多學習Win32的初學者參考
好了,F話不多說,下面進入正題
(以下全由鄙人自己的語言來描述,若有不當之處,還望見諒^_^)
……
一、先來學一下關於雙緩衝的基礎知識
雙緩衝,在遊戲開發中基本上最常用(軟件開發有時也會用到),它的目的是從根本上解決閃屏——實現的技術就是在內存中繪製所有的圖形,然後統一繪製到屏幕上。
那麼在Win32中(C語言),實現雙緩衝的步驟如下:(這裏以客戶區繪圖爲例, hdc、hdcBuffer、hdcBmp均是HDC類型變量名)
(1)首先獲取客戶區DC——hdc
(2)獲取關於DC的內存兼容DC——hdcBuffer、獲取關於DC的兼容內存位圖並選入hdcBuffer中
(3)先在hdcBuffer中繪製所需的圖(例如很多條直線、圖形等等)
(4)如果你想一次性貼很多位圖 , 那麼你還應該獲取一個關於DC的內存DC——hdcBmp , 將位圖依次選入hdcBmp中,然後將位圖從hdcBmp貼到hdcBuffer中
(5)最後將hdcBuffer(也就是內存中)中繪製好的位圖貼到原客戶區DC中
看到這兒,你可能會有點兒蒙,不怎麼明白,沒事兒,待會兒用實例代碼講述會讓你豁然開朗
在進入代碼階段之前,先來看看兩個函數:(分別取自msdn , 這裏的英文很簡單 , 就不用翻譯了)
(1)CreateCompatibleDC
HDC CreateCompatibleDC( HDC hdc // handle to DC );
創建兼容於hdc的內存DC,也就是操作系統在內存中分配相應內存資源,用於你在內存中實現繪圖操作
(2)CreateCompatibleBitmap
HBITMAP CreateCompatibleBitmap( HDC hdc, // handle to DC int nWidth, // width of bitmap, in pixels int nHeight // height of bitmap, in pixels );
創建一個兼容於hdc的內存BITMAP,類似於內存DC,操作系統在內存中分配相應內存資源,你可以在它上面繪製直線、圖形、位圖==
不過,切記:在程序結束或者你不再使用這些內存資源的時候,一定要用DeleteDC函數刪除內存DC、用DeleteObject函數刪除Bitmap
二、代碼階段
這裏使用微信中打飛機遊戲的素材,先來做一個簡單的例子(飛機平滑的移動)——也是我寫這個打飛機遊戲的第一步
下面通過代碼的方式來講解以下上面所說的幾個步驟:
(1)首先獲取客戶區DC——hdc
hdc = BeginPaint(hwnd, &ps);
(2)獲取關於hdc的內存兼容DC——hdcBuffer、獲取關於hdc的兼容內存位圖cptBmp並選入hdcBuffer中
//用於緩衝的內存DC hdcBuffer = CreateCompatibleDC(hdc);
//創建內存兼容位圖cptBmp hdc = GetDC(hwnd); cptBmp = CreateCompatibleBitmap(hdc, sClient.cx, sClient.cy); ReleaseDC(hwnd, hdc);
//將內存位圖選入緩衝內存DC中——以便可以繪製多個位圖 SelectObject(hdcBuffer, cptBmp);
(3)一次性貼多個位圖 , 那麼還應該獲取一個關於hdc的內存DC——hdcBmp , 將位圖依次選入hdcBmp中,然後將位圖從hdcBmp貼到hdcBuffer中
//用於貼位圖的內存DC hdcBmp = CreateCompatibleDC(hdc);
//將背景和飛機都先貼到內存緩衝DC hdcBuffer中(這是在內存中操作的) for(i=0; i<N; i++) { SelectObject(hdcBmp, hBmp[i]); if(i > 0) { //貼飛機(透明貼法,上次講過) TransparentBlt(hdcBuffer, sClient.cx * (i-1) / N, y - 128, sBmp[i].cx, sBmp[i].cy / (SMALL + i-1), hdcBmp, 0, 0, sBmp[i].cx, sBmp[i].cy / (SMALL + i-1), RGB(255, 255, 255)); } else { //貼背景 BitBlt(hdcBuffer, 0, 0, sBmp[i].cx, sBmp[i].cy, hdcBmp, 0, 0, SRCCOPY); } }
(4)最後將hdcBuffer(也就是內存中)中繪製好的位圖貼到原客戶區hdc中
//將內存緩衝DC hdcBuffer中所繪製的位圖統一貼到客戶區DC中(這是在顯示屏上操作的) BitBlt(hdc, 0, 0, sClient.cx, sClient.cy, hdcBuffer, 0, 0, SRCCOPY);
以下是窗口回調函數的源代碼:(內含詳盡註釋):
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { // 背景、飛機位圖 內存兼容位圖 static HBITMAP hBmp[N], cptBmp; // 背景、飛機位圖大小 客戶區大小 static SIZE sBmp[N], sClient; static int y;//飛機的y座標 BITMAP bmp; HINSTANCE hInstance; HDC hdc, hdcBmp, hdcBuffer; PAINTSTRUCT ps; int i; switch(message) { case WM_CREATE: //加載背景以及飛機的位圖 hInstance = ((LPCREATESTRUCT)lParam)->hInstance; for(i=0; i<N; i++) { hBmp[i] = LoadBitmap(hInstance, MAKEINTRESOURCE(iBmpNames[i])); GetObject(hBmp[i], sizeof(BITMAP), &bmp); sBmp[i].cx = bmp.bmWidth; sBmp[i].cy = bmp.bmHeight; } //設置定時器 SetTimer(hwnd, TIMER, 1, NULL); return 0; case WM_SIZE: sClient.cx = LOWORD(lParam); sClient.cy = HIWORD(lParam); //創建內存兼容位圖cptBmp hdc = GetDC(hwnd); cptBmp = CreateCompatibleBitmap(hdc, sClient.cx, sClient.cy); ReleaseDC(hwnd, hdc); return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); //用於貼位圖的內存DC hdcBmp = CreateCompatibleDC(hdc); //用於緩衝的內存DC hdcBuffer = CreateCompatibleDC(hdc); //將內存位圖選入緩衝內存DC中——以便可以繪製多個位圖 SelectObject(hdcBuffer, cptBmp); //將背景和飛機都先貼到內存緩衝DC hdcBuffer中(這是在內存中操作的) for(i=0; i<N; i++) { SelectObject(hdcBmp, hBmp[i]); if(i > 0) { //貼飛機(透明貼法,上次講過) TransparentBlt(hdcBuffer, sClient.cx * (i-1) / N, y - 128, sBmp[i].cx, sBmp[i].cy / (SMALL + i-1), hdcBmp, 0, 0, sBmp[i].cx, sBmp[i].cy / (SMALL + i-1), RGB(255, 255, 255)); } else { //貼背景 BitBlt(hdcBuffer, 0, 0, sBmp[i].cx, sBmp[i].cy, hdcBmp, 0, 0, SRCCOPY); } } //將內存緩衝DC hdcBuffer中所繪製的位圖統一貼到客戶區DC中(這是在顯示屏上操作的) BitBlt(hdc, 0, 0, sClient.cx, sClient.cy, hdcBuffer, 0, 0, SRCCOPY); //注意回收內存資源 DeleteDC(hdcBmp); DeleteDC(hdcBuffer); EndPaint(hwnd, &ps); return 0; //用定時器改變飛機的y座標 case WM_TIMER: //控制y座標,使飛機在窗口中不斷的移動 y = (y + 1) % (sClient.cy + 128); InvalidateRect(hwnd, NULL, FALSE);//重繪 return 0; case WM_DESTROY: //回收資源 for(i=0; i<N; i++) DeleteObject(hBmp[i]); DeleteObject(cptBmp); //銷燬定時器 KillTimer(hwnd, TIMER); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }
最後來看看實現的效果:
可能這裏的gif動畫顯示的效果不是很佳(可能csdn對gif的支持效果不是很好) ,不過實際效果應該是很好的,各位不妨試一下^_^?
ok,今天就先到此吧,我還得努力coding——希望能儘快將微信打飛機遊戲(C語言版)呈現在各位的視野中^_^