《WINDOWS游戏编程之从零开始》第三章学习笔记

2022/2/4 7:12:30

本文主要是介绍《WINDOWS游戏编程之从零开始》第三章学习笔记,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

个人整理的笔记,比较杂乱,可能并不是很适合阅读:)


  1. MessageBox 函数

    其四个参数分别为:

    HWND 类型的 hWnd, 表示窗口句柄

    HWND 是什么类型?简单说就是给窗口分配的句柄。(废话啊啊啊)总之就用其代指一个窗口

    LPCTSTR 类型的 lpText,表示消息内容

    LPCTSTR 类型的 lpCaption,表示消息框标题内容

    UINT 类型的 uType,表示消息窗口的样式

  2. PlaySound 函数

    其三个参数分别为:

    LPCTSTR 类型的 pszSound,指定声音源

    HMODULE 类型的 hmod,资源可执行文件的句柄(暂时不清楚是个什么句柄)

    DOWRD 类型的 fdwSound,控制声音播放. 一些常用的标识:

在这里插入图片描述

模仿示例程序时的坑

注意 Visual Studio 中要改项目属性,保证链接器子系统设置为 Windows 不要设置为 Console,这样才是一个 Windows 文件。否则 WinMain 不会正确运行

在这里插入图片描述

(使用 Console 当然可以通过控制台加载出一样的效果,但是调试和运行都会多一个控制台,当然是客户端不能需要的东西)

例如,MessageBox 的 uType 设为 0,就会加载默认的窗口样式:

在这里插入图片描述

3.6

  1. MSG结构体

    消息用MSG表示.

  2. 消息队列.

3.7

窗口创建四部曲:

  • 窗口类的设计

  • 窗口类的注册

  • 窗口类的正式创建

  • 窗口的显示与更新

    1. 窗口设计

    Windows 中控制窗口特征的结构体就两个, WNDCLASS 与 WNDCLASSEX,我们应当使用EX(更(geng第四声)新)。

    所谓设计,其实就是在结构体里“做填空题”,定好参数。

    1. 窗口注册

    在设计好窗口后,还需要调用 RegisterClassEx 对其进行注册。注册成功后才可以创建该类型的窗口。

    不过这是啥原因?不知道注册这一步的意义是什么。应该是把固定参数列表的窗口对应到一个具体的窗口名吧

    1. 窗口创建

    调用 AdujstWindowRect() 函数来根据尺寸和风格计算窗口尺寸。

    设计、注册都完成了之后,就可以调用 CreateWindow 函数来创建设计好的窗口了。

    一共11个参数(我的天):
    (1)LPCTSTR 类的窗口类名称 lpClassName,填写我们设定并注册好的窗口类名。

    (2)LPCTSTR 类的窗口名字 lpWindowName,就是显示在窗口最上方的名字。

    (3)DOWRD 类的窗口样式 dwStyle。有无标题栏、带系统菜单、最小化最大化这些等等…

    (4)x. 一般设定为CW_USEDEFAULT

    (5)y. 设定为CW_USEDEFAULT

    (6)int 型的 nWidth,指定窗口宽度.

    (7)int 型的 hHeight,指定窗口高度.

    (8)HWND 类的 hWndParent,指定被创建窗口的父窗口句柄。一般设置为 NULL, 没有父窗口

    (9)HMENU 类的 hMenu,指定窗口菜单的资源句柄。没有菜单时当然也可以设置为 NULL.

    (10)HINSTANCE 类的 hInstance. 和 WinMain 一样。指定应用程序实例的句柄。

    (11)lpParam,WM_CREATE 消息的附加参数 lParam 传入的数据指针(暂时不懂)。

    1. 窗口的显示与更新

    (1)改变窗口大小 BOOL WINAPI MoveWindow()

    等等,为什么这里的布尔类是大写的?

    C++基础----C++ 布尔类型(bool)及BOOL和bool的区别_qiaoxinyu1989的博客-CSDN博客_bool类型

    原来,BOOL 类是微软的 VC++ 自己写的宏定义:

    typedef int BOOL
    

    这个 BOOL 是三值逻辑,1 为 TRUE,0 为 FALSE,-1为 ERROR.

    总之 BOOL 实际上是整型啦,其通过与0的比较来返回其三值逻辑。

    (2)显示窗口 ShowWindow(HWND hWnd, int nCmdShow)

    第一个就是要显示的窗口,第二个可以直接取 WinMain 中的 nCmdShow (指定窗口该如何显示).

    (3)更新窗口 UpdateWindow(HWND hWnd)

    发送一个 WM_PAINT 到消息队列,进入窗口过程函数, 对窗口进行更新。

    貌似和重画操作不同?肯定不同吧。是窗口的更新。

    3.8 消息循环体系

    消息循环体系…是啥啊

    1. GetMessage 消息循环体系

      其实是一个函数,其作用是接收消息。

      注意,GetMessage 函数是有返回值的。当收到非 WM_QUIT 消息时,其会返回1,如果出现错误,返回-1,收到 WM_QUIT 消息时返回0.

      所以,核心其实就是:

      MSG msg = ...
      while (GetMessage(&msg, NULL, 0, 0))
      {
          TranslateMessage(&msg);
          DispatchMessage(&msg);
      }
      
    2. PeekMessage 消息循环体系

      更常用一点.

      其多了一个参数 wRemoveMsg. 这个参数指定消息的获取方式(这又是啥

      一个使用 PeekMessage 循环体系的案例:

      MSG msg = { 0 };
      while (msg.message != WM_QUIT)
      {
          if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
          {
              TranslateMessage(&msg);
              DispatchMessage(&msg);
          }
          else
          {
              Direct3D_Update(hwnd);
              Direct3D_Render(hwnd);
          }
      }
      

      二者区别: PeekMessage 体系在没有消息时会继续进行其他的渲染和更新,而 GetMessage 体系则会一直等到消息出现才会进行其他操作。

      3.9 窗口过程函数

      窗口过程函数,用于处理消息。

      典型的过程函数是通过一个 switch 语句,switch 变量 message 的具体内容来进行相应的操作。

      default 状态通常会调用一个默认的 DefWindowProc 过程函数。

      3.10 窗口类注销

      已经迫不及待要制作一个完整的窗口程序了(这些内容也是附加的)

      注销操作并不是必须的,但为了程序稳定最好加上(万一出现意料之外的问题呢)

      原型:

      BOOL WINAPI UnregisterClass(
          _In_ LPCSTR lpClassName,
          _In_opt_ HISTANCE hInstance
      );
      

      参数分别是注销的类名称以及类的应用程序的实例句柄。

3.11

照着写的代码,贴出的与书中不同,有一些修改和自己的理解,注释中给出了.

//-------------------【程序说明】-------------------
// 程序名称:GameCore
// Learn from 浅墨 orz
// 2022 年 2 月 Created by Eke
// 描述:跟着浅墨一起学做一个最简单的Windows窗口程序!
//--------------------------------------------------

//-----------------【头文件部分】---------------------
// 描述:程序依赖的头文件
//--------------------------------------------------
#include <Windows.h>

//-----------------【宏定义部分】--------------------
// 描述:定义辅助宏
//--------------------------------------------------
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
constexpr auto WINDOW_TITLE = L"准备好,我来帮你了朋友!"; // 新版本中使用constexpr比define更安全.

//----------------【全局函数声明部分】----------------
// 描述:全局函数声明.
// 上为窗口过程函数
//--------------------------------------------------
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);


//----------------【WinMain( )函数】-----------------
// 描述:程序主入口
//--------------------------------------------------
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) // 需要将 _In_, _In_opt_ 依照函数原型加上,否则会报警告. 为啥呢?
{
	// 四部曲
	// 1. Design (Oh 11 paras!)
	WNDCLASSEX wndClass = { 0 }; // 定义一个窗口类
	wndClass.cbSize = sizeof(WNDCLASSEX);
	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WndProc;
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = (HICON)::LoadImage(NULL, L"favicon.ico", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); // 试了下没有RED\BLUE 的brush,qwq
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = L"ForTheDreamOfGameDevelop";
	
	// 2. Register
	if (!RegisterClassEx(&wndClass)) return -1;

	// 3. Create
	HWND hwnd = CreateWindow(L"ForTheDreamOfGameDevelop", WINDOW_TITLE,
		WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, hInstance, NULL);

	// 4. Move, show and update
	MoveWindow(hwnd, 250, 80, WINDOW_WIDTH, WINDOW_HEIGHT, true);
	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);

	// 5. msg loop
	MSG msg = { 0 };
	while (msg.message != WM_QUIT);
	{
		if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
	// 6. unregister
	UnregisterClass(L"ForTheDreamOfGameDevelop", wndClass.hInstance);
	return 0;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_PAINT:
		ValidateRect(hwnd, NULL);
		break;
	case WM_KEYDOWN:
		if (wParam == VK_ESCAPE) DestroyWindow(hwnd);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hwnd, message, wParam, lParam);
	}
	return 0;
}

运行效果:

在这里插入图片描述
总之就是一个很简单的窗体啦!

总结

第一次以笔记的形式把学习的东西记录下来。之前学过一些东西,总因为后来很久没接触逐渐忘了,重新学又需要整理很多资料重新看,不妨将学习过程写成笔记,方便将很久没用的知识捡回来,也能更好地综合我的学习内容。



这篇关于《WINDOWS游戏编程之从零开始》第三章学习笔记的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程