Windows游戏图形基础(下)

发表于2017-10-24
评论0 2.2k浏览

Windows游戏图形基础(上),下面开始下半部分的内容介绍,一起来看看吧。


1.6.6 示例程序GDldemo1

在这个示例程序中,我们建立7 种系统提供的画笔以及画刷样式,用随机函数发生“ 二人组”srand()与rand()对各画笔与画刷的颜色值分量进行随机地初始化, 并将结果绘制在窗口中.
好了,开始贴详细注释的程序源代码。
程序代码片段一, 全局变量声明:
  1. //-----------------------------------【全局变量声明部分】-------------------------------------  
  2. //  描述:全局变量的声明  
  3. //------------------------------------------------------------------------------------------------  
  4. HDC         g_hdc=NULL;       //全局设备环境句柄  
  5. HPEN        g_hPen[7]={0}; //定义画笔句柄的数组  
  6. HBRUSH  g_hBrush[7]={0}; //定义画刷句柄的数组  
  7. int             g_iPenStyle[7] = {PS_SOLID,PS_DASH,PS_DOT,PS_DASHDOT,PS_DASHDOTDOT,PS_NULL,PS_INSIDEFRAME};  //定义画笔样式数组并初始化  
  8. int             g_iBrushStyle[6] = {HS_VERTICAL,HS_HORIZONTAL,HS_CROSS,HS_DIAGCROSS,HS_FDIAGONAL,HS_BDIAGONAL};  //定义画刷样式数组并初始化  

程序代码片段二, Game_lnit() 函数:
  1. //-----------------------------------【Game_Init( )函数】--------------------------------------  
  2. //  描述:初始化函数,进行一些简单的初始化  
  3. //------------------------------------------------------------------------------------------------  
  4. BOOL Game_Init( HWND hwnd )  
  5. {  
  6.     g_hdc = GetDC(hwnd);  //获取设备环境句柄  
  7.     srand((unsigned)time(NULL));      //用系统时间初始化随机种子   
  8.   
  9.     //随机初始化画笔和画刷的颜色值  
  10.     for(int i=0;i<=6;i )  
  11.     {  
  12.             g_hPen[i] = CreatePen(g_iPenStyle[i],1,RGB(rand()%6,rand()%6,rand()%6));  
  13.         if(i==6)  
  14.             g_hBrush[i] = CreateSolidBrush(RGB(rand()%6,rand()%6,rand()%6));  
  15.         else  
  16.             g_hBrush[i] = CreateHatchBrush(g_iBrushStyle[i],RGB(rand()%6,rand()%6,rand()%6));  
  17.     }  
  18.   
  19.     Game_Paint(hwnd);  
  20.     ReleaseDC(hwnd,g_hdc);  
  21.     return TRUE;  
  22. }  
程序代码片段三, Game_ Paint()函数:
  1. //-----------------------------------【Game_Paint( )函数】--------------------------------------  
  2. //  描述:绘制函数,在此函数中进行绘制操作  
  3. //--------------------------------------------------------------------------------------------------  
  4. VOID Game_Paint( HWND hwnd )  
  5. {  
  6.     //定义一个y坐标值  
  7.     int y=0;  
  8.   
  9.     //一个for循环,用7种不同的画笔绘制线条  
  10.     for(int i=0;i<=6;i )  
  11.     {  
  12.         y = (i 1) * 70;  
  13.   
  14.         SelectObject(g_hdc,g_hPen[i]);//将对应的画笔选好  
  15.         MoveToEx(g_hdc,30,y,NULL);   //“光标”移动到对应的(30,y)坐标处  
  16.         LineTo(g_hdc,100,y);             //从(30,y)坐标处向(100,y)绘制线段  
  17.     }  
  18.   
  19.     /*注意上面画完后y=420,下面画矩形的时候还有用*/  
  20.     //定义两个x坐标值  
  21.     int x1 = 120;  
  22.     int x2 = 190;  
  23.   
  24.     //用7种不同的画刷填充矩形  
  25.     for(int i=0;i<=6;i )  
  26.     {  
  27.         SelectObject(g_hdc,g_hBrush[i]);  //选用画刷  
  28.         Rectangle(g_hdc,x1,70,x2,y);     //画出一个封闭的矩形,矩形左上角坐标为(x1,50),右下角坐标为(x2,y)  
  29.         x1  = 90;  
  30.         x2  = 90;  
  31.     }  
  32. }  
程序代码片段四, Game_ Clean Up()函数:
  1. //-----------------------------------【Game_CleanUp( )函数】--------------------------------  
  2. //  描述:资源清理函数,在此函数中进行程序退出前资源的清理工作  
  3. //---------------------------------------------------------------------------------------------------  
  4. BOOL Game_CleanUp( HWND hwnd )  
  5. {  
  6.     //一个for循环,释放掉所有的画笔和画刷句柄  
  7.     for (int i=0;i<=6;i )  
  8.     {  
  9.         DeleteObject(g_hPen[i]);  
  10.         DeleteObject(g_hBrush[i]);  
  11.     }  
  12.     return TRUE;  
  13. }  
好了,代码贴得差不多了。运行这个程序,我们便会得到如下的画面。

而且每次运行时, 都会得到不同的画笔和画刷颜色画出的图案,说起来这个程序还并不是一无是处, 还是具有一定的观赏性的。

1.7 文字的输出

文字的显示在游戏编程中也是非常重要的内容,显示出赏心悦目的彩色文字甚至是特效字体能给游戏加上不少印象分。而在GDI 中,有着不少API 函数用于方便我们将文字信息输出到屏幕上。
DirectX中的2D文字显示函数, 就借助了GDI 中文字显示的一些API 函数,只是对它们进行了再封装而己。
在GDI 中,我们可以采用TextOut 函数、DrawText 函数、DrawTextEx 函数等等来输出文字。

1.7.1 最常用文字输出函数TextOut

TextOut 函数的作用是用当前选择的字符、背景颜色和正文颜色将一个字符串写到指定位置,简单点说,就是在指定地方输出一段
文字。我们先来看一下它的原型:
  1. BOOL TextOut(  
  2.   __in  HDC hdc,   //需要进行文本输出的DC的句柄  
  3.   __in  int nXStart, //开始书写的位置的x 坐标  
  4.   __in  int nYStart, // 开始书写的位置的Y 坐标  
  5.   __in  LPCTSTR lpString,  //指向字符串的指针  
  6.   __in  int cbString   //字符串的字符数  
  7. );  
其中的参数很好理解,首先TextOut 函数的第一个参数DC 句柄是指明大方向的, 表示为哪个DC 进行绘制。第二第三个参数分别是需要进行文本显示的位置的横纵坐标的值。第四个参数是指向字符串的指针, 最后一个参数是这段文本长度的整型值。需要特别注意的是,文本默认颜色是黑色,默认的背景色是WHITE_BRUSH 。后面我们会讲解如何改变默认的颜色、背景,以及选择自己喜欢的字体。
来看一个使用的实例吧:
  1. wchar_t textl [] =L ”要有最朴素的生活,与最遥远的梦想,即使明日天寒地冻,路远马亡”;  
  2. TextOut(g_hdc, 30, 150, textl, wcslen(textl)) ;  

1.7.2 进阶文字输出函数

进阶文字输出函数我们通常指的是DrawText 。MSDN 中对DrawText 函数的描述是,在指定的矩形里写入格式化文本,且根据指定的方法对文本格式化(扩展的制表符,字符对齐、折行等〉。
我们看一下他的原型:
  1. int DrawText (  
  2.   HDC hDC, // 需要进行文本输出的DC设备环境句柄  
  3.   LPCTSTR lpString, // 指向需要写入的字符串的指针  
  4.   int nCount, // 需要写入的字符串的长度  
  5.   LPRECT lpRect, // 指向包含了绘制区域的矩形结构体的指针  
  6.   UINT uFormat  // 书写模式的标识,有多种定制标识可以选择  
  7. } ;  

1.7.3 设置文字颜色

我们通常在GDI 中,可以用SetTextColor 函数来设置文字的颜色值。在MSDN 中查到它的原型如下:
  1. COLORREF SetTextColor(  
  2.   __in  HDC hdc,  //设备环境句柄  
  3.   __in  COLORREF crColor  //需要设置的文本颜色  
  4. );  
给大家一个调用实例:
  1. SetTextColor (g_hdc , RGB (S0 , 255 , 50) );  

1.7.4 设置文字背景透明

在GDI 中,默认的文字输出背景为白色, 这样输出文字的地方背景就被破坏了,显得非常不美观。为了能使文字输出的背景透明,就需要调用一下SetBkMode 函数,进行背景模式的设置。
我们可以在MSDN 中查到SetBkMode函数的原型如下:
  1. int SetBkMode(  
  2.   __in  HDC hdc,     //设备环境的句柄  
  3.   __in  int iBkMode  //mode 是要设置的模式,我们常设为TRANSPARENT.  
  4. );  
这里需要注意, GDI 在默认情况下输出文字时背景颜色为OPAQUE,为白色,这样显得不雅观。所以我们一般在输出文字之前, 都调用一次SetBkMode , 且在调用时将SetBkMode 的第二个参数设为TRANSPARENT。这样之后我们输出的文字、背景就透明了。
我们用如下的代码来将文字背景色设为透明:
  1. SetBkMode(g_hdc, TRANSPARENT);  // 设置输出文字背景色为透明  
这句代码一般在初始化的时候调用一次就好了, 这样后面我们输出的文字都是背景为透明了。

1.7.5 字体的创建

现在市面上各式各样的TTF 字体显得原来越好看,比如“张海山锐线简体”,还有“时尚中黑简体” 等等。
下面我们开始介绍用于字体创建的函数——CreateFont。这个函数创建一种有很多属性的逻辑字体, 字体可以在创建后用
SelectObject 选进我们的设备环境就行了
接我们字体创建函数CreateFont 在MSDN 中的定义:
  1. HFONT CreateFont(  
  2.   __in  int nHeight,   //字体的逻辑高度  
  3.   __in  int nWidth,  
  4.   __in  int nEscapement,  
  5.   __in  int nOrientation,  
  6.   __in  int fnWeight,  
  7.   __in  DWORD fdwItalic,  
  8.   __in  DWORD fdwUnderline,  
  9.   __in  DWORD fdwStrikeOut,  
  10.   __in  DWORD fdwCharSet,  //所需的字符集  
  11.   __in  DWORD fdwOutputPrecision,  
  12.   __in  DWORD fdwClipPrecision,  
  13.   __in  DWORD fdwQuality,  
  14.   __in  DWORD fdwPitchAndFamily,  
  15.   __in  LPCTSTR lpszFace   //字体名称  
  16. );  
  1. HFONT hFont=CreateFont(45,0,0,0,0,0, 0, 0, GB2312_CHARSET, 0, 0, 0, 0, TEXT("张海山锐线简体"));  
其中第一个参数为我们期望的字体大小, GB3I2_CHARSET是我们常用的字符集, 最后一个参数中设置我们想要设置的字体的中文名称,这里我们选的是“张海山锐线简体”, 当然需要你的电脑字体库中安装了这种字体,如果没安装的话,就会显示默认的那种很丑很没风格的字体。大家怕出现很丑的默认字体的话,可以选现在Windows7中自带的还算蛮好看的“微软雅黑”;

1.7.6 总结

关于游戏程序中的文字显示, 掌握这5 个API 函数基本上就够用了。另外还有一个专门设置文字背景颜色的SetBkColor函数,大家知道就可以了,我们用得很少。

1.7.7 示例程序GDIdemo2

这是一个关于字体的创建、彩色文字显示的示例程序。主要的实现代码都在Game_ Paint()函数中,我们把它贴出来讲解一下。
程序代码片段,Game_ Paint()函数:
  1. //-----------------------------------【Game_Paint( )函数】--------------------------------------  
  2. //  描述:绘制函数,在此函数中进行绘制操作  
  3. //--------------------------------------------------------------------------------------------------  
  4. VOID Game_Paint( HWND hwnd )  
  5. {  
  6.     HFONT hFont=CreateFont(30,0,0,0,0,0,0,0,GB2312_CHARSET,0,0,0,0,L"微软雅黑");  //创建一种字体  
  7.     SelectObject(g_hdc,hFont);  //将字体选入设备环境中  
  8.     SetBkMode(g_hdc, TRANSPARENT);    //设置输出文字背景色为透明  
  9.   
  10.     //定义三段文字  
  11.     wchar_t text1[]=L"我们所有的梦想都可以成真,只要我们有勇气去追求它们。";  
  12.     wchar_t text2[]=L"All our dreams can come true, if we have the courage to pursue them. ";  
  13.     wchar_t text3[]=L"--------沃尔特 迪斯尼";  
  14.   
  15.     //设置文字颜色并输出第一段文字  
  16.     SetTextColor(g_hdc,RGB(50,255,50));  
  17.     TextOut(g_hdc,30,150,text1,wcslen(text1));  
  18.     //设置文字颜色并输出第二段文字  
  19.     SetTextColor(g_hdc,RGB(50,50,255));  
  20.     TextOut(g_hdc,30,200,text2,wcslen(text2));  
  21.     //设置文字颜色并输出第三段文字  
  22.     SetTextColor(g_hdc,RGB(255,150,50));  
  23.     TextOut(g_hdc,500,250,text3,wcslen(text3));  
  24.   
  25.     DeleteObject(hFont);//释放字体对象  
  26. }  
效果截图如下:

1.8 位图绘制基础

俗话说“ 2D 游戏是贴图的艺术,3D游戏是渲染的艺术”这里说的贴图,就是位图的绘制了,它是用GDI 写游戏最核心的基础知识。
位图属于GDI 中尤其重要的对象之一, 如果想用GDI 来开发游戏,我们需要运用大量的位图来丰富游戏的画面。GDI 游戏的画面主要是通过绘制位图来实现的。
为大家总结好的利用位图绘图的大体思路如下:
先调用Load.Bitmap 载入位图到位图句柄中,接着把位图句柄用SelectObject 选入设备环境,
再通过在设备环境中调用绘制位图的函数把位图绘制出来。下面, 我们通过一个四步曲来讲解,这样才显得直观易懂。

1.8.1 位图绘制四步曲

经过我们的总结, 位图从文件显示到程序窗口中一般可以通过以下简明扼要的四步曲:
  • 加载位图, 从文件中加载位图对象。
  • 建立兼容DC ,建立一个与窗口设备环境DC 兼容的内存设备环境DC .
  • 选用位图对象,内存DC使用步骤1中所建立的位图对象。
  • 进行贴图, 将内存DC 的内容贴到窗口DC 中.
下面我们把这四步曲分步详细展开, 说明如何按部就班地完成相关操作。
1 . 位图绘制四步曲之一: 加载位图
位图绘制四步曲之一: 加载位图。关键词, 加载( Load )。
一般我们都是从文件加载位图, 使用的是Loadlmage() 函数。
Loadlmage 函数不仅可以加载位图,还可用于加载图标、光标等图像资源。在MSDN 中,我们可以查到其原型声明如下所示。
  1. HANDLE LoadImage(  
  2.   __in  HINSTANCE hinst,  //来源实例句柄  
  3.   __in  LPCTSTR lpszName, //名称  
  4.   __in  UINT uType,       //加载的类型  
  5.   __in  int cxDesired,    // 指定存储的宽度  
  6.   __in  int cyDesired,    // 指定存储的高度  
  7.   __in  UINT fuLoad       //加载的方式,  
  8. );  
  • 第一个参数,HINSTANCE 类型的hinst ,包含被加载图像模型的实例句柄,若从硬盘文件或者资源文件中进行加载, 这个参数设为NULL 。
下面我们看一个调用实例:
  1. HBITMAP g_hBitmap = (HBITMAP)Loadimage(NULL,L"Dota2.bmp", IMAGE_BITMAP, 800, 600, LR_LOADFROMFILE); //加载位图  

这句代码表示从工程所在文件夹加载一幅名为Dota2 图片的bmp 文件到位图句柄g_hBitmap中,且加载后在内存中这幅图存储的宽为800 ,高为600 。

2. 位图绘制四步曲之二: 建立兼窑DC
位图绘制四步曲之二: 建立兼容DC 。关键词,兼容(Compatible)
为了绘制出位图,我们需要创建一个和设备环境兼容的内存设备环境,这样才会有暂时存放位图对象的地方。我们一会儿就是把位图对象用SelectObject 函数选入到内存设备环境中, 然后再用贴图函数,把内存设备环境中的内容贴到设备环境中。我们通常习惯把内存设备环境叫做内存DC,下文中我们就这样写。
内存DC 在这里起到中转站的作用,用于暂时存储加载好的位图, 由于我们最终会把暂存在内存DC 上这幅位图贴到真正的DC 上,所以这个DC 要做到与窗口DC 的无缝衔接, 在属性和性质上兼容。在GDI 中一般我们都调用CreateCompatible()函数来建立内存DC 。
CreateCompatibleDC 函数的作用就是创建一个与指定DC 相兼容的内存DC,它的原型声明如下:
  1. HDC CreateCompatibleDC(  
  2.   __in  HDC hdc  
  3. );  
唯一的一个参数,HDC 类型的hdc,填需要与内存DC 兼容的目的DC 的句柄。
内存DC 使用后也必须进行释放的操作。需要注意的是,释放内存DC 所调用的函数为DeleteDC() , 而不是ReleaseDC() 。
DeleteDC 用于撤销指定的设备环境,它的原型声明如下:
  1. BOOL DeleteDC(  
  2.   __in  HDC hdc  
  3. );  
这个函数唯一的一个参数HDC 类型的hdc , 指向需要撤销的设备描述表(DC )的句柄。
如果一个设备上下文环境的句柄是通过调用GetDC 函数得到的, 那么应用程序不能通过DeleteDC 删除该设备上下文环境,它应该调用ReleaseDC 函数来释放该设备上下文环境。
也就是说,有GetDC 的地方后面必然跟一个ReleaseDC,这就是ReleaseDC 存在的意义, 而DeleteDC 就是专用于撤销指定的设备环境的。

3 . 位图绘制四步曲之三:选用位图对象
位图绘制四步曲之三: 选用位图对象。关键词, 选用( Select ) 。
选用位图对象在GDI 中一般采用SelectObject()来实现。

4. 位图绘制四步曲之四:进行贴图
位图绘制四步曲之四: 进行贴图。关键词,贴图(BitBlt )。
前面的三步其实都是在为这步做准备。
在这一步当中,我们把内存DC 中的内容复制到设备环境DC 上,就实现了贴图的效果。这个操作所使用的函数有很多, 比如BitBlt()、TransparentBlt()或者StretchBlt() 。我们最常使用的是BitBIt 函数,它在GDI 编程中具有重要的意义。
BitBlt 函数的功能是从源矩形中复制一个位图到目标矩形,必要时按目前目标设备设置的模式进行图像的拉伸或压缩。
我们可以在MSDN 中查到它的原型如下:
  1. BOOL BitBlt(  
  2.   __in  HDC hdcDest,  //目标设备环境句柄  
  3.   __in  int nXDest,   //目的DC的x坐标  
  4.   __in  int nYDest,   //目的DC的y坐标  
  5.   __in  int nWidth,   //贴到目的DC的宽度  
  6.   __in  int nHeight,  //贴到目的DC的高度  
  7.   __in  HDC hdcSrc,   //源设备环境句柄  
  8.   __in  int nXSrc,    //来源DC的x坐标  
  9.   __in  int nYSrc,    //来源DC的y坐标  
  10.   __in  DWORD dwRop   //贴图方式  
  11. );  
  • 第九个参数,DWORD 类型的dwRop ,指定光栅操作代码,即贴图的方式。这些代码定义着源矩形区域的颜色数据,将如何与目标矩形区域的颜色数据组合以完成最后的颜色。
通过指定不同的光栅操作代码,我们可以制作出很多特殊的效果出来。对于目前我们的目的进行简单的贴图而言,只需把这个参数取为SRCCOPY , 表示将源矩形区域直接拷贝到目标矩形区域。
最后给一个调用实例,对着调用实例,大家就能容易了解BitBlt 函数的用法:
  1. BitBlt(g_hdc, 0, 0, 800, 600, g_mdc,0, 0, SRCCOPY); //采用BitBlt 函数贴阁,参数设置为窗口大小  
其中, g_mdc 为源设备环境DC, g_hdc 为目标设备环境DC,这就表示是从g_mdc 向g_hdc来贴图的。

5 . 提纲要领
我们将这位图绘制四步曲结合起来看,发现这四步分别都是一句代码,非常地直观,大家只要理解并消化这四句代码,就学会了位图的绘制。
  1.        //-----【位图绘制四步曲之一:加载位图】-----  
  2. g_hBitmap = (HBITMAP)LoadImage(NULL,L"Naruto.bmp",IMAGE_BITMAP,WINDOW_WIDTH,WINDOW_HEIGHT,LR_LOADFROMFILE);   //加载位图  
  3. //-----【位图绘制四步曲之二:建立兼容DC】-----  
  4. g_mdc = CreateCompatibleDC(g_hdc);    //建立兼容设备环境的内存DC  
  5. //-----【位图绘制四步曲之三:选用位图对象 】-----  
  6. SelectObject(g_mdc,g_hBitmap);    //将位图对象选入到g_mdc内存DC中  
  7. //-----【位图绘制四步曲之四:进行贴图】-----  
  8. BitBlt(g_hdc,0,0,WINDOW_WIDTH,WINDOW_HEIGHT,g_mdc,0,0,SRCCOPY);    //采用BitBlt函数贴图,参数设置为窗口大小    

1.8.2 示例程序GDldemo3

我们把一张名为Naruto.bmp的火影忍者图片文件放到工程目录下面,然后用这节教大家的知识来载入这张位图并显示在窗口中。
下面来看下这个程序中的核心代码:
程序代码片段一, 宏定义与全局变量声明:
  1. //-----------------------------------【宏定义部分】--------------------------------------------  
  2. //  描述:定义一些辅助宏  
  3. //------------------------------------------------------------------------------------------------  
  4. #define WINDOW_WIDTH    932                         //为窗口宽度定义的宏,以方便在此处修改窗口宽度  
  5. #define WINDOW_HEIGHT   700                         //为窗口高度定义的宏,以方便在此处修改窗口高度  
  6. #define WINDOW_TITLE    L"【致我们永不熄灭的游戏开发梦想】GDI位图绘制示例程序"  //为窗口标题定义的宏  
  7.   
  8. //-----------------------------------【全局变量声明部分】-------------------------------------  
  9. //  描述:全局变量的声明  
  10. //------------------------------------------------------------------------------------------------  
  11. HDC             g_hdc=NULL,g_mdc=NULL;       //全局设备环境句柄  
  12. HBITMAP     g_hBitmap=NULL;  //定义一个位图句柄  

程序代码片段二, Game_Init() 函数:
  1. //-----------------------------------【Game_Init( )函数】--------------------------------------  
  2. //  描述:初始化函数,进行一些简单的初始化  
  3. //------------------------------------------------------------------------------------------------  
  4. BOOL Game_Init( HWND hwnd )  
  5. {  
  6.     g_hdc = GetDC(hwnd);  //获取设备环境句柄  
  7.   
  8.     //-----【位图绘制四步曲之一:加载位图】-----  
  9.     g_hBitmap = (HBITMAP)LoadImage(NULL,L"Naruto.bmp",IMAGE_BITMAP,WINDOW_WIDTH,WINDOW_HEIGHT,LR_LOADFROMFILE);   //加载位图  
  10.   
  11.     //-----【位图绘制四步曲之二:建立兼容DC】-----  
  12.     g_mdc = CreateCompatibleDC(g_hdc);    //建立兼容设备环境的内存DC  
  13.   
  14.     Game_Paint(hwnd);  
  15.     ReleaseDC(hwnd,g_hdc);  //释放设备环境  
  16.     return TRUE;  
  17. }  

程序代码片段三, Game_Paint() 函数:
  1. //-----------------------------------【Game_Paint( )函数】--------------------------------------  
  2. //  描述:绘制函数,在此函数中进行绘制操作  
  3. //--------------------------------------------------------------------------------------------------  
  4. VOID Game_Paint( HWND hwnd )  
  5. {  
  6.     //-----【位图绘制四步曲之三:选用位图对象 】-----  
  7.     SelectObject(g_mdc,g_hBitmap);    //将位图对象选入到g_mdc内存DC中  
  8.     //-----【位图绘制四步曲之四:进行贴图】-----  
  9.     BitBlt(g_hdc,0,0,WINDOW_WIDTH,WINDOW_HEIGHT,g_mdc,0,0,SRCCOPY);    //采用BitBlt函数贴图,参数设置为窗口大小    
  10. }  

程序代码片段四, Game CleanUp()函数:
  1. //-----------------------------------【Game_CleanUp( )函数】--------------------------------  
  2. //  描述:资源清理函数,在此函数中进行程序退出前资源的清理工作  
  3. //---------------------------------------------------------------------------------------------------  
  4. BOOL Game_CleanUp( HWND hwnd )  
  5. {  
  6.     //释放资源对象  
  7.     DeleteObject(g_hBitmap);  
  8.     DeleteDC(g_mdc);  
  9.     return TRUE;  
  10. }  
效果图如下:



最后总结一下, 用GDI 显示来显示一幅位图的操作是很直观的, 先用Loadlmage 函数载入位图, 然后用CreateCompatibleDC 创建一个兼容DC , 再用SelectObject 将位图句柄选入某设备环境之中, 然后用贴图函数BitBlt 进行贴图操作,最后释放位图资源和设备环境。即下面的这四步曲:

  • 加载位图,从文件中加载位图对象。
  • 建立兼容DC , 建立一个与窗口设备环境DC 兼容的内存设备环境DC 。
  • 选用位图对象,内存DC 使用步骤1中所建立的位图对象。
  • 进行贴图,将内存DC 的内容贴到窗口DC 中。
另外需要注意的是,资源的释放是要有讲究的,我们记住一个原则就可以了,就是对称原则——先创建的后释放,后创建的先释放。

1.9 消除闪烁:缓冲显示技术

我们在编写游戏或者遇到其他任何需要在每秒钟多次刷新窗口的程序时,经常都会遇到画面闪烁的问题。这跟屏幕的刷新过程和工作方式有着密切的关系。因为如果我们不进行任何处理措施,直接把绘制的中间过程显示在屏幕上,难免会出现“ 穿帮镜头” 。就像我们在进行一台晚会的演出,要想奉献出一台精彩绝伦的晚会, 除了节目本身要精彩以外, 还需要做到有条不絮, 分好台前和台
后。台后的演员准备好之后, 才会上台前去表演,把自己最完美的一面展现给观众。
游戏的画面处理艺术也是这个道理, 采取了与晚会演出相似的处理方式一一分台前和台后。
而在游戏画面显示中,我们把这种技术叫双缓冲( Double buffer ) 。

类似于我们上面讲到的进行一台晚会的演出,双缓冲技术就是我们在其他地方(简单的说就是不针对屏幕,不显示出来的地方)开辟一个存储空间,把所有的动画都要渲染到这个地方,而不是直接渲染到屏幕上(针对屏幕的存储区域) 。等到准备完成之后,再将画面整体拷贝显示到屏幕之上, 正所谓不打无准备之仗。
在GDI 中,直接针对屏幕的就是窗口DC——喜闻乐见的hdc , 而上文中我们所说的“不显示出来的的地方” , 一般为与屏幕窗口DC 兼容的内存DC 。前面我们在位图绘制四步曲的第二步中,创建的兼容DC , 就是例子。
双缓冲( Double buffering )技术我们有时候也称作页面切换技术( Page Flipping ),而常常为了使画面显示更加平滑,在双缓冲不能满足我们要求的时候,也经常用到第三个缓冲区,这就称为三缓冲( Triple Buffering )技术。这种缓冲区的数量可以根据我们的需要任意增加缩减,因为只是简单调用一下API 函数CreateCompatibleDC 就可以创建一个指定DC 兼容的内存DC ,所以用起来
非常方便。
在后面将要讲解的Direct3D ,就是运用了通过创建多层类似于内存DC 的缓冲链, 来实现三维画面的平滑显示, 在Direct3D 中把这种技术叫做交换链。

1.10 章节小憩

本章我们和大家一起详细探讨了游戏编程所需的GDI编程知识,讲到了GDI 的概念、设备环境、Windows 屏幕区域相关概念、GDI 基本几何绘图、随机系统初步、文字输出,以及重中之重的位图绘制。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引