创建Windows窗体

游戏开发 专栏收录该内容
17 篇文章 1 订阅

    开发 游戏的第一步,需要建立窗体。

    做出的空白窗体,并没有游戏的绘图系统,只是“建立了一个标准窗体”而已。需要探讨的是,在窗体背后,Windows 系统的工作。

Windows 窗体的构成部分

    常规意义上的 Windows 窗体,由下列几个部分组成。

    标题栏:窗口上方的鼠标拖动条区域。标题栏的左边有控制菜单的图标,中间显示的是程序的标题。

    菜单栏:位于标题栏的下面,包含很多菜单,涉及的程序所负责的功能不一样,菜单的内容也不一样。比如有些有文件菜单,有些就没有,有一些窗体甚至根本就没有菜单栏。

    工具栏:位于菜单栏的下方,工具栏会以图形按钮的形式给出用户最常使用的一些命令。比如,新建、复制、粘贴、另存为等。

    工作区域:窗体的中间区域。一般窗体的输入输出都在这里面进行,比如子窗体显示、层叠,在工作区域的子窗体内进行文字编辑等等。游戏的图形图像就在此处显示。

    状态栏:位于窗体的底部,显示运行程序的当前状态。通过它,用户可以了解到程序运行的情况。

    滚动条:如果窗体中显示的内容过多,不管横向还是纵向,当前可见的部分不够显示时,窗体就会出现滚动条,分为水平滚动条与垂直滚动条两种。

    窗体缩放按钮:窗体的缩放按钮在右上角,在窗体编程中属于 System 类目。这些缩放按钮依次为最小化、最大化和关闭按钮。

    下面试一张标准的 Windows 窗体截图。
这里写图片描述
    窗体结构的说明:

typedef struct tagWNDCLASSEX {
  UINT      cbSize; // 结构体大小,等于 sizeof(WNDCLASSEX)
  UINT      style;  // 窗体的风格
  WNDPROC   lpfnWndProc; // 窗体函数指针
  int       cbClsExtra;  // 附加在窗体类后的字节数,初始化是零
  int       cbWndExtra;  // 附加在窗体实例化的附加字节数。系统初始化是零,如果一个应用程序使用 WNDCLASSEX 注册一个通过在资源中使用 CLASS 指令建立的对话框时,必须把这个成员设成 DLGWINDOWEXTRA。
  HINSTANCE hInstance; // 该对象的实例句柄
  HICON     hIcon;     // 该对象的图标句柄
  HCURSOR   hCursor;   // 该对象的光标句柄
  HBRUSH    hbrBackground; // 该对象的背景刷子
  LPCTSTR   lpszMenuName;  // 菜单指针
  LPCTSTR   lpszClassName;  // 类名指针
  HICON     hIconSm;       // 与窗体关联的小图标,如果这个值为 NULL,那么就把 hIcon 转换为大小比较合适的小图标
} WNDCLASSEX, *PWNDCLASSEX;

使用 C/C++ 编写 Windows 窗体

#include <windows.h>
LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);
char szClassName[ ] = "WindowsApp";
int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nFunsterStil)

 {
    HWND hwnd;               /* 指向我们窗体的句柄 */
    MSG messages;            /* 保存发往应用的消息 */
    WNDCLASSEX wincl;        /* 前面详细介绍过的 WNDCLASSEX 结构的对象 */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      
    wincl.style = CS_DBLCLKS;                
    wincl.cbSize = sizeof(WNDCLASSEX);

    给 WNDCLASSEX 结构对象赋值。

/* 使用默认图标以及鼠标指针 */
    wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor(NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL; /* 没有菜单栏 */
    wincl.cbClsExtra = 0;                      /* 没有多余的字节跟在窗体类的后面 */
    wincl.cbWndExtra = 0;                      
    wincl.hbrBackground = (HBRUSH) GetStockObject(LTGRAY_BRUSH);
    if(!RegisterClassEx(&wincl)) return 0;

    代码在窗口过程调用函数的时候,将地址赋值给 lpfnWndProc,然后呼叫 RegisterClassEx(&wincl) 注册窗口类,系统就拥有了窗口过程函数的地址。如果注册失败,则返回 0。

hwnd = CreateWindowEx( 0,            /* 扩展风格为 0*/                               
           szClassName,         /* 类名 */
           "Windows App",         /* 窗体抬头标题 */
           WS_OVERLAPPEDWINDOW, /* 默认窗体 */
           CW_USEDEFAULT,       /* 让操作系统决定窗体对应 Windows 的 X 位置在哪里 */
           CW_USEDEFAULT,       /* 让操作系统决定窗体对应 Windows 的 Y 位置在哪里 */
           544,                 /* 程序宽度 */
           375,                 /* 程序高度 */
           HWND_DESKTOP,        /* 父窗体的句柄,父窗体定义为 Windows 桌面,HWND_DESKTOP 是系统定义的最顶层的托管的窗体 */
           NULL,                /* 没有菜单 */
           hThisInstance,       /* 程序实例化句柄 */
           NULL                 /* 指向窗体的创建数据为空 */
           );
    ShowWindow(hwnd, nFunsterStil);
    /* 要显示窗体,使用的是 ShowWindow 函数 */
    while(GetMessage(&messages, NULL, 0, 0))
    {      
           TranslateMessage(&messages);
           DispatchMessage(&messages);    
    }
    return messages.wParam;
}

    建立并显示窗体,在循环内将虚拟键消息转换为字符串消息,随后调度一个消息给窗体程序。

LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)                  /* 指向消息的句柄 */
    {
           case WM_DESTROY:
           PostQuitMessage(0);        
           break;
           default:                   
           return DefWindowProc(hwnd, message, wParam, lParam);

    }
    return 0;
}

    最后是消息处理。当窗体程序接收到某些操作的时候,比如键盘、鼠标等等,就会呼叫 DispatchMessage(&messages);函数将消息回调给系统,系统通过注册的窗口类得到函数指针并且通过函数指针调用函数对消息进行处理。

     MoveWindow,就是移动已经建立的窗体。MoveWindow 函数用来改变窗口的位置和尺寸,如果窗体本身就按照计算机的屏幕对齐左上角,对于窗体内的子窗体,就对齐父窗体的左上角。

BOOL MoveWindow( HWND hWnd,/* 窗体句柄 */
         int x,  /* 窗体左上角起点 x 轴 */
         int y,  /* 窗体左上角起点 y 轴 */
         int nWidth, /* 窗体宽度 */
         int nHeight, /* 窗体高度 */
         BOOL bRepaint = TRUE /* 是否重新绘制,如果是 true 系统会发送 WM_PAINT 到窗体,然后呼叫 UpdateWindow 函数进行重新绘制,如果是 false 则不重新绘制 */

    MoveWindow 会给窗体发送 WM_WINDOWPOSCHANGING,WM_WINDOWPOSCHANGED,WM_MOVE,WM_SIZE 和 WM_NCCALCSIZE 消息。

    类似的功能还有 SetWindowPos,SetWindowPos 功能更强大,可以设置更多的参数。

    目前只是建立了一个 Windows 的窗体,并不能直接编写游戏。

    OpenGL 并不附带任何关联窗体的编程,所以如果你使用的是 OpenGL 的接口来编写代码,稍微修改一下,这些窗体就能成为游戏屏幕窗体。

    游戏所有的内容都是在一个循环内完成的,即所有的绘图、线程、操作、刷新,都在一个大循环内完成。

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

    在这个 while 循环中,消息的派发都在此完成。游戏也一样,所有游戏内的代码几乎都在循环内完成。可以想象一个循环完成一个大的绘制过程,第二个循环刷新前一次绘制过程,最终类似电影一样,完成整个动画的绘制以及不间断的操作。

    在建立 Windows 窗体的时候,程序会从入口函数 WinMain 开始运行,定义和初始化窗体类,然后将窗体类实例化,随后进行消息循环获取消息,然后将消息发送给消息处理函数,最后做出相应的操作。

    使用脚本语言的方式编写窗体,只需要定义坐标、位置和窗体名称即可。

  • 0
    点赞
  • 0
    评论
  • 4
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值