工具软件   办公软件   操作系统   网络安全   设计在线   程序开发   教程宝典   软件下载   软件论坛
您的位置:软件 > 开发者网络 > 技术跟踪 > 技术理论 > 正文
游戏开发新手入门之跟踪窗口和使用GDI
[文章信息]
作者:ant3000
时间:2005-03-03
出处:编程论坛
责任编辑:方舟
[文章导读]
这次我们将学习WINDOWS GDI和其它一些相关的东西,象响应用户输入和处理Windows产生的一些消息
advertisement
专题教程宝典
【软件应用】
【办公软件】
【图形图像】
【网页制作】
【操作系统】
【网络安全】
【程序开发】
【日报周刊】
【多媒体教程】
· 天极软件应用多媒体教程
· 图解PS按比例自动批量裁剪照片
· Visual FoxPro9.0构建管理系统图解
· 体验面对面的网络视频通讯
· 对付网页恶意代码
· Word动画教程:表格操作全接触
· JBuilder 2005开发Applet游戏全接触
· WinRAR操作技巧与另类应用
· 用VMware 5实现多系统同时运行
· 轻松玩转Excel数据图表
[正文]

上一页  1 2 3 4  

  ☆ 用GDI显示位图

  记得我告诉过你位图是很容易操纵的,因为Windows本身就是位图。现在让我们看看到底有多容易吧!用GDI显示位图需要四个基本的步骤:

  1、得到你要操作的窗口的DC句柄。

  2、获得位图的句柄。

  3、为位图创建设备上下文。

  4、传送位图。

  你已经知道第一步怎么做了,以前我也间接提到过第二步的做法,但没有具体说。我说过通过函数LoadBitmap()可以得到位图资源的句柄,但它有些过时了,有一个更灵活的函数LoadImage()取代了它,现在让我们看看怎么使用这个新函数。原形如下:

HANDLE LoadImage(
HINSTANCE hinst, // handle of the instance containing the image
LPCTSTR lpszName, // name or identifier of image
UINT uType, // type of image
int cxDesired, // desired width
int cyDesired, // desired height
UINT fuLoad // load flags
);

  如果函数调用失败,返回NULL。成功,你得到位图的句柄,意味着你就可以从资源或外部文件调用位图了。注意,这个函数还可以取得光标、图标的句柄,所以返回类型只是简单的HANDLE。在Visual C++6.0中,你需要用HBITMAP类型定义位图的句柄变量,否则编译器会生气的。例如:

HBITMAP hBitmap;
hBitmap =LoadImage(……);

  下面是LoadImage()函数的参数说明:

  ※ HINSTANCE hinst:如果你从资源调用位图,这应该是你的应用程序的实例。如果你要从外部文件调用位图,就把它设置为NULL。

  ※ LPCTSTR lpszName:这个要么是资源标识符,记住用MAKEINTRESOURCE()宏转变数字常量;要么就是你要调用的图象的完整文件名称。

  ※ UINT uType:根据你的调用对象来决定。应该是IMAGE_BITMAP、IMAGE_CURSOR和IMAGE_ICON中的一种。

  ※ int cxDesired,cyDesired:这是你希望的图象的尺寸。如果你都设置为0,将是图象的真实尺寸。

  ※ UINT fuLoad:这是可以组合用的标志,当然是用“|”来连接。以下是一些常用的标志:

  ◎ LR_CREATEDIBSECTION:如果uType是IMAGE_BITMAP,将导致函数返回一个DIB(DIB,设备无关的位图)。基本的意思就是返回一个不依赖于显示设备的位图。

  ◎ LR_DEFAULTSIZE:对于光标和图标,如果cxDesired和cyDesired都设置为0,这个标志将启用Windows的默认尺寸,而不是图形的实际尺寸。

  ◎ LR_LOADFROMFILE:如果你要从外部文件中调入图象,你就必须用这个标志。

  条件允许的话,你应当尽量使用LR_CREATEDIBSECTION和LR_LOADFROMFILE这两个标志。现在,你已经得到了图象(过去我们总说位图,好像不太准确,毕竟有时我们不从资源里调用)的句柄,下一步你必须建立设备上下文把图象放进去。位图的应该独有的特点是:它只能被选入到内存设备上下文中。内存设备上下文被定义为一个具有显示表面的设备上下文。它只存在于内存中,并且与特定的设备上下文相关。使用内存设备上下文,你必须首先创建它。CreateCompatibleDC()函数正是用于这个目的。它的一般形式如下:

HDC CreateCompatibleDC(HDC hdc);

  函数唯一的参数是与内存设备上下文相兼容的设备上下文句柄。如果内存设备上下文与视频屏幕兼容,则这个参数可以为NULL。我们就用NULL。如果函数失败,返回NULL。现在,我们把位图(或图象)放入内存失败上下文,我们用这个函数:

HGDIOBJ SelectObject(
HDC hdc, // handle to device context
HGDIOBJ hgdiobj // handle to object
);

  函数的返回类型HGDIOBJ是一个比HBITMAP更通用的类型。不用担心,HGDIOBJ和HBITMAP的一致性没有任何问题。以下是函数的参数说明:

  ※ HDC hdc:是设备上下文的句柄。要调用图象,必须是内存设备上下文的句柄。

  ※ HGDIOBJ hgdiobj:要调用对象的的句柄。可调用的有位图、画刷、字体、画笔等。这里是位图(图象)的句柄。

  返回值是要调入设备上下文中的对象的句柄。这里是位图的句柄。如果失败,返回NULL。

  现在你已经把位图装入设备上下文,你还需要进行最后的一步:把内存DC里的内容拷贝到显示设备上下文中。然而,我们首先要得到位图的一些信息,如尺寸,这是显示图象时必须的。所以,我们还需要另一个函数GetObject(),它可以用于获得对象的信息,当然,这里我们是要获得位图的信息。函数的一般形式如下:

int GetObject(
HGDIOBJ hgdiobj, // handle to graphics object of interest
int cbBuffer, // size of buffer for object information
LPVOID lpvObject // pointer to buffer for object information
);

  返回值是一个字节数。如果失败,返回0。当GetObject()调用的目标是位图时,返回的信息是与位图的宽度、高度和颜色格式有关的结构成员。参数说明如下:

  ※ HGDIOBJ hgdiobj:要得到信息的对象的句柄。这里我们传送位图的句柄。

  ※ int cbBuffer:存放调用返回的信息的缓冲区的大小。对于位图,我们将得到BITMAP类型结构,所以这里设置成sizeof(BITMAP)。

  ※ LPVOID lpvObject:指向存放由调用返回的信息的缓冲区的指针。

  你需要定义一个BITMAP结构类型的变量,调用GetObject()函数放入缓冲区的信息。由于BITMAP结构对我们来说是一个新的结构,所以就介绍一下:

typedef struct tagBITMAP { // bm
LONG bmType;
LONG bmWidth;
LONG bmHeight;
LONG bmWidthBytes;
WORD bmPlanes;
WORD bmBitsPixel;
LPVOID bmBits;
} BITMAP;

  很多成员,但我们实际上只对其中的两个有兴趣。但我们还是都介绍一下:

  ※ LONG bmType:指定位图类型,必须为0。有用吧!

  ※ LONG bmWidth,bmHeight:分别是位图的宽度和高度,以象素为单位。必须都大于0。

  ※ LONG bmWidthBytes:指定每一行扫描线中的字节数。因为Windows假定位图是字对齐的,所以这个值必须能够被2整除。

  ※ LONG bmPlanes:指定颜色面的数目。

  ※ LONG bmBitsPixel:指定表述象素颜色所需的位数。(好像没什么用)

  ※ LPVOID bmBits:如果你想存取实际的位图数据,这个指针指向位图位值得位置。

  OK,差不多了。一旦位图被选入内存设备上下文,且代码已经得到了位图宽度和高度的必要信息后,我们就可以将内存中存储的位图通过位块传输到达屏幕,然后在任意位置对它进行显示。我说过处理位图是很容易的,对吧?(如果你觉得不容易,只能怪我说的不够好)有两个函数需要说明,先说第一个:

BOOL BitBlt(
HDC hdcDest, // handle to destination device context
int nXDest, // x-coordinate of destination rectangle's upper-left corner
int nYDest, // y-coordinate of destination rectangle's upper-left corner
int nWidth, // width of destination rectangle
int nHeight, // height of destination rectangle
HDC hdcSrc, // handle to source device context
int nXSrc, // x-coordinate of source rectangle's upper-left corner
int nYSrc, // y-coordinate of source rectangle's upper-left corner
DWORD dwRop // raster operation code
);

  BitBlt()函数是执行位图显示操作最简单且最直接的方法。根据函数调用的成功或失败,返回值是TRUE或FALSE。布尔函数都是这样。有很多参数,但很好理解:

  ※ HDC hdcDest:目标设备上下文句柄。根据我们的情况,应该是显示设备上下文句柄。

  ※ int nXDest,nYDest:目标矩形左上角的x、y坐标,也就是被显示的位图左上角的屏幕位置。

  ※ int nWidth,nHeight:位图的宽度和高度。

  ※ HDC hdcSrc:原来的设备上下文句柄。根据我们的情况,应该是内存设备上下文句柄。

  ※ int nXSrc,nYSrc:源位图的x、y坐标。由于进行位块传输的矩形必须与在参数nWidth和参数nHeight中定义的尺寸相同,所以通常都设为0。但不一定总是这样,例如你只想显示位图的一部分,就不能都设置为0。

  ※ DWORD dwRop:有很多光栅代码你可以选择,但只有一个我们感兴趣,SRCCOPY。它直接把源DC内的内容拷贝到目标DC中。
以上就是第一个函数。下面说说第二个函数StretchBlt(),简单的说,位图实际的拉伸或压缩就是通过它来实现的。函数的一般形式如下:

BOOL StretchBlt(
HDC hdcDest, // handle to destination device context
int nXOriginDest, // x-coordinate of upper-left corner of dest. rectangle
int nYOriginDest, // y-coordinate of upper-left corner of dest. rectangle
int nWidthDest, // width of destination rectangle
int nHeightDest, // height of destination rectangle
HDC hdcSrc, // handle to source device context
int nXOriginSrc, // x-coordinate of upper-left corner of source rectangle
int nYOriginSrc, // y-coordinate of upper-left corner of source rectangle
int nWidthSrc, // width of source rectangle
int nHeightSrc, // height of source rectangle
DWORD dwRop // raster operation code
);

  它比BitBlt()复杂一些,所以它比BitBlt()慢。它们的参数差不多,并且有了注释,这里就不再重复了。光栅代码也是选择SRCCOPY。现在,只剩下最后一件事情——清除。建立一个设备上下文【CreateCompatibleDC(HDC hdc)】不同于得到设备上下文【(GetDC)】,不能用ReleaseDC(),要用:

BOOL DeleteDC(HDC hdc);

  参数是建立的DC的句柄。返回值是一个布尔类型,你知道布尔类型是怎么回事,对吧?All right,感觉还不坏吧。下面我们把以上步骤合并,隆重推出一个超级大包子(我觉得它有点象包子)。我事先假设你已经定义了一个全局的应用程序实例的句柄hinstance。

int ShowBitmapResource(HDC hDestDC, int xDest, int yDest, int nResID)
{
HDC hSrcDC; // source DC - memory device context
HBITMAP hbitmap; // handle to the bitmap resource
BITMAP bmp; // structure for bitmap info
int nHeight, nWidth; // bitmap dimensions

// first load the bitmap resource
if ((hbitmap = (HBITMAP)LoadImage(hinstance, MAKEINTRESOURCE(nResID), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION)) == NULL)
return(FALSE);

// create a DC for the bitmap to use
if ((hSrcDC = CreateCompatibleDC(NULL)) == NULL)
return(FALSE);

// select the bitmap into the DC
if (SelectObject(hSrcDC, hbitmap) == NULL)
return(FALSE);

// get image dimensions
if (GetObject(hbitmap, sizeof(BITMAP), &bmp) == 0)
return(FALSE);

nWidth = bmp.bmWidth;
nHeight = bmp.bmHeight;

// copy image from one DC to the other
if (BitBlt(hDestDC, xDest, yDest, nWidth, nHeight, hSrcDC, 0, 0, SRCCOPY) == NULL)
return(FALSE);

// kill the memory DC
DeleteDC(hSrcDC);

// return success!
return(TRUE);
}

  ☆ 最后一件事

  你可能还不太明白,但你现在确实有足够的知识用Windows GDI在窗口里建立一个小游戏了!你能创建窗口,显示图形。游戏的逻辑同DOS下的C语言游戏逻辑一样,即使你不知道DOS下的C,你玩过游戏吧,玩过就应该有个初步的认识。你甚至能用鼠标处理产生的消息。但有一件事儿我们漏了,可能它不属于本章的范围,但我们不能不提它——键盘支持。Windows提供了一个非常好的函数GetAsyncKeyState()检测键盘的状态。它返回一个16位的值,高字节显示了一个键子是否被按下。它的原形如下: SHORT GetAsyncKeyState(int vKey);

  参数是键子的标识符。都以VK_开头,例如一些最常用的:VK_RETURN,VK_ESCAPE,VK_UP,VK_LEFT,VK_RIGHT和VK_DOWN。你甚至可以用VK_LBUTTON和VK_RBUTTON标识鼠标按键。多方便呀。观察高字节,如果是1,键子就是正被按下。我总是用一个宏来做这些:

#define KEYSTATE(vknum) ((GetAsyncKeyState(vknum) & 0x8000) ? TRUE : FALSE)

  如果你不仔细,可能还没有注意到这个条件符号(?),在C语言中是一个三元算子——评估它左边的表达式。如果表达式的值为true,表达式就是冒号左边的值,如果false,就是冒号右边的值。明白了吗?好极了!

  ☆ 总结

  现在,你可以做GDI基础的游戏了。本章比我原打算的要长一些。由于讲的内容较多,我做了一个例程给你借鉴。它是类似于屏幕保护的东东,只生成一个.EXE文件格式,不是全屏的,在一个小窗口里。它展示了出口的创建和相应了几个键盘消息…………。

上一页  1 2 3 4  

天极社区邀请您:写博客日记  上传相片   论坛聊天  订阅电子杂志  推荐网摘   免费图铃工具
笔名:   请您注意:

 遵守国家有关法律、法规,尊重网上道德,承担一切因您的行为而直接或间接引起的法律责任。

 天极网拥有管理笔名和留言的一切权利。
评论:
 
发表评论推荐给朋友我想参加相关培训打印我对此感兴趣订阅电子杂志
相关内容焦点新闻
  • 微软停止视窗XP网上激活 打击盗版重大改革
  • 周鸿祎否认辞职传言 不会介入盛大收购新浪
  • 日本丸红曲线进入京东方 1.6亿元购10%股权
  • 苏宁总裁中招被捕流言 股价昨下跌4.55%
  • 英特尔64位被指有价无市 国内厂商消极应对
  • AMD总裁称坚决支持WAPI 不会进军消费电子
  • 手机销售额首次超过彩电 换机高峰提前到来
  • 预测:影响未来IT安全性的五种攻击手段
  • Advertisement