读书人

lt;Win32_19gt;用双缓冲技术实现真个的平滑

发布时间: 2013-10-08 16:55:16 作者: rapoo

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

今年暑假自学了Win32 SDK , 从初学到现在,还是颇有收获。不过既然学了,就得学有所用。

我们都知道IT行业中有两大方向:软件和游戏(其实网络是嵌入在二者之中的)。

之前写了一个集音乐、视频播放于一身的简易播放器——说白了就是一个小软件。那么下一步,就是写一个游戏——纯Win32 C语言,不适用任何游戏引擎——打飞机游戏。

虽然游戏还在创作中(今天才开始,目前我正在努力coding,完成之后必将和各位分享),不过我还是想在写完这个游戏之前讲一个特别重要的技术——双缓冲技术

这个技术大家应该都不陌生,不过作为初学者的我,还是希望共享一下自己的心得,以供更多学习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中

//用于缓冲的内存DChdcBuffer = CreateCompatibleDC(hdc);

//创建内存兼容位图cptBmphdc = GetDC(hwnd);cptBmp = CreateCompatibleBitmap(hdc, sClient.cx, sClient.cy);ReleaseDC(hwnd, hdc);

//将内存位图选入缓冲内存DC中——以便可以绘制多个位图SelectObject(hdcBuffer, cptBmp);

(3)一次性贴多个位图 , 那么还应该获取一个关于hdc的内存DC——hdcBmp , 将位图依次选入hdcBmp中,然后将位图从hdcBmp贴到hdcBuffer中

//用于贴位图的内存DChdcBmp = 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 HBITMAPhBmp[N], cptBmp;//    背景、飞机位图大小 客户区大小static SIZEsBmp[N], sClient;static inty;//飞机的y坐标BITMAPbmp;HINSTANCEhInstance;HDChdc, hdcBmp, hdcBuffer;PAINTSTRUCTps;inti;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);//创建内存兼容位图cptBmphdc = GetDC(hwnd);cptBmp = CreateCompatibleBitmap(hdc, sClient.cx, sClient.cy);ReleaseDC(hwnd, hdc);return 0;case WM_PAINT:hdc = BeginPaint(hwnd, &ps);//用于贴位图的内存DChdcBmp = CreateCompatibleDC(hdc);//用于缓冲的内存DChdcBuffer = 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);}


最后来看看实现的效果:

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

可能这里的gif动画显示的效果不是很佳 ,不过实际效果应该是很好的,各位不妨试一下^_^?

ok,今天就先到此吧,我还得努力coding——希望能尽快将微信打飞机游戏(C语言版)呈现在各位的视野中^_^

1楼u010475354昨天 11:17

读书人网 >编程

热点推荐