游戏引擎学习第二天

视频参考:https://www.bilibili.com/video/BV1C2miYPEQf/

WNDCLASSA

https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-wndclassa

在 C 语言中然后使用时,需要加上 struct 关键字:

为了让代码更加简洁,C 语言引入了 typedef,

允许我们为结构体类型定义一个别名,

这样就不需要每次都写 struct 关键字了:

// 使用时不再需要 struct 关键字

WNDCLASSA wc; // 直接使用别名

WNDCLASS WindowClass = {}; 是标准的 零初始化,将所有成员初始化为其类型的默认值(通常是 0 或 nullptr)。

WNDCLASS WindowClass = {0}; 将结构体的第一个成员初始化为 0,并且其他成员通常会被零初始化。

CS_CLASSDCCS_OWNDC 是 Windows API 中 WNDCLASS 结构体的 style 成员的两个标志位,用来指定窗口类的设备上下文 (DC) 行为。

标志 描述 设备上下文类型
CS_CLASSDC 所有窗口共享一个设备上下文 (DC) 共享 DC
CS_OWNDC 每个窗口都有自己的设备上下文 (DC) 独立 DC
  • CS_CLASSDC 适合需要共享设备上下文的情况。
  • CS_OWNDC 适合需要每个窗口独立绘制的情况。

MainWindowCallback 函数是一个 窗口过程函数(Window Procedure),在 Windows 程序中,它用于处理与窗口相关的消息。

每当窗口接收到消息时(如用户输入、窗口大小变化、系统事件等),该函数就会被调用来处理这些消息。

处理窗体消息

cpp 复制代码
// game.cpp : Defines the entry point for the application.
//

#include <windows.h>
LRESULT CALLBACK
MainWindowCallback(HWND hwnd, // 窗口句柄,表示消息来源的窗口
                   UINT Message, // 消息标识符,表示当前接收到的消息类型
                   WPARAM wParam, // 与消息相关的附加信息,取决于消息类型
                   LPARAM LParam) { // 与消息相关的附加信息,取决于消息类型
  LRESULT Result; // 定义一个变量来存储消息处理的结果

  switch (Message) {                 // 根据消息类型进行不同的处理
  case WM_SIZE: {                    // 窗口大小发生变化时的消息
    OutputDebugStringA("WM_SIZE\n"); // 输出调试信息,表示窗口大小已改变
  } break;

  case WM_DESTROY: {                    // 窗口销毁时的消息
    OutputDebugStringA("WM_DESTROY\n"); // 输出调试信息,表示窗口正在销毁
  } break;

  case WM_CLOSE: {                    // 窗口关闭时的消息
    OutputDebugStringA("WM_CLOSE\n"); // 输出调试信息,表示窗口正在关闭
  } break;

  case WM_ACTIVATEAPP: { // 应用程序激活或失去焦点时的消息
    OutputDebugStringA(
        "WM_ACTIVATEAPP\n"); // 输出调试信息,表示应用程序激活或失去焦点
  } break;

  default: { // 对于不处理的消息,调用默认的窗口过程
    Result = DefWindowProc(hwnd, Message, wParam,
                           LParam); // 调用默认窗口过程处理消息
  } break;
  }

  return Result; // 返回处理结果
}

int CALLBACK WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, //
                     PSTR cmdline, int cmdshow) {
  WNDCLASS WindowClass = {};
  // 使用大括号初始化,所有成员都被初始化为零(0)或 nullptr

  // WindowClass.style:表示窗口类的样式。通常设置为一些 Windows
  // 窗口样式标志(例如 CS_HREDRAW, CS_VREDRAW)。
  WindowClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
  // CS_HREDRAW 当窗口的宽度发生变化时,窗口会被重绘。
  // CS_VREDRAW 当窗口的高度发生变化时,窗口会被重绘

  //  WindowClass.lpfnWndProc:指向窗口过程函数的指针,窗口过程用于处理与窗口相关的消息。
  WindowClass.lpfnWndProc = MainWindowCallback;

  // WindowClass.hInstance:指定当前应用程序的实例句柄,Windows
  // 应用程序必须有一个实例句柄。
  WindowClass.hInstance = hInst;

  // WindowClass.lpszClassName:指定窗口类的名称,通常用于创建窗口时注册该类。
  WindowClass.lpszClassName = "gameWindowClass"; // 类名

  return 0;
}

注册窗口

注册窗口 成功后创建窗口

cpp 复制代码
if (RegisterClass(&WindowClass)) {  // 如果窗口类注册成功
    HWND WindowHandle = CreateWindowEx(0,                                // 创建窗口,使用扩展窗口风格
                                       WindowClass.lpszClassName,        // 窗口类的名称,指向已注册的窗口类
                                       "game",                           // 窗口标题(窗口的名称)
                                       WS_OVERLAPPEDWINDOW | WS_VISIBLE, // 窗口样式:重叠窗口(带有菜单、边框等)并且可见
                                       CW_USEDEFAULT,                    // 窗口的初始位置:使用默认位置(X坐标)
                                       CW_USEDEFAULT,                    // 窗口的初始位置:使用默认位置(Y坐标)
                                       CW_USEDEFAULT,                    // 窗口的初始宽度:使用默认宽度
                                       CW_USEDEFAULT,                    // 窗口的初始高度:使用默认高度
                                       0,                                // 父窗口句柄(此处无父窗口,传0)
                                       0,                                // 菜单句柄(此处没有菜单,传0)
                                       hInst,                            // 当前应用程序的实例句柄
                                       0                                 // 额外的创建参数(此处没有传递额外参数)
    );
    // 如果窗口创建成功,WindowHandle 将保存窗口的句柄
} else {  // 如果窗口类注册失败
    // 这里可以处理注册失败的逻辑
    // 比如输出错误信息,或退出程序等
}

如果窗口创建成功,启动一个无限循环,等待和处理消息

cpp 复制代码
// 如果窗口创建成功,WindowHandle 将保存窗口的句柄
if (WindowHandle) {  // 检查窗口句柄是否有效,若有效则进入消息循环
    for (;;) {  // 启动一个无限循环,等待和处理消息
        MSG Message;  // 声明一个 MSG 结构体,用于接收消息
        BOOL MessageResult = GetMessage(&Message, // 获取下一个消息
                                        0,        // 目标窗口句柄,0表示从所有窗口获取消息
                                        0,        // 获取所有消息
                                        0);       // 获取所有消息

        if (MessageResult > 0) {  // 如果返回值大于 0,表示成功获取到消息
            TranslateMessage(&Message);  // 翻译消息,如果是键盘消息需要翻译
            DispatchMessage(&Message);   // 分派消息,调用窗口过程处理消息
        } else {  // 如果返回值为 0 或 -1,表示没有更多消息或发生了错误
            break;  // 跳出循环,结束消息循环
        }
    }
} else {  // 如果窗口创建失败
    // 这里可以处理窗口创建失败的逻辑
    // 比如输出错误信息,或退出程序等
}

至此windows窗口创建成功
alt text

现在窗口不能关闭,能输出WM_SIZE,WM_ACTIVATEAPP 消息

绘制

cpp 复制代码
  case WM_PAINT: { // 处理 WM_PAINT 消息,通常在窗口需要重新绘制时触发
    PAINTSTRUCT Paint; // 定义一个 PAINTSTRUCT 结构体,保存绘制的信息
    // 调用 BeginPaint 开始绘制,并获取设备上下文 (HDC),同时填充 Paint 结构体
    HDC DeviceContext = BeginPaint(hwnd, &Paint);

    // 获取当前绘制区域的左上角坐标
    int x = Paint.rcPaint.left;
    int y = Paint.rcPaint.top;

    // 计算绘制区域的宽度和高度
    int Height = Paint.rcPaint.bottom - Paint.rcPaint.top;
    int Width = Paint.rcPaint.right - Paint.rcPaint.left;
    // 这里的宽度计算是错误的,应该是右边减去左边

    // 使用 WHITENESS 操作符填充矩形区域为白色
    PatBlt(DeviceContext, x, y, Width, Height, WHITENESS);

    // 调用 EndPaint 结束绘制,并释放设备上下文
    EndPaint(hwnd, &Paint);
  } break;
cpp 复制代码
PAINTSTRUCT 主要在 窗口绘制(painting) 过程中使用,特别是当窗口的部分区域需要重绘时(例如窗口被覆盖后再恢复)。BeginPaint 和 EndPaint 函数通常会使用 PAINTSTRUCT 来获取绘制所需的上下文信息。
typedef struct tagPAINTSTRUCT {
    HDC         hdc;            // 设备上下文句柄,用于绘制窗口内容
    BOOL        fErase;         // 是否清除背景,TRUE表示清除,FALSE表示不清除
    RECT        rcPaint;        // 描述要更新的区域(矩形区域),窗口内容被绘制到该区域
    BOOL        fRestore;       // 是否恢复被擦除区域的内容,通常为 TRUE 时表示恢复
    BOOL        fIncUpdate;     // 是否为增量更新(更新的区域是变化部分)
    BYTE        rgbReserved[32]; // 保留字段,用于扩展结构体,通常不使用
} PAINTSTRUCT, *PPAINTSTRUCT, *NPPAINTSTRUCT, *LPPAINTSTRUCT;

//PatBlt 函数是 Windows 图形设备接口(GDI)的一部分,用于执行图形绘制操作,具体是填充矩形区域,且操作可以基于位图操作符(ROP)。以下是对 PatBlt 函数的详细解释:

cpp 复制代码
WINGDIAPI BOOL WINAPI PatBlt(
    _In_ HDC hdc,   // 设备上下文句柄
    _In_ int x,     // 矩形区域的左上角 x 坐标
    _In_ int y,     // 矩形区域的左上角 y 坐标
    _In_ int w,     // 矩形的宽度
    _In_ int h,     // 矩形的高度
    _In_ DWORD rop  // 位图操作符
);

如果PatBlt 编译未定义链接Gdi32.lib

复制代码
# 链接 User32.lib 库
target_link_libraries(game PRIVATE User32.lib Gdi32.lib)

设置填充颜色白色和黑色来回变换在刷新窗口的时候

cpp 复制代码
static DWORD Operation = WHITENESS;

// 使用 WHITENESS 操作符填充矩形区域为白色
PatBlt(DeviceContext, x, y, Width, Height, Operation);
if (Operation == WHITENESS) {
    Operation = BLACKNESS;
} else {
    Operation = WHITENESS;
}
相关推荐
WarPigs32 分钟前
Unity性能优化笔记
笔记·unity·游戏引擎
Chef_Chen4 小时前
从0开始学习R语言--Day18--分类变量关联性检验
学习
键盘敲没电4 小时前
【IOS】GCD学习
学习·ios·objective-c·xcode
海的诗篇_5 小时前
前端开发面试题总结-JavaScript篇(一)
开发语言·前端·javascript·学习·面试
AgilityBaby5 小时前
UE5 2D角色PaperZD插件动画状态机学习笔记
笔记·学习·ue5
AgilityBaby5 小时前
UE5 创建2D角色帧动画学习笔记
笔记·学习·ue5
武昌库里写JAVA6 小时前
iview Switch Tabs TabPane 使用提示Maximum call stack size exceeded堆栈溢出
java·开发语言·spring boot·学习·课程设计
一弓虽7 小时前
git 学习
git·学习
Moonnnn.9 小时前
【单片机期末】串行口循环缓冲区发送
笔记·单片机·嵌入式硬件·学习
viperrrrrrrrrr710 小时前
大数据学习(131)-Hive数据分析函数总结
大数据·hive·学习