WebVR开发教程——玩转Gamepad手柄
现在大部分VR搭配gamepad手柄,用户通过手持手柄可以与虚拟场景进行交互。
就如headset头盔一样,gamepad手柄也有3-DoF和6-DoF的两种类型:
- 3-DoF如daydream controller,只支持方向追踪,于是google推荐采用laser激光笔进行交互。
- 6-DoF如Oculus touch,可以进行方向和位置追踪,因此可以很好的模拟手臂的动作。
相比headset传感器输入产生的交互,gamepad还多了各种输入元件,如按钮、touchpad触控板或thumbstick手摇杆等。
于是,根据手柄输入硬件又可将gamepad事件分为三类:
- A. 传感器事件:由传感器对手柄进行物理追踪,如激光笔交互;
- B. 按钮事件:通过点击按钮产生的交互行为;
- C. 控制单元事件:由thumbstick, touchpad输入产生,如swipe滑动来翻页等。
Gamepad API
Gamepad API是一个HTML5接口,让开发者可以通过js访问游戏手柄,使用Gamepad API的第一步是获取gamepad实例。
一个典型的gamepad一般都会有button按钮和axes control控制单元,而VR gamepad则是在前两者的基础上,加上对传感器的支持。
属性 | 说明 |
---|---|
id | string类型,包含手柄的标识信息。 |
connected | bool类型,反映手柄是否处于连接状态 |
buttons | 返回GampadButton 对象数组,即手柄上的所有可用按钮 |
axes | 返回double类型数组,数组元素为手柄控制元件上各轴向数值 |
pose | 返回一个GamepadPose 对象,包含手柄的方向和位置信息 |
获取headset实例需要调用navigator.getVRDisplays()
方法,同样,获取一个手柄的实例,则是调用navigator.getGamepads()
方法,它返回一个gamepads
数组。
一旦有手柄连接上,gamepads
数组将产生有效的gamepad对象,否则,只能是null。
function getGamepad(id)
const gamepads = navigator.getGamepads();
for (let i = 0; i < gamepads.length; ++i) {
let gamepad = gamepads[i];
// 只有gamepad不为null才有效
if (gamepad && gamepad.id === id) return gamepad;
}
}
// 或者写成这样: let getGamepad = id => navigator.getGamepads().filter( gamepad => gamepad && gamepad.id === id )[0];
this.gamepad = getGamepad('daydream vr controller'); // 获取daydream controller手柄
上面实现的是根据手柄id获取单个gamepad实例的方法,有些VR手柄如Vive Controller, Oculus Touch等是双手柄,则需要获取两个gamepad实例。
接下来,我将针对gamepad实例的buttons
, axes
, pose
三个重要属性进行介绍,它们对应的是手柄按钮、控制元件、传感器三类组件,是实现gamepad交互事件的三大法宝。
Gamepad.buttons
作为gamepad
实例的一个重要属性,代表手柄或遥控器上的所有可用按钮,返回的是由一个或多个GamepadButton
对象组成的数组。
GamepadButton
顾名思义指的是gamepad上的按钮实例,我们可以该实例获取按钮的状态,比如是否被点击。
属性 | 类型 | 说明 |
---|---|---|
id | string类型 | 按钮的id名 |
pressed | bool类型 | 按钮是否处于按压状态。 |
touched | bool类型 | 按钮是否处于触摸状态。 |
value | double类型 | 反映按钮被按压的程度 |
由于gamepad的构造都不尽相同,如果想识别Gamepad.buttons
中确认键或者返回键对象,可以通过GamepadButton.id
的值来判断。
下面是利用pressed
实现tap事件的代码,这里定义的tap事件,是指手指按下按钮瞬间产生的触发事件,不按压或持续按压过程不会产生tap。
update() {
const button = this.gamepad.buttons[0]; // 确认键对象通常位于数组第一个
if (!this._lastPressed && button.pressed) {
// 处理tap事件
}
this._lastPressed = button.pressed;
}
用代码的语言来说,就是只有满足:1) 上一帧的button.pressed
为false
; 2) 当前帧的button.pressed
为true
的才会触发tap事件。
于是,我们需要定义一个_lastPressed
来记录上一帧button是否pressed。
使用gamepad.buttons
可以轻松实现gamepad按钮的点击事件,接下来,将介绍另一个重要属性gampad.axes
,通过它我们可以判断触控板手势、摇杆朝向等。
Gamepad.axes
返回的是gamepad控制元件的轴数据集,如手柄上的手摇杆Thumbstick
、遥控器上的触控板Touchpad
都是具有双轴向的元件。
当用户用手指推进摇杆或者轻触触控板时,都可以用一个二维笛卡尔坐标[x,y]
来表示当前摇杆或触控板被触发的方位,如下图,返回一个-1.0~1.0的double数值组,一般将按水平、竖直的顺序排序,如axes[0]表示x轴位置、axes[1]表示y轴位置。
update() {
const axes = this.gamepad.axes; // 获取轴向数组
const x = axes[0], y = axes[1],
dx = x - this._lastAxes[0], dy = y - this._lastAxes[1];
// 控制画廊位移
gallery.position.x += dx;
gallery.position.y += dy;
this._lastAxes = axes;
}
上面通过计算两帧之间摇杆在x轴和y轴的位移,控制画廊的显示位置,当摇杆向左推时,画廊也向左移动。
GamepadPosegamepad.pose
属性返回的GamepadPose
对象,与头显的VRPose
对象类似,GamepadPose
访问的是VR手柄的传感器(加速计和陀螺仪),可以直接获取gamepad的方向、位置、速度和加速度等信息。
属性 | 类型 | 说明 |
---|---|---|
hasPosition | bool | gamepad是否具有position属性。 |
hasOrientation | bool | gamepad是否具有orientation属性。 |
position | Float32Array | 返回gamepad的位置矩阵 |
orientation | Float32Array | 返回gamepad的方向矩阵 |
angularAcceleration | Float32Array | 返回x, y, z轴每秒的角加速度 |
angularVelocity | Float32Array | 返回x, y, z轴每秒的角速度 |
linearAcceleration | Float32Array | 返回x, y, z轴每秒的线性加速度 |
linearVelocity | Float32Array | 返回x, y, z轴的线性速度 |
hasPosition与hasOrientation
只有3-DoF的gamepad如Gear VR和Daydream的Controller只包含orientation
方向矩阵,因此hasOrientation
为true
而hasPosition
为false
;
而6-DoF的gamepad如Oculus touch和HTC Vive Controller由于orientation
和position
兼具,因此hasOrientation
和hasPosition
都为true
。### position与orientation
GamepadPose最重要的属性,通过这两个属性可以将现实的手柄映射到VR三维世界中,比如当用户使用手柄玩射击游戏时,就需要获取每一帧gamepad的oritentation,并赋值给3d场景里的模型。
update() {
const { orientation, position } = this.gamepad.pose;
controller.quaternion.fromArray( orientation ); // 将方向矩阵赋值给遥控器模型
controller.position.fromArray( position ); // 将位置矩阵赋值给遥控器模型
}
Acceleration与Velocity
GamepadPose还提供了一系列运动属性:角加速度、角速度、线性速度、线性加速度,我们可以根据这些属性进行更丰富的物理行为,比如使用加速度×质量来计算物体受力情况,适用于诸如击剑、击球等复杂运动形式,这里就不展开细说了。
共同学习,写下你的评论
评论加载中...
作者其他优质文章