游戏引擎学习第二天

视频参考: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;
}
相关推荐
非概念4 分钟前
stm32学习笔记----51单片机和stm32单片机的区别
笔记·stm32·单片机·学习·51单片机
无敌最俊朗@1 小时前
stm32学习之路——八种GPIO口工作模式
c语言·stm32·单片机·学习
EterNity_TiMe_2 小时前
【论文复现】STM32设计的物联网智能鱼缸
stm32·单片机·嵌入式硬件·物联网·学习·性能优化
墨笺染尘缘2 小时前
Unity——对RectTransform进行操作
ui·unity·c#·游戏引擎
L_cl2 小时前
Python学习从0到1 day28 Python 高阶技巧 ⑤ 多线程
学习
前端SkyRain2 小时前
后端Node学习项目-用户管理-增删改查
后端·学习·node.js
提笔惊蚂蚁2 小时前
结构化(经典)软件开发方法: 需求分析阶段+设计阶段
后端·学习·需求分析
DDDiccc2 小时前
JAVA学习日记(十五) 数据结构
数据结构·学习
AgilityBaby3 小时前
FairyGUI和Unity联动(入门篇)
unity·游戏引擎
腾科张老师4 小时前
为什么要使用Ansible实现Linux管理自动化?
linux·网络·学习·自动化·ansible