用雙緩衝技術實現真正的平滑

本文由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語言版)呈現在各位的視野中^_^

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章