DX11 Without DirectX SDK--05 键盘和鼠标输入
前言
提供键鼠输入可以说是一个游戏的必备要素。在这里,我们不使用DirectInput
,而是使用Windows消息处理机制中的Raw Input
,不过要从头开始实现会让事情变得很复杂。DXTK提供了鼠标输入的Mouse.h
和键盘输入的Keyboard.h
(现在已经单独抽离出来使用),对消息处理机制进行了封装,使用Mouse
类和Keyboard
类可以让我们的开发效率事半功倍。
对Raw Input
有兴趣的同学,你可以看键鼠类的内部实现,也可以看MSDN文档: Raw Input
Mouse
类和Keyboard
类都在名称空间DirectX
内。
DirectX11 With Windows SDK完整目录
欢迎加入QQ群: 727623616 可以一起探讨DX11,以及有什么问题也可以在这里汇报。
鼠标输入
Mouse
类是一个单例类,我们可以通过Mouse::Get()
静态方法来获取该实例:
static Mouse& Mouse::Get();
也可以在应用程序类中直接作为成员使用,因为它提供了默认构造函数。不过为了异常安全,建议使用智能指针:
std::unique_ptr<DirectX::Mouse> m_pMouse;
m_pMouse = std::make_unique<DirectX::Mouse>();
要想使用鼠标类,我们还需要完成三件事情:
1.在初始化阶段给鼠标类设置要绑定的窗口句柄,使用Mouse::SetWindow
方法:
void Mouse::SetWindow(HWND window);
2.设置鼠标模式,需要使用Mouse::SetMode
方法:
void Mouse::SetMode(Mouse::Mode mode);
鼠标模式有下面两种:
enum Mode
{
MODE_ABSOLUTE = 0, // 绝对坐标模式,每次状态更新xy值为屏幕像素坐标,且鼠标可见
MODE_RELATIVE, // 相对运动模式,每次状态更新xy值为每一帧之间的像素位移量,且鼠标不可见
};
可以加在D3DApp::Init
方法上:
bool D3DApp::Init()
{
m_pMouse = std::make_unique<DirectX::Mouse>();
m_pKeyboard = std::make_unique<DirectX::Keyboard>();
if (!InitMainWindow())
return false;
if (!InitDirect3D())
return false;
return true;
}
3.在消息处理的回调函数上进行修改,在接收到下面这些消息后,需要调用Mouse::ProcessMessage
方法:
WM_ACTIVATEAPP WM_INPUT
WM_LBUTTONDOWN WM_MBUTTONDOWN WM_RBUTTONDOWN WM_XBUTTONDOWN
WM_LBUTTONUP WM_MBUTTONUP WM_RBUTTONUP WM_XBUTTONUP
WM_MOUSEWHEEL WM_MOUSEHOVER WM_MOUSEMOVE
具体的修改的部分在最后会展示。
完成这些操作后,我们的程序就可以开始使用鼠标了。
对于每一帧,我们可以通过Mouse::GetState
方法获取当前帧下鼠标的运动状态:
Mouse::State Mouse::GetState() const;
Mouse::State
包含如下成员:
struct State
{
bool leftButton; // 鼠标左键被按下
bool middleButton; // 鼠标滚轮键被按下
bool rightButton; // 鼠标右键被按下
bool xButton1; // 忽略
bool xButton2; // 忽略
int x; // 绝对坐标x或相对偏移量
int y; // 绝对坐标y或相对偏移量
int scrollWheelValue; // 滚轮滚动累积值
Mode positionMode; // 鼠标模式
};
对于剩下的方法:
Mouse::ResetScrollWheelValue
方法可以清空滚轮的滚动累积值
Mouse::IsConnected
方法则可以检验鼠标是否连接
Mouse::SetVisible
方法设置鼠标是否可见
Mouse::IsVisible
方法可以检验鼠标是否可见
鼠标状态追踪
Mouse::ButtonStateTracker
类提供了更高级的功能,通过根据上一次的鼠标事件和当前鼠标事件的对比,来判断鼠标的状态。它有两个重要方法:
void Mouse::ButtonStateTracker::Update( const Mouse::State& state );
// 在每一帧的时候应提供Mouse的当前状态去更新它
State Mouse::ButtonStateTracker::GetLastState() const;
// 获取上一帧的鼠标事件,应当在Update之前使用,否则变为获取当前帧的状态
然后还有5个重要公共成员:
ButtonState leftButton; // 鼠标左键状态
ButtonState middleButton; // 鼠标滚轮按键状态
ButtonState rightButton; // 鼠标右键状态
ButtonState xButton1; // 忽略
ButtonState xButton2; // 忽略
注意: 这里要区分
State
和ButtonState
类型的五个同名成员,含义不同。
枚举量ButtonState
含义:
enum ButtonState
{
UP = 0, // 按钮未被按下
HELD = 1, // 按钮长按中
RELEASED = 2, // 按钮刚被放开
PRESSED = 3, // 按钮刚被按下
};
由于鼠标状态追踪类不是单例,而且它存有上一帧的鼠标状态,不应该作为一个临时变量,而是也应该像鼠标类一样在整个程序生命周期内都存在。
在绝对模式下,我们也可以获取两帧之间的鼠标相对位移量:
Mouse::State mouseState = m_pMouse->GetState();
Mouse::State lastMouseState = mMouseTracker.GetLastState();
int dx = mouseState.x - lastMouseState.x, dy = mouseState.y - lastMouseState.y;
虽然这样子第一帧的时候,鼠标状态追踪类并没有开始记录,而鼠标已经记录下了第一帧的状态,但由于开始运行的第一帧通常都很快,等到我们第一次使用鼠标的时候已经经过了一段时间,所以并不会产生什么问题。
然后在更新了跟踪器状态后,就可以判断鼠标状态做进一步操作了。以渲染立方体的项目为例,这里打算通过鼠标拖动产生旋转,如:
// 更新鼠标按钮状态跟踪器,仅当鼠标按住的情况下才进行移动
mMouseTracker.Update(state);
if (mouseState.leftButton == true && mMouseTracker.leftButton == mMouseTracker.HELD)
{
// 旋转立方体
cubeTheta -= (mouseState.x - lastMouseState.x) * 0.01f;
cubePhi -= (mouseState.y - lastMouseState.y) * 0.01f;
}
mCBuffer.world = XMMatrixRotationY(cubeTheta) * XMMatrixRotationX(cubePhi);
键盘输入
Keyboard
类也是一个单例类,我们可以通过Keyboard::Get()
静态方法来获取该实例:
static Keyboard& Keyboard::Get();
也可以在应用程序类中直接作为成员使用,因为它提供了默认构造函数。不过为了异常安全,建议使用智能指针:
std::unique_ptr<DirectX::Keyboard> m_pKeyboard;
m_pKeyboard = std::make_unique<DirectX::Keyboard>();
要想使用键盘类,我们还需要完成一件或两件事情:
1.如果Keyboard
类内有SetWindow
方法,则需要调用以初始化,否则不需要:
void Keyboard::SetWindow(ABI::Windows::UI::Core::ICoreWindow* window);
2.在消息处理的回调函数上进行修改,在接收到下面这些消息后,需要调用Keyboard::ProcessMessage
方法:
WM_ACTIVATEAPP
WM_KEYDOWN WM_SYSKEYDOWN WM_KEYUP WM_SYSKEYUP
具体的修改的部分在最后会展示。
完成上述操作我们就可以使用键盘了。
对于每一帧,我们可以通过Keyboard::GetState
方法获取当前帧下键盘所有按键的状态:
Keyboard::State Keyboard::GetState() const;
Keyboard::State
结构体记录了按键信息,而Keyboard::Keys
枚举量定义了有哪些按键。获取了键盘按键状态后,我们要关注的是Keyboard::State
内的方法:
Keyboard::State::IsKeyDown
方法判断按键是否被按下
bool Keyboard::State::IsKeyDown(Keyboard::Keys key) const;
Keyboard::State::IsKeyUp
方法判断按键是否没有按下
bool Keyboard::State::IsKeyUp(Keyboard::Keys key) const;
下面演示的是键盘连续操作,其中dt是两帧之间的时间间隔
if (keyState.IsKeyDown(Keyboard::W))
cubePhi += dt * 2;
if (keyState.IsKeyDown(Keyboard::S))
cubePhi -= dt * 2;
if (keyState.IsKeyDown(Keyboard::A))
cubeTheta += dt * 2;
if (keyState.IsKeyDown(Keyboard::D))
cubeTheta -= dt * 2;
mCBuffer.world = XMMatrixRotationY(cubeTheta) * XMMatrixRotationX(cubePhi);
注意:对于鼠标和键盘的拖动距离,推荐鼠标用偏移量,而推荐键盘用按压持续时间
键盘状态追踪
如果要判断按键是刚按下还是刚放开,则需要Keyboard::KeyboardStateTracker
类帮助
Keyboard::KeyboardStateTracker::Update
方法需要接受当前帧的State以进行更新:
void Keyboard::KeyboardStateTracker::Update(const Keyboard::State& state);
然后就可以使用它的IsKeyPressed
或IsKeyReleased
方法来进行判断键盘按键是否刚按下,或者刚释放了,同样需要接受Keyboard::Keys
枚举量
于是我们可以尝试让前面的立方体通过键盘或鼠标动起来。
D3DApp::MsgProc方法的变化
这里展示了消息处理部分的变化:
LRESULT D3DApp::MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
// 省略原有的部分...
// 监测这些键盘/鼠标事件
case WM_INPUT:
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_XBUTTONDOWN:
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
case WM_XBUTTONUP:
case WM_MOUSEWHEEL:
case WM_MOUSEHOVER:
case WM_MOUSEMOVE:
m_pMouse->ProcessMessage(msg, wParam, lParam);
return 0;
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
case WM_KEYUP:
case WM_SYSKEYUP:
m_pKeyboard->ProcessMessage(msg, wParam, lParam);
return 0;
case WM_ACTIVATEAPP:
m_pMouse->ProcessMessage(msg, wParam, lParam);
m_pKeyboard->ProcessMessage(msg, wParam, lParam);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
补充说明:当你打开Mouse.cpp
和Keyboard.cpp
查看源码的时候,大概率会遇到下面的报错:
你可以直接无视该错误继续编译,即便到VS2019这个情况依然存在。
现在来看看当前章节对应的项目。该程序使用键盘的WSAD四个键控制立方体旋转,或者鼠标拖动旋转。
DirectX11 With Windows SDK完整目录
欢迎加入QQ群: 727623616 可以一起探讨DX11,以及有什么问题也可以在这里汇报。
DX11 Without DirectX SDK--05 键盘和鼠标输入的更多相关文章
- DirectX11 With Windows SDK--05 键盘和鼠标输入
前言 提供键鼠输入可以说是一个游戏的必备要素.在这里,我们不使用DirectInput,而是使用Windows的消息处理机制,不过要从头开始实现会让事情变得很复杂.DXTK提供了鼠标输入的Mouse. ...
- unity3d的键盘和鼠标输入
一.键盘的输入 •GetKey,GetKeyDown,GetKeyUp三个方法分别获取用户键盘按键的输入 1. GetKey:用户长按按键有效: bool down = Input.GetKeyDow ...
- TForm.ShowModal只是接管消息循环,禁止外部键盘和鼠标输入到别的窗口,但并不封锁其它窗口继续获取消息(比如WM_TIMER消息仍可被发送到别的窗口上)
窗体上放一个TTimer,然后双击输入: procedure TForm1.Timer1Timer(Sender: TObject); var cvs: TCanvas; Rect: TRect; S ...
- DX11 Without DirectX SDK--使用Windows SDK来进行开发
在看龙书(Introduction to 3D Game Programming with Directx 11)的时候,里面所使用的开发工具包为Microsoft DirectX SDK(June ...
- DX11 Without DirectX SDK--04 使用DirectX Tool Kit帮助开发
回到 DirectX11--使用Windows SDK来进行开发 DirectX Tool Kit下载 DirectX Tool Kit是一个包含许多类的集合,用于为公共Windows平台编写Dire ...
- C#使用 DirectX SDK 9做视频播放器 并在视频画线添加文字 VMR9
视频图像处理系列 索引 VS2013下测试通过. 在百度中搜索关键字“DirectX SDk”,或者进入微软官网https://www.microsoft.com/en-us/download/det ...
- DirectX SDK版本与Visual Studio版本
对于刚刚接触 DirectShow 的人来说,安装配置是一个令人头疼的问题,经常出现的情况是最基本的 baseclass 就无法编译.一开始我也为此费了很大的功夫,比如说修改代码.修改编译选项使其编译 ...
- DX11 Without DirectX SDK--06 DirectXMath数学库
回到 DirectX11--使用Windows SDK来进行开发 xnamath.h原本是位于DirectX SDK的一个数学库,但是现在Windows SDK包含的数学库已经抛弃掉原来的xnamat ...
- DX11 Without DirectX SDK--01 DirectX11初始化
回到 DirectX11--使用Windows SDK来进行开发 由于个人觉得龙书里面第4章提供的Direct3D 初始化项目封装得比较好,而且DirectX SDK Samples里面的初始化程序过 ...
随机推荐
- tomcat集群实现源码级别剖析
随着互联网快速发展,各种各样供外部访问的系统越来越多且访问量越来越大,以前Web容器可以包揽接收-逻辑处理-响应整个请求生命周期的工作,现在为了构建让更多用户访问更强大的系统,人们通过不断地业务解耦. ...
- Android View底层到底是怎么绘制的
Android绘制链图: 网上很多讲Android view的绘制流程往往只讲到了Measure - Layout - Draw. 但是,这只是一个大体的流程,而我们需要探讨的是Android在我们 ...
- [加密]C#实现维吉尼亚加密与解密(解密前提为已知密匙)
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...
- mysql进阶(八)怎么对varchar类型排序问题
MySQL中怎么对varchar类型排序问题 asc 升级 desc降序 在mysql默认order by 只对数字与日期类型可以排序,但对于varchar字符型类型排序好像没有用了,下面我来给各位同 ...
- 树莓派linux驱动学习之LED控制
前面我们编写了hello world的程序,接下来继续研究GPIO功能,通过GPIO来控制LED的亮灭,这在单片机中应该算是十分简单的一个程序了,但是在Linux系统中控制GPIO没有那么简单,难点就 ...
- DBUtils源码分析
其实,在这篇文章里,我只是分析了dbutis的query的运作流程. 至于类为什么要这样设计,蕴含的设计模式等等高级知识点咱们在下节再探讨. 先看看最简单的DBUtils是如何工作的. 数据库里有一张 ...
- 报表软件公司悬赏 BUG,100块钱1个的真实用意
上一篇文章我讲到,报表软件FineReport一反常态,做了个<提BUG,拿现金>的活动,1个BUG,100块钱.纵览软件行业,如金蝶用友浪潮IBM微软等国内外巨头,均没有这样的举动去征集 ...
- 安卓打开File Explorer里面不显示内容
其实这不算是开发问题,是自己对这个文件的内容不了解罢了.这个文件的内容与手机是直接相关系的.只有打开模拟器才可以查看里面的内容. 打开模拟器,里面的文件一览无余.因为自己遇到了问题,还有些惊慌,但真实 ...
- linux内核算法---hex_to_bin分享
这是我从内核抠出来的一段代码,用处就是传入一个字符,即可以用printf语句%d以十进制数的格式输出,同时也可以以%p地址的形式输出. 代码如下: #include <stdio.h> # ...
- 菜鸟玩云计算之十八:Hadoop 2.5.0 HA 集群安装第1章
菜鸟玩云计算之十八:Hadoop 2.5.0 HA 集群安装第1章 cheungmine, 2014-10-25 0 引言 在生产环境上安装Hadoop高可用集群一直是一个需要极度耐心和体力的细致工作 ...