`
444878909
  • 浏览: 635653 次
文章分类
社区版块
存档分类
最新评论

<Win32_19>用双缓冲技术实现真正的平滑

 
阅读更多

今年暑假自学了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语言版)呈现在各位的视野中^_^

分享到:
评论

相关推荐

    C#编程经验技巧宝典

    16&lt;br&gt;&lt;br&gt;0033 Return语句的使用 17&lt;br&gt;&lt;br&gt;0034 如何实现无限循环 17&lt;br&gt;&lt;br&gt;0035 巧用foreach语句控制控件 18&lt;br&gt;&lt;br&gt;0036 有效使用switch case语句 18&lt;br&gt;&lt;br&gt;2.3 运算符 19&lt;br&gt;&lt;br&gt;0037 如何使用...

    LINUX系统编程----<>(简称LSP)中文版

    &lt;&lt;Linux System Prorgramming&gt;&gt;(简称LSP)中文版 简介和主要概念 文件I/O 缓冲输入输出 高级文件I/O 进程管理 高级进程管理 文件与目录管理 内存管理 信号 时间

    stm32定时器+ADC+DMA+双缓冲 实现数据采集.rar_STM32双缓冲_STM32数据_adc双缓冲_stm32 dm

    stm32定时器+ADC+DMA+双缓冲 实现数据采集

    wince.net下串口 VB 源码

    &lt;br&gt;使用方法:&lt;br&gt;&lt;br&gt;Dim m_cs232 As New CRs232&lt;br&gt;&lt;br&gt;m_cs232.openPort() '打开串口&lt;br&gt;Dim send() As Byte = {&H1, &H2, &H3}&lt;br&gt;m_cs232.write(send) '发送&lt;br&gt;&lt;br&gt;Dim i as integer = m_cs232.GetReadBuffer...

    ORACLE精品脚本笔记

    &lt;br&gt;&lt;br&gt;查看碎片程度高的表 &lt;br&gt;&lt;br&gt;SELECT segment_name table_name , COUNT(*) extents &lt;br&gt;FROM dba_segments WHERE owner NOT IN ('SYS', 'SYSTEM') GROUP BY segment_name &lt;br&gt;HAVING COUNT(*) = (SELECT MAX...

    jive.chm

    &lt;br&gt; 2 jcs学习笔记 &lt;br&gt; 3 关于Hibernate的Cache问题 &lt;br&gt; 4 用缓冲技术提高JSP应用的性能和稳定性 &lt;br&gt; 5 SwarmCache入门 &lt;br&gt;&lt;br&gt; &lt;br&gt; &lt;br&gt;源代码研究&lt;br&gt; 1 Jive中的全局配置 &lt;br&gt; 2 Jive源代码情景分析-index....

    RRGRID

    GRID_LINE_COLOR WM_GRID_USER_OUTER + 32&lt;br&gt;&lt;br&gt;//设置选择行在Grid没有焦点时前景色和背景色&lt;br&gt;//wParam -- 前景色,lParam -- 背景色&lt;br&gt;#define GM_SET_NOFOCUS_SELLINE_COLOR WM_GRID_USER_OUTER + 33...

    WIN32GDI双缓冲打螃蟹小游戏

    WIN32 APP下使用GDI双缓冲技术做的简单的2D射击游戏

    DMA.zip_ADC DMA双缓冲_STM32F407+ADC+DMA_c51 dma_dma双缓冲区adc_双缓冲DMA

    在keil环境下基于stm32F407的DMA的双缓冲的ADC数据采集,这是一个完整的工程,可以直接下载使用

    Oracle 性能调整(真正由ORACLE甲骨文出品)

    – 解决性能问题的步骤 &lt;br&gt; 分析症状 &lt;br&gt; 确定问题范围 &lt;br&gt; 参数调整or &lt;br&gt; 结构调整or &lt;br&gt;&lt;br&gt; 应用调整 &lt;br&gt; 性能监控 &lt;br&gt;&lt;br&gt;Shared Pool &lt;br&gt; – SHARED_POOL_SIZE 控制共享SQL缓冲存储区和数据字 &lt;br&gt; ...

    韩国zeroboardXE ver 1.0.1 整站程序

    &lt;br&gt;&lt;br&gt;&lt;br&gt;安装包 : zbxe.1.0.1.zip 或 zbxe.1.0.1.tgz &lt;br&gt;&lt;br&gt;升级补丁(从v1.0.0) : zbxe.1.0.1.changed.zip &lt;br&gt;&lt;br&gt;&lt;br&gt;使用手册及参考文档&lt;br&gt;&lt;br&gt;&lt;br&gt;官方手册(韩文) &lt;br&gt;&lt;br&gt;&lt;br&gt;升级时的注意事项&lt;br&gt;&lt;br&gt;...

    封包截取器

    故不能取到自己的ip和端口)&lt;br&gt;&lt;br&gt;5、增加Toolbar,方便使用&lt;br&gt;&lt;br&gt;6、增加自动开缓冲功能(普通模式在一些特殊应用网络数据量很大时,列表内的数据可能会丢失,此功能可避免此问题)&lt;br&gt;&lt;br&gt;&lt;br&gt;2006.4.6(VER:...

    STM32+定时器+ADC+DMA+双缓冲 实现数据采集.rar_ADC定时器_DMA 双缓冲_stm32 定时dma_stm

    STM32+定时器+ADC+DMA+双缓冲 实现数据采集

    linux下缓冲区溢出攻击源代码

    经典缓冲区溢出攻击源代码...《&lt;br&gt;&lt;br&gt;深入理解计算机系统》一书中使用到的例子,我将这个例子进行&lt;br&gt;&lt;br&gt;了详细的分析,并进行了实现。提供的包中包含我在设计过程中&lt;br&gt;&lt;br&gt;遇到的各种设计难题和技术难题。欢迎下载

    双缓冲技术.rar

    双缓冲技术.rar 包含代码+文档+各种资料 我自己整理的 双缓冲技术.rar 包含代码+文档+各种资料 我自己整理的 双缓冲技术.rar 包含代码+文档+各种资料 我自己整理的 双缓冲技术.rar 包含代码+文档+各种资料 我自己...

    Java双缓冲技术.doc

    本文从实例出发,着重介绍了用双缓冲消除闪烁的原理以及双缓冲在Java中的两种常用实现方法(即在update(Graphics g)中实现和在paint(Graphics g)中实现),以期读者能对双缓冲在Java编程中的应用能有个较全面的认识...

    MiniDraw.rar_MFC 双缓冲_MFC双缓冲绘图_mfc 绘图 双_minidraw_双缓冲

    使用MFC文档视图结构,采用双缓冲绘图技术写的gdi绘图程序

    CustomPlayer_qt+ffmpeg_QT_qt双缓冲_qtffmpeg_QtPlayer.zip

    CustomPlayer_qt+ffmpeg_QT_qt双缓冲_qtffmpeg_QtPlayer.zip

    vc 双缓冲技术

    vc 双缓冲技术

Global site tag (gtag.js) - Google Analytics