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

本文介绍如何实现平滑的角色移动,并解决常见的闪屏问题。通过使用BitBlt和TransparentBlt函数,实现角色的透明效果。此外,文章还讨论了如何通过调整背景刷设置来消除闪屏现象。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文由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,来看看解决后的效果:

 

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

 

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

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

 

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

RK3566 Android12平板 通过按键反复点亮屏幕和息屏,在点亮屏幕是会闪屏,应该如何修改屏参。相关配置如下,应该如何修改 #include <dt-bindings/display/media-bus-format.h> / { backlight: backlight { compatible = "pwm-backlight"; pwms = <&pwm4 0 25000 0>; brightness-levels = < 0 20 20 21 21 22 22 23 23 24 24 25 25 26 26 27 27 28 28 29 29 30 30 31 31 32 32 33 33 34 34 35 35 36 36 37 37 38 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 >; default-brightness-level = <200>; status = "okay"; }; vcc3v3_lcd0_n: vcc3v3-lcd0-n { compatible = "regulator-fixed"; regulator-name = "vcc3v3_lcd0_n"; enable-active-high; gpio = <&gpio0 RK_PC7 GPIO_ACTIVE_HIGH>; pinctrl-names = "default"; pinctrl-0 = <&vcc3v3_lcd0_en>; regulator-state-mem { regulator-off-in-suspend; }; }; panel { compatible = "simple-panel"; backlight = <&backlight>; power-supply=<&vcc3v3_lcd0_n>; enable-gpios = <&gpio0 RK_PC4 GPIO_ACTIVE_HIGH>; reset-gpios = <&gpio0 RK_PA6 GPIO_ACTIVE_LOW>; pinctrl-names = "default"; pinctrl-0 = <&lcd_enable_gpio>, <&lcd_rst_gpio>; enable-delay-ms = <100>; prepare-delay-ms = <20>; unprepare-delay-ms = <20>; reset-delay-ms = <50>; disable-delay-ms = <50>; bus-format = <MEDIA_BUS_FMT_RGB666_1X18>; width-mm = <217>; height-mm = <136>; display-timings { native-mode = <&timing0>; timing0: timing0 { clock-frequency = <72000000>; hactive = <1280>; vactive = <800>; hback-porch = <80>; hfront-porch = <40>; vback-porch = <24>; vfront-porch = <6>; hsync-len = <20>; vsync-len = <8>; hsync-active = <0>; vsync-active = <0>; de-active = <0>; pixelclk-active = <0>; }; }; ports { #address-cells = <1>; #size-cells = <0>; port@0 { reg = <0>; dual-lvds-even-pixels; panel_in_lvds: endpoint { remote-endpoint = <&lvds_out_panel>; }; }; }; }; }; &pwm4 { status = "okay"; }; &i2c5 { gt9xx: gt9xx@14 { compatible = "goodix,gt9xx"; reg = <0x14>; pinctrl-names = "default"; pinctrl-0 = <&touch_gpio>; touch-gpio = <&gpio3 RK_PB0 IRQ_TYPE_LEVEL_HIGH>; reset-gpio = <&gpio3 RK_PB1 GPIO_ACTIVE_HIGH>; max-x = <1200>; max-y = <1920>; tp-size = <9110>; tp-supply = <&vcc_3v3>; status = "disabled"; }; ts@40 { compatible = "gslX680-pad"; reg = <0x40>; pinctrl-names = "default"; pinctrl-0 = <&touch_gpio>; touch-gpio = <&gpio3 RK_PB0 IRQ_TYPE_LEVEL_HIGH>; reset-gpio = <&gpio3 RK_PB1 GPIO_ACTIVE_HIGH>; screen_max_x = <1280>; screen_max_y = <800>; chip_id = <1>; //0:gslx680, 1:GSL3670 revert_x = <0>; //x rotate revert_y = <0>; //y rotate revert_xy = <1>; //xy swap }; }; ///////////////////////////// lvds ////////////////////////////////////////////////////// &lvds { status = "okay"; ports { port@1 { reg = <1>; lvds_out_panel: endpoint { remote-endpoint = <&panel_in_lvds>; }; }; }; }; &lvds_in_vp1 { status = "okay"; }; &lvds_in_vp2 { status = "disabled"; }; &route_lvds { status = "okay"; connect = <&vp1_out_lvds>; }; ///////////////////////////// dsi0 /////////////////////////////////////////////////////// &dsi0 { status = "disabled"; }; &dsi0 { status = "disabled"; }; &dsi0_in_vp0 { status = "disabled"; }; &dsi0_in_vp1 { status = "disabled"; }; &route_dsi0 { connect = <&vp0_out_dsi0>; status = "disabled"; }; &dsi1 { status = "disabled"; }; &dsi1_in_vp0 { status = "disabled"; }; &dsi1_in_vp1 { status = "disabled"; }; &hdmi { status = "okay"; }; &hdmi_in_vp0 { status = "okay"; }; &hdmi_in_vp1 { status = "disabled"; }; &route_hdmi { status = "okay"; connect = <&vp0_out_hdmi>; }; &video_phy0 { status = "okay"; }; &video_phy1 { status = "disabled"; }; &vop { status = "okay"; assigned-clocks = <&cru DCLK_VOP0>, <&cru DCLK_VOP1>; assigned-clock-parents = <&pmucru PLL_HPLL>, <&cru PLL_VPLL>; }; &vop_mmu { status = "okay"; };
最新发布
07-23
评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值