CSDN博客

img t1p2

DirectX 9.0高级 游戏编程(第一章)Windows(3)

发表于2004/9/21 17:08:00  1248人阅读

应该熟练掌握的窗口几何

    由于目前Windows应用程序的用途是严格受限的,即你要实现什么样的功能就只能使用Windows提供的什么样的函数或者结构。因此这里,你只需要关心在窗体几何中要用到的两个基本的Windows结构:POINT和RECT。

    在Windows中,有两个坐标空间。一个是客户区域(client area)坐标空间,它的原点(0,0)在窗口(称为client space,客户空间?)的左上角。当窗口在屏幕中移动的时候,客户区域的相对坐标不变。另一个坐标空间是桌面坐标空间,这是一个绝对空间,其坐标原点是在屏幕(也叫screen space)的左上角。

    Windows使用POINT结构来表现2D坐标系。它有两个长整型(long integer)结构变量,一个是代表水平轴(x轴),一个代表垂直轴(y轴)。

   typedef struct tagPOINT {
       LONG x;
       LONG y;
   } POINT;

    由于所有的窗口都是长方形(呵呵,称为矩形可能更书面化。)的,因此Windows就用一个RECT结构来表现一个长方形。你会注意到,这个结构的本质是首尾相连的两个点,第一个代表矩形的左上角,另一个代表矩形的右下角。

typedef struct _RECT {
       LONG left;
       LONG top;
       LONG right;
       LONG bottom;
   } RECT;

left 窗口的左边
top 窗口的顶部
right 窗口的右边(右边 – 左边就是宽度)
bottom            窗口的底部(底部 – 顶部 就是 高度)

    你可以使用函数GetClientRect来取得窗口的客户端矩形。因为左边和顶部总是0,所以右边和底部的数值就是窗口的宽度和高度。
[color=blue]
BOOL GetClientRect(
     HWND hWnd,
     LPRECT lpRect
   );
[color]
hWnd 你想要知道有关信息的窗口的句柄
lpRect 你想要用客户端矩形来填充的RECT结构的指针

    由于使用GetClientRect所得到的窗口的客户端矩形的信息是基于客户区域坐标空间的,因此你可能经常需要知道这些点相对于桌面坐标空间应该是什么坐标。ClientToScreen函数为你提供了这个转换功能,它的函数原型如下:

BOOL ClientToScreen(
     HWND hWnd,
     LPPOINT lpPoint
   );

hWnd 客户端点所定义的窗口的句柄
lpPoint 客户端点的指针,这个点(client space中的)将被转换成屏幕空间(screen space)中的点

    为了将你从GetClientRect函数产生的矩形转换为屏幕空间下的矩形,你可以只在bottom和right这两个矩形成员上使用ClientToScreen函数(亲爱的ogdev会员,请你想想为什么可以这么做)。虽然偷懒是一种很不雅的行为,但是确实可以使用,呵呵!

    有个事情要说明一下,那就是窗口的宽度和高度的变化可能会将窗体结构搞乱。比如,你想要一个800 x 600像素的客户端矩形,但是你调用的CreateWindow给出的却是整个窗口的尺寸,包括那些调整大小栏,标题栏和菜单栏,这就是说跟你原先所要求的不一样,客户端矩形有可能变小了。不过,幸运的是,你可以使用AdjustWindowRect函数将表示客户端矩形的矩形转换为你想要的窗口尺寸(800 x 600)。它要求每个坐标都根据窗口的风格dwStyle去重新调整它们的数值,而窗口的风格,则必须设置为同CreateWindow中使用的dwStyle一样才能正确工作。如果不是WS_POPUP风格,窗口的左边和顶部坐标经过调整后可能为负数。

BOOL AdjustWindowRect(
     LPRECT lpRect,
     DWORD dwStyle,
     BOOL bMenu
   );

lpRect 指向将被调整的RECT结构的指针

dwStyle 已经产生的窗口的风格,它表明每个坐标需要调整多少。举个例子,如果窗口的风格为WS_POPUP,就根本不用调整。(亲爱的ogdever们,你们知道为什么吗?对了,因为此时CreateWindow所创建的窗口就是你想要的客户端矩形。)

bMenu Boolean(布尔值),如果窗口有菜单,则是TRUE。像在这个案例中,因为没有菜单,所以你就只需要传递FALSE给这个参数就行了。

    Windows有一个高性能的图形库,用来在图形设备的句柄上执行操作。这个库叫做GDI,或者图形设备接口(Graphical Device Interface)。它允许用户绘制直线,椭圆,位图,文本(在后面的章节中,我会给你看它的文本绘制能力)和其它一些事物等。其中的一个例子程序就是使用它在屏幕上绘制”Hello,World”文本。在本书的后面,你将会看到GDI更多的功能。


重要的窗口消息

    本书中大多数的代码都是用Windows作为一个jumping-off point(临界点?)— 一种在屏幕上建立可供你绘制的窗口的方式。
    在这里,我只给你看Windows中大量的窗口消息列表中的一小部分子集。表1.4列出了一些重要的消息以及他们的参数。

表1.4 一些重要的窗口消息

WM_CREATE
    在Windows完全创建了应用程序的窗口之后,但还没有开始绘制之前,此消息被发送给应用程序。这是应用程序第一次可以看到它的窗口的HWND到底是什么东东的时候。

WM_PAINT
    当Windows想要窗口进行自我绘制的时候,此消息被发送到应用程序。
    参数:(HDC) wParam  是可以在里面进行绘制的窗口的dc(device context)句柄

WM_CLOSE
    当窗口被要求关闭的时候,此消息被发送。你可以在关闭窗口之前,要求确认。

WM_SIZE
    窗口被调整大小的时候,此消息被发送。当窗口被调整大小的时候,左上角的位置不变(因此,当你从左上角开始调整大小的时候,
WM_MOVE和WM_SIZE消息都会被发送)
    参数:wParam:调整大小的标志。除了SIZE_MINIMIZED以外,还有其他标志。SIZE_MINIMIZED是在窗口被最小化的时候被发送的。
          LOWORD(lParam) 客户端区域新的宽度(不是整个窗口)
          HIWORD(lParam) 客户端区域新的高度(不是整个窗口)

WM_MOVE
    当窗口被移动的时候,此消息被发送。
    参数:(int)(short) LOWORD (lParam) 客户端区域左上角新的x坐标
          (int)(short) HIWORD (lParam)     客户端区域左上角新的y坐标

WM_QUIT
    这个是应用程序可以得到的最后一个消息,得到这个消息后,应用程序就结束了。你不用处理这个消息,实际上它也不会经过WndProc。不过,它将在WinMain的message pump中被处理,程序将跳出循环,随后结束。

WM_KEYDOWN
    每当键被按下的时候,此消息就被接收。在一个指定的时间内自动重复也会收到这个消息
    参数:(int) wParam 这是该按键的虚拟键码。在处理它之前,如果你在消息中调用TranslateMessage,同时,如果它是一个键的ASCII码(字母,数字,标点符号),那么它就会被转换成该键实际的ASCII字符。

WM_KEYUP
    当按键被释放的时候,此消息被接收。
    参数:(int)wParam 该被释放的键的虚拟键码。

WM_MOUSEMOVE
    MouseMove是一个经常需要被接收的消息。鼠标每次在窗口的客户端区域移动的时候,应用程序就得到鼠标光标相对于客户端区域原点的新的位置。
    参数:LOWORD(lParam) 鼠标的x坐标,相对于客户端区域的左上角
          HIWORD(lParam) 鼠标的y坐标,相对于客户端区域的左上角
          wParam 键的标志。这个可以让你知道键盘的状态是什么,它使用了什么特殊键(举个例子:比如Alt-left组合)
    键的标志如下:
    · MK_CONTROL: 说明CTRL键被按下了
    · MK_LBUTTON: 说明鼠标左键被按下了
    · MK_MBUTTON: 说明鼠标中键被按下了
    · MK_RBUTTON: 说明鼠标右键被按下了
    · MK_SHIFT: 说明Shift键被按下了

WM_LBUTTONDOWN
    当用户在客户端区域按下鼠标左键的时候,此消息被接收。只有在键被按下的时候,你才收到一个消息,如果他们一直处于被按下的状态,也不会不断地发送消息。
    参数:LOWORD(lParam) 鼠标的x坐标,相对于客户端区域的左上角
          HIWORD(lParam) 鼠标的y坐标,相对于客户端区域的左上角
          wParam 键的标志。这个可以让你知道键盘的状态是什么,它使用了什么特殊键(举个例子:比如Alt-left组合)
    键的标志如下:
    · MK_CONTROL: 说明CTRL键被按下了
    · MK_MBUTTON: 说明鼠标中键被按下了
    · MK_RBUTTON: 说明鼠标右键被按下了
    · MK_SHIFT: 说明Shift键被按下了
WM_MBUTTONDOWN
    当用户在客户区域按下树标中键的时候,你就会接收到此消息。只有当该键被按下的时候,你才会收到消息。如果该键一直处于按下的状态,是不会不断地收到消息的。
    参数:LOWORD(lParam) 鼠标的x坐标,相对于客户端区域的左上角
          HIWORD(lParam) 鼠标的y坐标,相对于客户端区域的左上角
          wParam 键的标志。这个可以让你知道键盘的状态是什么,它使用了什么特殊键(举个例子:比如Alt-left组合)。
    键的标志如下:
    · MK_CONTROL: 说明CTRL键被按下了
    · MK_LBUTTON: 说明鼠标左键被按下了
    · MK_RBUTTON: 说明鼠标右键被按下了
    · MK_SHIFT: 说明Shift键被按下了

WM_RBUTTONDOWN
    当用户在客户区域按下鼠标右键的时候,你就会接收到此消息。只有当该键被按下的时候,你才会收到消息。如果该键一直处于按下的状态,是不会不断地收到消息的。
    参数:LOWORD(lParam) 鼠标的x坐标,相对于客户端区域的左上角
          HIWORD(lParam) 鼠标的y坐标,相对于客户端区域的左上角
          wParam 键的标志。这个可以让你知道键盘的状态是什么,它使用了什么特殊键(举个例子:比如Alt-left组合)。
    键的标志如下:
    · MK_CONTROL: 说明CTRL键被按下了
    · MK_LBUTTON: 说明鼠标左键被按下了
    · MK_MBUTTON: 说明鼠标中键被按下了
    · MK_SHIFT: 说明Shift键被按下了

WM_LBUTTONUP
    当用户在客户端区域释放鼠标左键的时候被接收。
    参数:此参数等同于WM_RBUTTONDOWN中的参数

WM_MBUTTONUP
    当用户在客户端区域释放鼠标中键的时候被接收。
    参数:此参数等同于WM_RBUTTONDOWN中的参数

WM_RBUTTONUP
    当用户在客户端区域释放鼠标右键的时候被接收。
    参数:此参数等同于WM_RBUTTONDOWN中的参数

WM_MOUSEWHEEL
    很多新的鼠标的滚轮现在都有Z控制轴,它既可以向前或者向后转动,也可以按。如果它被按下了,一般都是发送鼠标中键的消息。但是,如果它是向前或者向后转动,那么以下参数就会被传递。
    参数:
          (short) HIWORD(wParam)  从最后一个消息以来,滚轮转动的圈数。正数表示滚轮向前转动(远离用户),负数表示滚轮向后转动(朝用户的方向)
          (short) LOWORD(lParam)  鼠标的x坐标,相对于客户端区域的左上角
          (short) HIWORD(lParam)  鼠标的y坐标,相对于客户端区域的左上角
          LOWORD(wParam)  键的标志。这个可以让你知道键盘的状态是什么,它使用了什么特殊键(举个例子:比如Alt-left组合)。
    键的标志如下:
    · MK_CONTROL: 说明CTRL键被按下了
    · MK_LBUTTON: 说明鼠标左键被按下了
    · MK_MBUTTON: 说明鼠标中键被按下了
    · MK_SHIFT: 说明Shift键被按下了
MFC

    你现在可能已经猜到了,Windows应用程序编程实际上不是一件很容易的事情。人们往往会对难题感到恐惧,当他们失败了多次以后,他们就会泄气。因此如果Windows工作所需的大量代码需要在每个应用程序中被重新填充的话,那么它就会被人认为是一段丑陋的,令人生厌的代码。这些东西应该被抽象出来,然后封装,让每个应用程序都可以直接调用,而不用再让程序员自己重新写。目前,市场上有很多的库可以做这些事情,其中最主要的就是微软开发的一套库,叫作MFC。

    MFC,或者叫微软基础类,是一些被设计用来封装Win32 API的系统类。它可以使你用一个简单的方式来创建一个最普通的Windows 程序任务。你的程序都是继承自CWinApp,窗口都是继承自CWnd,对话框都是继承自CDialog,等等。这些都使程序的编写变得更容易,因为大量复杂的Windows所必需的工作,MFC已经替你解决了。MFC是快速创建现有前端代码的最理想工具。

    然而,事情并不像它刚开始出现的时候那么顺利。首先,MFC是连接文档查看类型的应用程序(就像写字板)的。它的代码支持浮动工具条,可以处理一些非模态的对话框,能和GDI一起工作。但不幸的是,这些东西在制作3D游戏的时候使用的并不多。

    MFC另外一个问题是代码大小和运行速度。它所给出的新增函数是以很高的代价产生出来的:相当大的DLL,除非它们事先被载入内存,否则他们会使你的程序载入时间变长。

    最后,对DirectX来说,MFC并不是一个最好开发伙伴。它们的程序设计模型和已经设计好的一些API都是不相同的。比如说,Direct3D应用程序需要知道什么时候窗口被移动了或者大小被改变了,这样它可以重新画窗口。然而,在MFC中,对于窗口的这些变化的通知并不是一个即时事件,因此不能及时通知窗口重新绘制自己。如果DirectX窗口是一个可以相对于它的父窗口移动的文档窗口,这种不便就尤其突出。不过,这些阻碍并不是不能克服,他们只是众多痛苦中的一种罢了。不过,我要告诉你一个好消息,那就是在开发3D游戏的时候,你的绝大多数程序将运行在全屏模式下,这些并不需要MFC提供的GUI bells and whistles。

    在本书中,所有的代码都不会用到MFC,因此我们就不再探讨它的细节了。如果你认真的开发3D游戏,你就会需要一些工具来管理你的代码。这时候,你可以打开一本关于MFC的好书,充实一下自己,马上就可以使用了。在这里,我推荐大家看《Professional MFC with Visual C++》 ,这是一本关于MFC的最好的书之一,由Mike Blaszczak编著,Wrox 出版社出版。
0 0

相关博文

我的热门文章

img
取 消
img