我们知道,电脑一般是以键盘和鼠标作为主要的输入设备;而手机则是主要依靠触摸屏以及内置的重力加速度传感器/陀螺仪;游戏主机设备则一般是游戏手柄输入。
虽然外形和使用方法差别明显,但是对于电脑系统来说,这些都是外设,而且是低速外设。这些设备多通过串行总线(比如USB)与计算机系统相连。
由于操作系统的一个重要职能就是管理硬件设备,而且设备可能会被多个程序所共享,因此在我们一般不需要直接与这些硬件设备的驱动直接打交道,而是通过操作系统所提供的接口进行访问。这也意味着,在不同的系统上我们会遇到不同的接口,即使是同一个类型/型号的硬件设备。
除了本世纪兴起的体感类游戏之外,传统的游戏一般以开关量输入和模拟量输入为主要控制输入信息。开关量就是指诸如键盘按键、鼠标按键、手柄按键那种,只有按下和非按下(弹起)这两种状态的控制量;而模拟量输入则是指诸如手柄摇杆或者近些年手柄两肩上的板机那样,在一个范围之内可以连续改变其角度从而产生一个连续变化的输入的控制量。
我们首先来使用键盘当中的光标方向键来模拟开关量,控制模型的转动,像下面这样:

上传视频封面
首先我们需要在Framework/Common当中新建一个Runtime Module: InputManager,其中包含平台无关的四个方向键的按下和恢复(弹起)响应函数:
然后我们需要将各个平台的按键消息与其挂钩:
Mac OS
我们需要将Cocoa当中的按键消息(NSEventTypeKeyDown, NSEventTypeKeyUp)与InputManager进行挂钩,大致是下面这么一个样子:
Windows平台
我们需要将Windows消息当中的(WM_KEYDOWN, WM_KEYUP)消息与InputManager的方法进行挂钩,大约是下面这个样子:
Linux
我们需要将XCB Event当中的(XCB_KEY_PRESS, XCB_KEY_RELEASE)与InputManager的方法进行挂钩,大致是下面这个样子:
这里需要注意的是XCB返回的是键盘的扫描码。这个扫描码实际上是指的按键的物理位置(电气位置),在不同类型的键盘上是不一样的。我这里简便起见直接使用了扫描码,但是这其实是不对的(没有通用性)。实际上还应该需要根据系统提供的键盘配置文件将扫描码转换成为按键名称之后才对。
映射完成之后,我们需要在InputManager.cpp当中添加这几个按键响应对于场景的操作逻辑。这部分其实才是日常的游戏开发当中所说的游戏程序(逻辑)的重要一部分。也就是说,我们目前到此为止所写的代码多属于游戏引擎的开发,而游戏内容的开发(使用游戏引擎开发游戏)的编程工作则主要是编写用户输入是如何影响游戏场景(当然也包括AI输入等)。
因此,这部分应该是可以由引擎使用者改写的。在软件行业这叫“支持二次开发”。这又是一个很深的坑,因为我们需要封装我们的引擎接口,选择需要支持的二次开发语言(比如Lua、JavaScript、Python、C#或者是C++),并提供一整套相关的工具。这些我们在后面会通过许多篇文章来进行。这里我们首先采用Hard Coding的方法来快速测试。
首先我们在Framework/Common/GraphicsManager.hpp当中删除按照时间自动旋转模型的代码,并暴露两个新的方法:
第一个方法表示将模型按照X轴进行旋转,第二个方法表示将模型按照Y轴进行旋转。参数radians表示旋转的弧度。我们的引擎采用右手坐标系,所以radians为正表示逆时针旋转(当👀朝着坐标轴-方向看的时候),而为负表示顺时针旋转。
然后我们在InputManager.cpp当中,根据不同的按键调用这两个方法就可以轻松实现如上面视频所示的用按键对模型旋转的控制了。比如下面这样:
关于演示用模型
演示用模型来自参考引用*1,许可证类型为个人使用(非商业用途)。一些贴图为TGA格式,所以我根据参考引用*2在Framework/Parser之下新增了TGA文件格式的解析器。TGA格式很简单,所以就不另外写单独的特别篇来阐述解析器的编写了,有兴趣的请直接看本篇对应的代码。
参考引用:
