第二章 DirectX 基础(下)

接下来我们介绍update函数,这个函数就是用来接收用户事件,并做出事件处理的。因此,它肯定是放在窗口过程函数WndProc中的。这个窗口过程函数WndProc函数中定义了很多事件,比如说键盘按下,按起,鼠标点击,鼠标移动等等。也就是说,我们需要在switch中来列举出这几种事件。各种事件的处理方式是不一样的。例如键盘按下和按起事件,我们需要知道用户按下的是那个键,比如说数字键,字母键或是其他。鼠标点击事件主要是获取到鼠标的位置,这个位置坐标是相对于窗体左上角而言的。说白了,鼠标位置坐标(x,y)就是以窗体左上角为原点的二维坐标系中,向右为X轴正值,向下为Y轴正值。鼠标移动事件大部分情况下,其实没有必要单独处理,只需要更新鼠标位置坐标(x,y)即可。我们自定义的update函数有两个参数,一个是int type, 另一个是WPARAM wParam。第一个int type我们就用来定义事件类型。第二个WPARAM wParam其实就是窗口过程函数WndProc中的形参wParam,这个就代表了键盘按键值,另外一个LPARAM lParam就是鼠标位置信息。首先我们先声明鼠标位置全局变量(x/y),我们在鼠标点击和移动事件中,来更新这两个数值。

// Direct3D设备指针对象
LPDIRECT3DDEVICE9 D3DDevice = NULL;

// 字体指针对象
LPD3DXFONT D3DFont = NULL;

// 鼠标位置
int mx = 0, my = 0;

然后,我们调整窗口过程函数,增加鼠标和键盘事件处理,代码如下:

case WM_KEYDOWN:				// 按下键盘
	update(1, wParam);
	render(hwnd);
	break;

case WM_KEYUP:					// 按起键盘
	update(2, wParam);
	render(hwnd);
	break;

case WM_LBUTTONUP:				// 鼠标左击
	if (wParam & MK_LBUTTON) {
		mx = LOWORD(lParam);
		my = HIWORD(lParam);
	}
	update(3, NULL);
	render(hwnd);
	break;

case WM_RBUTTONUP:				// 鼠标右击
	if (wParam & MK_RBUTTON) {
		mx = LOWORD(lParam);
		my = HIWORD(lParam);
	}
	update(4, NULL);
	render(hwnd);
	break;

case WM_MOUSEMOVE:				// 鼠标移动
	mx = LOWORD(lParam);
	my = HIWORD(lParam);
	update(5, NULL);
	render(hwnd);
	break;

当事件发生后,我们根据事件类型做简单处理后,然后就调用update方法来对事件做游戏逻辑处理,处理完之后再次调用render方法进行游戏画面的渲染。由于我们这里并没有真实的游戏逻辑,因此我们的update方法仅仅打印事件信息即可,也就是键盘的按下键值,以及鼠标点击或移动的位置坐标。在这里,我们借助C++string来拼接这些事件信息。使用string的话,首先要引入头文件,如下:

// 引入头文件
#include <windows.h>
#include <d3d9.h>
#include <d3dx9.h>
#include <string>

之后,我们再声明事件信息的字符串全局变量,如下:

// 鼠标位置
int mx = 0, my = 0;
std::string mouseStr = "没有鼠标事件";		// 鼠标事件
std::string keyboardStr = "没有键盘事件";		// 键盘事件
wchar_t* stringToWchar(std::string str);	// string转wchar_t

需要注意一点的是,我们使用D3DFont->DrawText绘制文字的时候,文字必须是wchar_t类型,因此我们需要将string转成wchar_t类型。首先,我们先完成update函数,然后再给出stringToWchar函数。代码如下:

// 鼠标事件
if (type == 3 || type == 4 || type == 5) {

	if(type == 3) mouseStr = "鼠标左击";
	if(type == 4) mouseStr = "鼠标右击";
	if(type == 5) mouseStr = "鼠标移动";
	mouseStr += ",位置 x = " + std::to_string(mx) + ", y = " + std::to_string(my);
}

// 键盘事件
if (type == 1 || type == 2) {

	if(type == 1) keyboardStr = "按下键盘";
	if(type == 2) keyboardStr = "按起键盘";
	
	switch (wParam)
	{
	case 'A': keyboardStr += ",按下 A"; break;
	case 'B': keyboardStr += ",按下 B"; break;
	case 'C': keyboardStr += ",按下 C"; break;
	// 中间代码太长忽略
	default: keyboardStr += ",其他按键"; break;
	}
}

Update方法处理完毕后,我们就需要增加打印事件信息的代码。

// 第三步:绘制文字
RECT rect;
GetClientRect(hwnd, &rect);
D3DFont->DrawText(0, L"Hello,Direct3D游戏开发!", -1, &rect, DT_CENTER, D3DCOLOR_XRGB(255, 0, 0));

// 绘制鼠标和键盘事件信息
rect.top += 50;
D3DFont->DrawText(0, stringToWchar(mouseStr), -1, &rect, DT_LEFT, D3DCOLOR_XRGB(255, 0, 0));
rect.top += 50;
D3DFont->DrawText(0, stringToWchar(keyboardStr), -1, &rect, DT_LEFT, D3DCOLOR_XRGB(255, 0, 0));

最后我们在补充上字符串转换函数stringToWchar即可,代码如下:

// 字符串转换
wchar_t* stringToWchar(std::string inputStr) {

	int len = (int)(inputStr.length() + 1);
	wchar_t* inputStr2 = new wchar_t[len];
	MultiByteToWideChar(CP_ACP, 0, inputStr.c_str(), -1, inputStr2, len);
	return inputStr2;
}

整个程序基本完成了,最后还有一个endGame函数,它主要用来释放COM对象。在本案例中我们使用了D3DDeviceD3DFont两个COM对象,释放代码如下:

// 定义游戏结束函数(释放对象)
void endGame() {

	D3DFont->Release();
	D3DFont = NULL;
	D3DDevice->Release();
	D3DDevice = NULL;
}

注意,这个endGame方法调用是在什么位置?就是在窗体销毁的位置,

case WM_DESTROY:
	endGame();				// 调用游戏结束函数
	PostQuitMessage(0);		// 窗口销毁消息
	break;

在Windows 系统中,当我们按下键盘按键或者鼠标点击的时候,Windows 系统会将此事件视为“消息”,并将该消息存放到应用程序的消息队列中。应用程序需要做一个消息循环,使用PeekMessage方法获取消息,TranslateMessage函数用于将虚拟键消息转换为字符消息,DispachMessage函数分派一个消息到窗口过程函数对消息进行处理。在窗口过程函数中,我们可以获取到消息的类型以及附加信息,进而做对应的业务处理。WM_KEYDOWNWM_KEYUP就属于消息类型,除了键盘和鼠标消息之外,还存在其他消息,比如窗口重绘消息WM_PAINT,窗口销毁消息WM_DESTROY等等。一般在处理键盘事件的时候,按下按键和按起按键是前后成对发生的,我们只处理一个即可。对消息的处理是在窗口过程函数进行处理的,该函数中有三个参数UINT messageWPARAM wParamLPARAM lParam参数。其中UINT message就代表了消息类型,另外两个参数会根据消息类型的不同,分别代表不同的附加信息。

Windows系统中,所有键盘的按键都被定义为一组通用的“虚拟键码”,每一个按键都对应一个虚拟键码。例如 VK_RETURN代表回车键,VK_ESCAPE代表ESC键,09代表数字键,AZ代表字母键等等。我们使用窗口过程函数处理键盘事件的时候,WPARAM wParam就代表了虚拟键码。

处理鼠标消息的方法与处理键盘消息的方法类似,当鼠标消息发生时,WPARAM wParam参数则记录鼠标按键以及Ctrl键和Shift键的状态信息。这个WPARAM wParam参数可以进一步帮助我们进行鼠标按下的判断。LPARAM lParam参数的值可分为高位字节和低位字节两个部分,其中高位字节是鼠标X坐标值,低位字节则是鼠标Y坐标值。这个坐标值是相对于窗口左上角(0,0)位置而言的,不是屏幕左上角。另外,我们还需要了解几个函数,SetCursorPos函数用来设定鼠标的位置,参数就是X/Y坐标值。GetCursorPos函数用于获取鼠标位置,参数为POINT类型(也是X/Y坐标值)。ClientToScreen表示窗口坐标转换屏幕坐标,ScreenToClient代表屏幕坐标转窗口坐标。ShowCursor用于显示和隐藏鼠标,参数是bool值,true代表显示鼠标,false代表隐藏鼠标。

 本课程的所有代码案例下载地址:

workspace.zip

备注:这是我们游戏开发系列教程的第二个课程,这个课程主要使用C++语言和DirectX来讲解游戏开发中的一些基础理论知识。学习目标主要依理解理论知识为主,附带的C++代码能够看懂且运行成功即可,不要求我们使用DirectX来开发游戏。课程中如果有一些错误的地方,请大家留言指正,感激不尽!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咆哮的程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值