本文由BlueCoder編寫 轉載請說明出處:
http://blog.csdn.net/crocodile__/article/details/11581641
我的郵箱:[email protected] 歡迎大家和我交流編程心得
我的微博:BlueCoder_黎小華 歡迎光臨^_^
今天咋一看,發現很久沒寫博客了
的確,開學之後,寫博客的時間越來越少了……
今天來做一個比較實用的小應用——平滑的人物走動,同時解決常見的閃屏問題、實現透明位圖
這些技術在遊戲開發中是很常見的
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
一、爲了對比效果差異,我們先就用之前講過的BitBlt函數來直接貼位圖
先來看一看一些主要的代碼:
變量說明:
static HBITMAP hBk, hBmp; //背景、人物位圖句柄 static SIZE sBk, sBmp, sClient; //背景、人物位圖大小 , 客戶區大小 static POINT ptBmp; //人物位圖位置
在WM_CREATE消息中做一些初始化工作:
case WM_CREATE: { //加載位圖資源 BITMAP bmp; hBmp = LoadBitmap(((LPCREATESTRUCT)lParam)->hInstance, MAKEINTRESOURCE(IDB_BITMAP1)); hBk = LoadBitmap(((LPCREATESTRUCT)lParam)->hInstance, MAKEINTRESOURCE(IDB_BITMAP2)); GetObject(hBmp, sizeof(BITMAP), &bmp); sBmp.cx = bmp.bmWidth; sBmp.cy = bmp.bmHeight; GetObject(hBk, sizeof(BITMAP), &bmp); sBk.cx = bmp.bmWidth; sBk.cy = bmp.bmHeight; } //初始化人物位置 ptBmp.x = 100; ptBmp.y = 100; return 0;
在WM_SIZE消息中獲取客戶區大小
case WM_SIZE: sClient.cx = LOWORD(lParam); sClient.cy = HIWORD(lParam); return 0;
在WM_PAINT消息中繪製位圖
case WM_PAINT: hdc = BeginPaint(hwnd, &ps); hdcMem = CreateCompatibleDC(hdc); SelectObject(hdcMem, hBk); //由於背景圖片可能超過客戶區大小 , 故採取縮放模式顯示背景圖片 SetStretchBltMode(hdc, COLORONCOLOR); StretchBlt(hdc, 0, 0, sClient.cx, sClient.cy, hdcMem, 0, 0, sBk.cx, sBk.cy, SRCCOPY); //繪製人物位置 SelectObject(hdcMem, hBmp); BitBlt(hdc, ptBmp.x, ptBmp.y, sBmp.cx, sBmp.cy, hdcMem, 0, 0, SRCCOPY); DeleteDC(hdcMem); EndPaint(hwnd, &ps); return 0;
在WM_MOUSEMOVE消息中控制人物位置
//鼠標移動時,這個消息會發送很多, //因此用它來檢驗閃屏效果是很理想的 case WM_MOUSEMOVE: ptBmp.x = LOWORD(lParam); ptBmp.y = HIWORD(lParam); InvalidateRect(hwnd, NULL, TRUE); return 0;
下面是BitBlt函數的實現效果:(可以發現人物周邊出現了白色區域)
可見這和實際遊戲中是有差別的
二、實現位圖的透明
實現之前,先來看一看一個win32 sdk中的含api函數TransparentBlt
msdn:
BOOL TransparentBlt( HDC hdcDest, // handle to destination DC int nXOriginDest, // x-coord of destination upper-left corner int nYOriginDest, // y-coord of destination upper-left corner int nWidthDest, // width of destination rectangle int hHeightDest, // height of destination rectangle HDC hdcSrc, // handle to source DC int nXOriginSrc, // x-coord of source upper-left corner int nYOriginSrc, // y-coord of source upper-left corner int nWidthSrc, // width of source rectangle int nHeightSrc, // height of source rectangle UINT crTransparent // color to make transparent );
前10個參數和BitBlt的差不多,不用多解釋。主要是最後一個參數crTransparent,當前位圖中需要透明的顏色(一般都是白色或者黑色)
==> 因此,你應該保證非透明區域不能包含透明顏色,否則會有一定的出入
另外還需要注意的一點:Transparent函數只適合低於32位色位圖的透明,當然常見的都是RGB原色——24位的,因此它是夠用的
只需要將WM_PAINT中的BitBlt換成Transparent就能實現久違的位圖透明效果
TransparentBlt(hdc, ptBmp.x, ptBmp.y, sBmp.cx - 10, sBmp.cy - 10,
hdcMem, 0, 1, sBmp.cx, sBmp.cy - 1, RGB(255, 255, 255));
下面就是實現效果:
可以發現,透明效果是實現了,但是閃屏確實很厲害……
三、解決閃屏問題
要解決問題,需要知道問題的根源所在:
各位還記得WNDCLASS這個類型的結構體變量嗎?
它在註冊窗口前需要初始化,我們來看看初始化代碼:
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
對,問題就出現在這裏,我們設置了背景刷爲白色的刷子,那麼當你重繪客戶區的時候,程序就會使用你默認設定的這個白色刷子來刷背景,由於鼠標移動消息很頻繁,因此就會看到很厲害的閃屏
那麼,解決方法就很簡單了,主要有兩種方式:
(1)將背景刷設定爲NULL,空刷子——透明的刷子
wndclass.hbrBackground = NULL;
(2)不改變背景刷(依然使用白色背景刷子),只是在試窗口無效時,我們選擇不重繪背景,具體就是將InvalidateRect的最後一個參數設定爲FALSE
case WM_MOUSEMOVE:
ptBmp.x = LOWORD(lParam);
ptBmp.y = HIWORD(lParam);
InvalidateRect(hwnd, NULL, FALSE);//這裏設爲FALSE
return 0;
ok,來看看解決後的效果:
可見頻繁的閃屏解決了^_^
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
今天到此爲止吧(如果各位需要源代碼或者相應資源,可以評論留下郵箱,我會發給你^_^)