zoukankan      html  css  js  c++  java
  • 《Windows游戏编程大师技巧》二、Windows编程模型


    这一章开篇语对我有很大的启迪,一直对Windows下编程有着畏难心理。太庞大的平台和体系,
    太多的API和术语,太快的技术换代节奏,而我是个想要把握住本质掌握住基础才会有安全感
    和成就感的程序员,所以我只乐于学习Win32 API。而现在受到这本书的启发,对Windows编程
    的畏惧少了一些,也有了一些学习计划:

    1.窗体界面 -> Win32\MFC (简单了解界面消息处理机制吧,实在是不擅长画UI,MFC也有些难)
    2.网络编程 -> Winsock (这是我比较想学的)
    3.Web控件 -> ActiveX (不了解,应该学学)
    4.动画游戏 -> GDI\DirectX (感兴趣,想写点小游戏自娱自乐)

    所以我觉得写个小游戏作为业余项目是很不错的,游戏部分可以学学DirectX,设置界面可以简单
    学习下MFC,然后再给游戏增加个网络对战功能就可以学到Winsock了。业余项目关键是不要追求
    完美,先把基本功能完成,基础框架搭好。曾经想用Java2D写个塔防游戏,就是太想一下子就写好,
    于是就半途而废了。

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

    Windows编程就像去见牙医:虽然明明知道对自己是有益处的,可还是没人喜欢总是找牙医。
    虽然我不能保证在阅读本章之后你会变得更加喜欢去见牙医,但是我敢保证你会比以往更喜欢
    Windows编程。别因为我要解放你的思想而感到害怕(特别是钟情于DOS的顽固分子)。


    Windows的起源

    Windows 1.0完全建立在DOS基础上,不能执行多任务,运行缓慢。Windows 3.0让开发人员和
    商业应用程序逐渐脱离DOS。但游戏编程的人们还在唱着“坚守DOS岗位直到炼狱冻结”的赞歌。
    Windows 95推出后,我才真正开始喜欢Windows编程。

    Windows 9X/NT是多任务、多线程的,这和DOS有很大不同。DOS下,一旦你的程序开始运行,
    就只能运行该程序(中断处理程序除外)。要想多线程就必须自己模拟,这也是游戏程序员多年
    来所做的事。

    Windows还是一个事件驱动的操作系统。我们不必关心其他正在运行的应用程序,Windows会去处理,
    我们只要关心自己的应用程序中信息的处理。这在Windows 3.0/3.1中是不可能的,它们并不是真正的
    多任务系统,每一个应用程序都要产生下一个程序。



    匈牙利符号表示法

    像微软一样的公司,有几千个程序员在干不同的项目,一个名叫Simonyi的人负责创建了一套
    编写微软代码的规范。所有微软的API、界面、技术文件等等都采用这些规范。这个规范被称为
    匈牙利符号表示法,也许因为他经常加班弄得饥肠辘辘吧(饥饿Hungry和匈牙利Hungary)。



    1.变量:根据变量类型采用不同的前缀,之后的每个子名都要以大写字母开头。如char *szFileName;
              全局变量还要在开头加上g_或g,如int g_iXPos;

    2.函数:与变量相同,但没有前缀。如int PlotPixel(int ix, int iy, int ic);

    3.类型和常量:所有字母大写,子名可以用下划线分隔。如#define MAX_CELLS 64

    4.类:所有C++的类以大写C为前缀,其余与变量相同。如class CVector { ... };

    5.参数:与变量命名相同,如GetPixel(int ix, int iy); 但GetPixel(int x, int y);也是可以的。

    学会阅读匈牙利符号表示法但不代表要改变你自己的编程风格。本书在使用Win32 API函数的场合
    使用匈牙利表示法,而在其他位置使用自己的风格。


    世界上最简单的Windows程序

    基于DOS的“Hello World”程序。注意:要用VC创建一个控制台应用程序,
    这是类DOS的应用程序,只是它是32位的。将目标.EXE设为控制台应用
    程序,而非Win32.EXE!

    #include <stdio.h>
    
    void main(void)
    {
         printf("\nTHERE CAN BE ONLY ONE!!!\n");
    }

    Windows程序都以WinMain()开始,就像DOS都以main()开始一样。

    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    #include <windowsx.h>
    
    int WINAPI WinMain(HINSTANCE hInstance,
                           HINSTANCE hPrevInstance,
                           LPSTR lpCmdLine,
                           int nShowCmd)
    {
         MessageBox(NULL, "THERE CAN BE ONLY ONE!!!",
              "MY FIRST WINDOWS PROGRAM",
              MB_OK | MB_ICONEXCLAMATION);
         return (0);
    }


    现实中的Windows程序

    我们所需的只是一个基本的Windows程序,打开一个窗口、处理信息、调用游戏主循环等。
    因此只要进行下列工作:
         1. 创建Windows类。
         2. 创建事件句柄。
         3. 注册Windows类。
         4. 用之前的Windows类创建一个窗口。
         5. 创建一个能够从事件句柄获得或向事件句柄传递信息的主循环。

    1.创建Windows类

    Windows中的每一个窗口、控件、列表框、对话框和小部件都是一个Windows类。你也可以
    自定义Windows类,你编写的每一个应用程序都应该至少有一个Windows类,否则会非常麻
    烦。控制Windows类的数据结构是:WNDCLASSEX。创建Windows类就是设置好它的各种属性。

    WNDCLASSEX winclass = {
              sizeof(WNDCLASSEX),
              CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW,
              WindowsProc,
              0,
              0,
              hInst,
              LoadIcon(NULL, IDI_APPLICATION),
              LoadCursor(NULL, IDC_ARROW),
              (HBRUSH) GetStockObject(BLACK_BRUSH),
              NULL,
              "WINCLASS1",
              LoadIcon(NULL, IDI_APPLICATION)
         };

    2.事件句柄

    由上图看到一个重要的属性 - 回调函数,它是通过lpfnWndProc设定。
    来看一个事件处理函数的例子,在这里我们只关心WM_CREATE\PAINT\DESTROY三个事件。

    LRESULT CALLBACK WindowsProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
         HDC hdc ;
         PAINTSTRUCT ps ;
         RECT rect ;
         switch (message)
         {
              case WM_CREATE:               
                   return 0 ;
              case WM_PAINT:
                   hdc = BeginPaint (hwnd, &ps) ;               
                   EndPaint (hwnd, &ps) ;
                   return 0 ;
              case WM_DESTROY:
                   PostQuitMessage (0) ;
                   return 0 ;
         }
         return DefWindowProc (hwnd, message, wParam, lParam) ;
    }

    3.注册Windows类

    很简单只需一行代码:RegisterClassEx(&winclass);
    还有一个旧版本的RegisterClass()用于注册基于旧结构WNDCLASS类。
    一旦注册就可以任意创建它的窗口了。

    4.创建窗口

         hwnd = CreateWindowEx(
                             NULL,                         // extended style
                            "WINCLASS1",               // class
                             "Your Basic Window",     // title
                            WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                            0, 0, 400, 400,               // initial x/y/width/height
                            NULL,                         // parent window handle
                            NULL,                         // window menu handle
                            hInst,                         // program instance handle
                            NULL);                         // creation parameters

    如果上面没有设置WS_VISIBLE则需要手动调用ShowWindow来让窗口显示。
    调用UpdateWindow触发WM_PAINT事件。
    ShowWindow (hwnd, iCmdShow) ;
    UpdateWindow (hwnd) ;

    5.主事件循环

    while (GetMessage (&msg, NULL, 0, 0))
    {
         TranslateMessage (&msg) ;
         DispatchMessage (&msg) ;
    }

    非常简单。GetMessage是关键,用途是从事件队列中获得消息,保存到MSG结构中。
    MSG结构包含了WinProc参数列表中的所有参数,之后调用TranslateMessage稍加处理
    和转换,并通过DispatchMessage调用WinProc进行进一步处理。

    一个给力的图示,包含了所有事件处理流程!



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

    2012年4月22日 更新

    1.创建WNDCLASSEX时指定了类名为"WINCLASS1",之后注册它,并在创建窗体时指定了类名"WINCLASS1",
    所以就会关联到WNDCLASSEX,并创建出它指定样式大小的窗体。而在创建其他预定义控件时,如按钮控件,
    "button"类已经预定义并注册了,我们只需在调用CreateWindowEx指定类名为"button"。

    2.GetMessage()和PeekMessage()的区别:它们都是从消息队列中取消息。当消息队列中没有消息时,GetMessage()
    会阻塞当前线程,而PeekMessage()直接返回不会阻塞。并且PeekMessage()只取消息不从队列中删除,取出并删除
    消息需要指定PM_REMOVE参数:PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)。

    3.MSG结构:
         typedef struct tagMSG {
              HWND hwnd;          // window where message occurred
              UINT message;       // message id itself
              WPARAM wParam;   // sub qualifies message
              LPARAM lParam;      // sub qualifies message  
              DWORD time;          // time of message event
              POINT pt;              // position of mouse
         } MSG;

    MSG结构的前四个成员变量正是WinProc函数的四个参数。验证了WinMain主循环中GetMessage()取到MSG结构后,
    调用DispatchMessage()从MSG中取出参数并触发WinProc进行进一步处理。

    4.wParam和lParam在不同的消息中会保存不同的值。在按钮控件的消息中,msg为WM_COMMAND,LOWORD(wParam)
    为子窗口ID(创建按钮时指定),HIWORD(wParam)为通知码(1 - BN_CLICKED,2 - BN_DOUBLECLICKED),lParam为
    子窗口句柄。


  • 相关阅读:
    KVM安装配置
    cobbler配置解析
    denyhosts配置详解
    Rsync+sersync实现数据实时同步
    Linux启动提示Kernel panic
    oracle常用的数据字典
    Cachefiled
    from __future__ import division
    Java Map用法
    CCF系列之窗口(201403-2)
  • 原文地址:https://www.cnblogs.com/xiaomaohai/p/6157845.html
Copyright © 2011-2022 走看看