• 《Windows游戏编程大师技巧》三、Windows高级编程



    Windows编程很绝的地方在于:你不用了解太多细节,就可以完成很多工作。

    使用资源

    资源就是你的程序代码结合在一起的多块数据,可以被程序本身在运行时加载。
    资源应当也放在程序的.EXE文件中的原因是:

    1.同时包含代码和数据的.EXE文件更容易发布。
    2.外力不容易任意删改程序的数据文件(如.BMP和.WAV文件)。

    对于想编译进程序中的数据类型没有限制,下列这些预定义的资源类型就可以
    满足大部分需要:

    图标 - 小的位图文件
    光标 - 鼠标指针的位图
    字符串 - 可以硬编码在代码中,也可以集中放在这
    声音 - 大部分Windows程序都使用.WAV格式
    位图 - 这是标准的位图,使用.BMP扩展名
    对话框 - 也可以作为资源来存储
    图元 - 一系列图像操作记录的回放



    要添加资源文件,必须有一个以ASCII形式的资源描述文件.RC。编译过程如下:



    下面是如何在.RC脚本文件中定义一个ICON资源:

    windowicon ICON star.ico (使用字符串)
    124 ICON ship.ico          (使用数字)

    使用字符串定义时会产生歧义,windowicon可能是个字符串也可能是个#define定义的符号常量。
    所以还需要一个.H文件来解析符号索引。

    RESOURCES.H的内容:

    #define ID_ICON1 100
    #define ID_ICON2 101
    #define ID_ICON3 102

    RESOURCES.RC的内容:

    #include "RESOURCES.H"

    ID_ICON1 ICON star.ico
    ID_ICON2 ICON ball.ico
    ID_ICON3 ICON cross.ico

    现在,将RESOURCES.RC和.ICO文件添加到工程,并在程序中#include RESOURCES.H。
    若使用字符串定义,则:winclass.hIcon = LoadIcon(hInstance, "icon_name");
    若使用符号索引,则:winclass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(ID_ICON1));
    注意第一个参数不再是NULL而是hInstance。

    其他资源也都是类似这样定义和使用的。
    光标:LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CURSOR1));
    字符串表:LoadString(hInst, IDS_STRING109, string3, 20);


    GDI简介

    应该了解GDI,以便知道如何在不使用DirectX的情况下如何在Windows环境绘制各种图形。
    而理解WM_PAINT消息对于标准的GDI图形和Windows编程来讲是非常重要的,因为大部分
    Windows程序的显示都围绕该消息。例外是游戏编程中DirectDraw或Direct3D负责图形绘制。

    PAINTSTRUCT ps;     // used in WM_PAINT
    HDC hdc;                 // handle to a device context
    case WM_PAINT:
    {
         // simply validate the window
         hdc = BeginPaint(hwnd, &ps);
         // do all your painting here
         EndPaint(hwnd, &ps);
         // return success
         return (0);
    }

    参看下面图示,当一个窗口被移动、改变大小或被其他窗口“弄脏”时,该窗口的用户区的
    部分或全部需要重画。这时,WM_PAINT消息就被发送了。



    对BeginPaint()和EndPaint()函数的调用可以完成一系列任务。首先,它们使用户区有效;
    其次,它们用该窗口创建时参照的Windows类中定义的背景刷来填充该窗口的背景。

    你只能访问实际上需要刷新的该窗口用户区的一部分。无效矩阵区域的坐标都保存在
    BeginPaint()函数返回值PAINTSTRUCT的rcPaint字段中。如果要访问整个用户区的话,
    这就是一个问题。解决方法是通过GetDC()直接获得图形设备描述表。

    但BeginPaint()-EndPaint()会向Windows发消息指示窗口有效,而GetDC()-ReleaseDC()不会,
    所以WM_PAINT消息将一直不停地传递下去,因为必须使该窗口有效。因此在ReleaseDC()
    后还要调用ValidateRect()。

    PAINTSTRUCT ps;
    HDC hdc;
    RECT rect;
    case WM_PAINT:
    {
         // simply validate the window
         hdc = GetDC(hwnd);
         // do all your painting here
         ReleaseDC(hwnd, hdc);
         // get client rectangle of window
         GetClientRect(hwnd, &rect);
         // validate window
         ValidateRect(hwnd, &rect);
         // return success
         return(0);
    }

    注意GetClientRect是用来获取用户矩形区域的坐标。每个窗口都有两套坐标系:
    Windows坐标系和用户坐标系。区别见下图:



    你很可能会说:“非得这么麻烦吗”是的,非得如此,因为这是Windows。哈哈!
    本书的大多数例程里,将在WM_PAINT消息以外的地方使用GetDC()-ReleaseDC(),
    BeginPaint()-EndPaint()只用于WM_PAINT消息句柄中。


    基本文本显示

    Windows有最复杂且最强悍的文本渲染系统。当实际运用在实时游戏中时,用GDI文本
    引擎输出文本就显得太慢了,还是要亲手设计基于DirectX的文本引擎。但先了解下有助于
    调试和输出。

    输出文本有两个常用函数:TextOut()和DrawText()。TextOut()是一个寒酸的文本输出函数,
    而DrawText()则像凌志汽车一样豪华。我经常使用TextOut()因为它运行比较快。

         case WM_PAINT: {
                   hdc = BeginPaint (hwnd, &ps) ;
                   GetClientRect (hwnd, &rect) ;
                      
                   char buffer[50];
                   static int counter = 0;
                   sprintf(buffer, "WM_PAINT called %d times ", ++counter);
                   TextOut(hdc, rect.left, rect.top, buffer, strlen(buffer));

                   DrawText (hdc, TEXT (“Hello text”), -1, &rect,
                        DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;               

                   EndPaint (hwnd, &ps) ;
                   return 0 ;
         }

    ==========================================================

    2012年4月22日 更新

    补充学习处理重要事件:Window操作、键盘操作、鼠标操作。

    1.Window操作

    WM_CLOSE消息在WM_DESTROY和WM_QUIT之后发送,它表示用户正试图关闭窗口。
    如果在WinProc()里仅return (0),那么用户不能关闭窗口。WM_SIZE消息对于窗口游戏
    非常重要,当窗口尺寸改变时,必须调整图像显示来适应。


    2.键盘操作

    在Windows环境下,可以以多种方式访问键盘消息:
    2.1 通过WM_CHAR消息:保存产生的ASCII码,如'a'或'A',字处理程序要关心这个。
    2.2 通过WM_KEYDOWN/UP消息:保存产生的扫描码,即键盘上每一个按键的编码,
    如ESC键VK_ESCAPE。游戏只需关心WASD移动,F开火而不必关心产生的是大写字符
    还是小写字符,所以不必关心WM_CHAR消息。
    2.3 通过调用GetAsyncKeyState():在状态表中跟踪该键的最后已知状态,使用它的
    妙处是它与事件循环没有耦合,可以在任何地方测试按键。


    3.鼠标操作

    鼠标移动事件WM_MOUSEMOVE。lParam保存鼠标位置,wParam保存按键状态。

    case WM_MOUSEMOVE:
    {
         int mouse_x = (int) LOWORD(lParam);
         int mouse_y = (int) LOWORD(lParam);
         int buttons = (int) wParam;
         if (buttons & MK_LBUTTON)
              ...
         if (buttons & MK_RBUTTON)
              ...
    } break;

    鼠标只是移动没有按键产生的事件。

    case WM_LBUTTONDBLCLK:
    {
         int mouse_x = (int) LOWORD(lParam);
         int mouse_y = (int) LOWORD(lParam);
         ...
         // tell windows you handled it
         return (0);
    } break;


    4.自行发送消息

    自行传递消息有两种方法:
    SendMessage()向窗口传递一个要求立即处理的消息。
    PostMessage()将消息发往窗口的消息队列。

    为什么要自行发送消息?因为Window的设计者希望你这样做,这也是窗口环境下的工作原理。
    下一章中学习按键控件时将会看到,发送消息是和控件窗口交流的唯一途径!

  • 相关阅读:
    【Demo 0087】线程创建使用消亡
    【Demo 0083】查看进程启动时间
    【Demo 0080】进程
    关于.vhd文件的一个问题
    此服务器上的时间与主域控制器的时间不一致解决方法
    centos5.6下安装mysql5.5.16
    杂谈,入社见闻录
    Oracle 11g学习笔记(1)
    Oracle 11g学习笔记(2)
    转:如何彻底卸载MySQL
  • 原文地址:https://www.cnblogs.com/xiaomaohai/p/6157844.html
Copyright © 2020-2023  润新知