• 《Windows游戏编程大师技巧》 四、GDI、控件和突发奇想



    本章是纯粹讲述Windows编程的最后一章。谢天谢地!

    高级GDI绘图

    GDI比起DirectX来实在太慢了,那为什么还要了解?

    1.GDI功能很全,并且是Windows与生俱来的渲染引擎。
    2.如果想创建任何工具或标准应用程序,都要用到GDI。
    3.GDI与DirectX混合使用,来仿真DirectX尚未实现的函数。


    到底什么是图形设备描述表?

    图形设备描述表HDC保存了视频卡的各种信息,访问各种GDI函数时都要用到。
    对于使用GDI的任何函数,它都是必须的。并且它还保存了设置,这样省去了
    GDI函数调用时的许多参数。


    画笔:用于画线条和轮廓。
    画刷:用于填充任何闭合的对象。

    在你的系统配置中有许多画笔和画刷,但是在当前图形设备描述表中每次只有
    一个画笔或画刷被激活。该过程称之为选定(selection)。对于得到的画笔或画刷,
    在完成绘制图形之后必须删除该画笔或画刷。因为Windows GDI关于画笔和画刷句柄
    只有有限数目的存取位置,有可能被用光!

    HGDIOBJ GetStockObject(int fnObject);                               // 获得一些预定义的对象句柄
    HPEN CreatePen(int fnPenStyle, int nWidth, COLORREF crColor);// 创建新画笔
    HGDIOBJ SelectObject(HDC hdc, HGDIOBJ hObject);              // 返回之前正在被使用的画笔句柄
    BOOL DeleteObject(HGDIOBJ hObject);                                // 销毁的画笔


    绘制点

    使用GDI绘制点很容易,不需要画笔或画刷。
    Windows GDI使用的是一个上下颠倒的第一象限的笛卡尔坐标系。



         for (int index = 0; index < 1000; index++) {
                        int x = rand() % 400;
                        int y = rand() % 400;
                        COLORREF color = RGB(rand()%255, rand()%255, rand()%255);
                        SetPixel(hdc, x, y, color);
         }



    绘制线段

    1.创建画笔,并在图形设备描述表中选定它。
    2.调用MoveToEx()设定线段的起始位置。
    3.调用LineTo()从起始位置到终点位置绘制线段。

         HPEN blue_pen = CreatePen(PS_SOLID, 1, RGB(0, 0, 255));
         HGDIOBJ old_pen = SelectObject(hdc, blue_pen);
                     
         MoveToEx(hdc, 10, 10, NULL);
         LineTo(hdc, 50, 60);

         SelectObject(hdc, old_pen);
         DeleteObject(blue_pen);

    绘制矩形、圆形、多边形都是类似的。


    定时高于一切

    PC内置有一个精确的定时器(毫秒级),但我们最好还是使用Windows内置的定时函数。
    Windows将这个定时器虚拟成为几乎无限多个虚拟定时器,定时地向WinProc传递WM_TIMER
    消息。



    创建定时器很方便,但是该技术是有缺陷的:第一,多个定时器将发送多个消息;
    第二,定时器并不那么准确;最后,在大多数游戏循环中,都希望程序以指定帧频
    运行。我们可以通过查询系统时钟来实现。

    while (TRUE)
    {
         DWORD start_time = GetTickCount();

         // Retrieve msg in queue(non-block, GetMessage may block if queue is empty)
         if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
         {
              if (msg.message == WM_QUIT)
                   break;
              TranslateMessage(&msg);
              DispatchMessage(&msg);
         }

         // Lock time to 30 fps (1s / 30 = 33ms)
         while ((GetTickCount() - start_time) < 33);

         // Main game processing goes here
         if (KEYDOWN(VK_ESCAPE))
              SendMessage(hwnd, WM_CLOSE, 0, 0);
    }


    使用控件

    游戏编程方面的书一般不讨论Windows控件功能,但了解一些可以用控件作为工具。

    创建按钮:
         CreateWindowEx(
                                  NULL,                         // extended style
                                  "button",                    // class
                                  "PUSHBUTTON",          // title
                                  WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
                                  10,
                                  10 + button * 36,          // initial x, y
                                  strlen(button_names[button]) * 16,
                                  24,                              // initial width, height
                                  hwnd,                         // handle to parent
                                  (HMENU) (100 + button),     // handle to menu
                                  hInst,                         // instance of application
                                  NULL);

    处理按下按钮时产生的WM_COMMAND消息,上面设置了按钮控件的id是100。

         case WM_COMMAND:
         {
              if (LOWORD(wparam) == 100)
                   ...
         }



    控件同时也是窗口,也能像窗口一样接收消息。但由于它们是某个父控件的子控件,
    接收到的消息被转发到父控件窗口事件句柄中。我们可以使用SendMessage()向子控件
    发送消息。比如SendMessage(hwndbutton, BM_CLICK, 0, 0); 向按钮发送点击事件。


  • 相关阅读:
    在PHP中截取字符串
    SQL查看一张表中是否存在记录
    最多两位小数,如123.45的转换结果为:壹百贰拾叁元肆角伍分
    SQL将金额转换为汉子
    SQL 汉字转换成拼音首字母 首字母查
    SQL生成随机数
    Linux gsoap2.6 webservices
    cxGrid的一些使用方法
    不修改forms.pas单元就可以去掉MDI窗口的滚动条
    今天,我种下了一朵小蓝花
  • 原文地址:https://www.cnblogs.com/xiaomaohai/p/6157843.html
Copyright © 2020-2023  润新知