<Win32_18>平滑的人物走动 —— 解决闪屏

本文由BlueCoder编写   转载请说明出处:

http://blog.csdn.net/crocodile__/article/details/11581641

我的邮箱:bluecoder@yeah.net    欢迎大家和我交流编程心得

我的微博: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,来看看解决后的效果:

 

可见频繁的闪屏解决了^_^

 

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

今天到此为止吧(如果各位需要源代码或者相应资源,可以评论留下邮箱,我会发给你^_^)

  • 10
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 30
    评论
评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值