编程初学者:游戏编程起源(初学者)Ⅴ



★第 3章 跟踪你窗口和使用GDI

☆ 介绍
如果你看过了头两章你或许已经在问我什么时候能给你讲点有成就感东东呢?OK时候到了这次我们将学习WINDOWS GDI(图形设备接口)和其它些相关东西象响应用户输入和处理Windows产生些消息至于显示图形我们将接触 3个课题:文本显示绘制象素显示位图我们先来研究下几个Windows消息细节
重复话:你需要C语言基础知识最好看过上两章由于本章将使你能做个具体图形DEMO个源代码例程附在本章后面是用Visual C++写和编译所以吗最好同我保持致哦!好了开动吧!

☆ 设备上下文
在第章里我们创建和注册了个窗口类其中有行定义了窗口风格(功能)是这个样子:

sampleClass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW; // standard tings

其中 3个属性是很但这个——CS_OWNDC需要解释如果你记得我曾经告诉过你这个属性允许窗口有自己独特设备上下文但直到现在我们还没有具体OK时间到了开讲!
设备上下文是个结构个表现组图形对象和属性结构还有些输出设备设置和属性使用设备上下文允许你直接操纵图形不用考虑低级细节Windows GDI是个图形翻译系统是介于应用和图形硬件的间GDI可以输出到任意兼容设备不过最常使用设备是视频监视器、图形硬拷贝设备(如打印机或绘图仪)或者是内存中图元文本GDI能够绘制直线、曲线、封闭图形和文本所有访问GDIWindows都需要个设备上下文句柄作为参数感谢上帝这是非常容易做到你若想得到个窗口设备上下文句柄你可以用这个:

HDC GetDC(
HWND hWnd // handle to a window
);

很简单是不是?所有你做把要操作窗口句柄传递给它然后返回个设备上下文句柄如果你传递是NULL将返回整个屏幕设备上下文(DC以后都用DC表示)句柄如果失败将返回NULL
设备上下文不仅仅处理图形但我们习惯于泛泛认为它是处理图形处理显示图形DC类型称作显示DC处理打印称作打印DC;处理位图数据称作内存DC还有其它些设备DC感觉有点复杂吧不要紧这是Windows主要功能就是迷惑群众^_^旦我们接触些代码就不会觉得难了
当你结束使用DC时定要释放它也就是释放它占用内存空间要把这种思想贯穿到以后编程中去占用了内存不用时要释放切记!释放DC是个很简单:

ReleaseDC(
HWND hWnd, // handle to window
HDC hDC // handle to device context
);

若成功释放返回值是1否则是0参数有注释我还是说下:
HWND hWnd:你所要控制那个窗口句柄如果你开始传递是NULL现在还要传递NULL
HDC hDC:DC句柄
在用DC和GDI进行图形显示前我们先看看创建窗口例子时要遇到几条重要消息我将要提到 4条消息是:WM_MOVE、WM_SIZE、WM_ACTIVATE、WM_PAINT

☆ 追踪窗口状态
头两个是很简单当窗口被用户移动时将发送WM_MOVE消息窗口新位置坐标储存在lparam中(还记得吗消息在lparam和wparam中被进步描述它们是消息控制参数)lparam低端字中存储窗口客户区左上角坐标x高端字中存储坐标y
当窗口大小被改变时将发送WM_SIZE消息同WM_MOVE消息差不多lparam低端字中存储客户区宽度高端字存储高度同WM_MOVE区别wparam参数也控制了些重要东西它可以是下列中任意个值:
※ SIZE_MAXHIDE:其它窗口被最大化了
※ SIZE_MAXIMIZED:本窗口被最大化了
※ SIZE_MAXSHOW:其它窗口被还原了
※ SIZE_MINIMIZED:本窗口被最小化了
※ SIZE_RESTORED:窗口被改变了尺寸但既没最大化也没有最小化
当我编写窗口例子时我通常喜欢把窗口当前位置和大小保留在几个全局变量里假设我们命名这些全局变量为xPosyPosxSize和ySize你最好这样控制WM_SIZE和WM_MOVE这两个消息:

(msg WM_SIZE)
{
xSize = LOWORD(lparam);
ySize = HIWORD(lparam);
}
(msg WM_MOVE)
{
xPos = LOWORD(lparam);
yPos = HIWORD(lparam);
}

现在轮到WM_ACTIVATE消息了它告诉你个新窗口被激活这是很有用如果出现优先申请你就不可能处理所有逻辑有时例如写个全屏DIRECTX忽略WM_ACTIVATE消息将导致你出现致命可能它做了些你不希望它做事情在任何情况下守候WM_ACTIVATE消息从而采取行动个好主意
窗口被激活和被解除激活都会发出WM_ACTIVATE消息我们可以通过检测wparam低端字来得知是被激活还是被取消这将有 3种可能值:
※ WA_CLICKACTIVE:窗口被鼠标激活
※ WA_ACTIVE:窗口被其它东西激活(键盘、、等等)
※ WA_INACTIVE:窗口被解除激活
为了处理这个消息我保留了另个全局变量bFocus当接收到WM_ACTIVATE消息值将改变举例如下:

(msg WM_ACTIVATE)
{
(LOWORD(wparam) WA_INACTIVE)
focus = FALSE;

focus = TRUE; // tell Windows we handled it
(0);
}

有两个相关联消息WM_KILLFOCUS和WM_SETFOCUS在窗口接收到输入焦点时候Windows消息WM_SETFOCUS被发送给它在失去焦点时候则发送WM_KILLFOCUS消息应用可以截取这些消息以得知输入焦点任何改变情况什么是输入焦点呢?存有输入焦点应用(窗口)就是被激活那个窗口你就认为被激活窗口就是输入焦点就行了可能出现没有窗口具有输入焦点所以我建议用WM_ACTIVATE消息跟踪你窗口状态(有些胡涂?不要紧你就记住用WM_ACTIVATE就行了)往下进行



☆ WM_PAINT 消息
WN_PAINT消息通知全部或部分客户窗口需要重新绘制当用户在最小化、重叠或调整客户窗口区域时候就会产生这条消息重新绘制你需要做两件事首先是要用到WM_PAINT消息专用个是BeginPa这是它原形:

HDC BeginPa(
HWND hwnd, // handle to window
LPPAINTSTRUCT lpPa // poer to structure for pa information
);

在我告诉你返回值是什么的前让我们先看看参数:
HWND hwnd:需要重绘窗口句柄你应该已经对于这种参数比较熟悉了
LPPAINTSTRUCT lpPa:这是很重要是指向PAINTSTRUCT结构指针该结构包含所有要被重绘区域信息
继续的前我应该给你看看PAINTSTRUCT结构:

typedef struct tagPAINTSTRUCT { // ps
HDC hdc;
BOOL fErase;
RECT rcPa;
BOOL fRestore;
BOOL fIncUpdate;
BYTE rgbReserved[32];
} PAINTSTRUCT;

结构内成员如下:
HDC hdc:哈哈有理由复习下设备上下文(DC书中叫“设备描述表”)了这是要被刷新区域句柄
BOOL fErase:指明应用是否应该抹去背景如果是FALSE介绍说明系统已经删除了背景还记得在Windows类中我们曾经用黑色画刷定义了个背景吗?这就意味着系统将用这个画刷抹去无效区域
RECT rcPa:这是最重要个成员它将告诉你需要被重绘无效区域矩形我将稍后告诉你RECT结构
BOOL fRestoreBOOL fIncUpdateBYTE rgbReserved[32]:好消息这些是保留成员为老Windows服务所有你我都不必管它们:)
现在我已经给你看了这么多这就是BeginPa全部它做了 3件事儿首先它使窗口再次有效直到下次被改变WM_PAINT消息发出前这个窗口都是有效第 2如果在窗口类(Windows )里定义了背景画刷就像我们做过那样就用这个画刷重绘无效区域(所谓无效就是被改变)第 3返回了被重绘区域DC句柄重绘区域是由RECT结构定义:

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

我们已经指出这个结构描绘了个矩形但是有件事情需要说说RECT包含左上角但不包含右下角什么意思呢?让我们先定义个RECT对象:

RECT myRect = {0, 0, 5, 5};

这个RECT包含象素(00)但是没有达到(55)所以矩形右下角实际是(44)看起来没有什么意义但是你得习惯它
现在还记得我所说有关使用DC事儿吗?旦你用完了你就必须释放它OK你记起来了用EndPa释放回应WM_PAINT消息每次完BeginPa必须匹配个EndPa释放DC这是原形:

BOOL EndPa(
HWND hWnd, // handle to window
CONST PAINTSTRUCT *lpPa // poer to structure for pa data
);

通过返回TRUE或FALSE分别表明成功还是失败有两个简单参数:
HWND hWnd:就是窗口句柄
CONST PAINSTRUCT *lpPa:指向PAINTSTRUCT类型结构变量地址同BeginPa第 2个参数是回事不要被CONST迷惑了它只是保证和确认没有改变结构内容
你还可以通过ValidateRect()代替BeginPa使得窗口再次有效但你得手工操作可能我们真什么时候就要用到它所以给你它原形:

BOOL ValidateRect(
HWND hWnd, // handle of window
CONST RECT *lpRect // address of validation rectangle coordinates
);

通过返回TRUE或FALSE来确定成功还是失败参数很简单:
HWND hWnd:烦不烦我不说了:)
CONST RECT *lpRect:是指向RECT结构是否有效指针如果你传递NULL则整个客户区域都是有效
现在把以上讲到做个样子给你看吧假设我们已经定义了个全局变量hMainWindow作为我们窗口句柄

(msg WM_PAINT)
{
PAINTSTRUCT ps; // declare a PAINTSTRUCT for use with this message
HDC hdc; // display device context for graphics calls

hdc = BeginPa(hMainWindow, &ps); // validate the window

// your paing goes here!

EndPa(hMainWindow, &ps); // release the DC

// tell Windows we took care of it
(0);
}

这段代码很简单也没做什么令人兴奋事儿!OK如果你不想用窗口类里默认画刷重绘窗口你必须自己做些事情包括使用我们还没有讲图形部分永远不要害怕我们过会儿就讲现在我们在讨论消息还有些事情我必须解释

☆ 关闭你应用(关闭窗口)
待续



Tags:  网络游戏的起源 网络游戏起源 游戏的起源 编程初学者

延伸阅读

最新评论

发表评论