<Win32_18>平滑的人物走动 —— 解决闪屏
今天咋一看,发现很久没写博客了
的确,开学之后,写博客的时间越来越少了……
今天来做一个比较实用的小应用——平滑的人物走动,同时解决常见的闪屏问题、实现透明位图
这些技术在游戏开发中是很常见的
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
一、为了对比效果差异,我们先就用之前讲过的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的最后一个参数设定为TRUE
case WM_MOUSEMOVE: ptBmp.x = LOWORD(lParam); ptBmp.y = HIWORD(lParam); InvalidateRect(hwnd, NULL, FALSE);//这里设为FALSE return 0;
ok,来看看解决后的效果:
可见频繁的闪屏解决了^_^
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
今天到此为止吧(如果各位需要源代码或者相应资源,可以评论留下邮箱,我会发给你^_^)