WebVR開發教程——交互事件(二)使用Gamepad API
前面我們從頭顯和手柄兩個層面對VR交互事件進行介紹,前者使用的是WebVR API,而後者則需用到Gamepad API,本期將對Gamepad API展開介紹。
Gamepad API
Gamepad API是一個HTML5接口,讓開發者可以通過js訪問遊戲手柄,使用Gamepad API的第一步是獲取gamepad實例。

一個典型的gamepad一般都會有button按鈕和axes control控制單元,而VR gamepad則是在前兩者的基礎上,加上對傳感器的支持。
Gamepad

獲取headset實例需要調用navigator.getVRDisplays()
方法,同樣,獲取一個手柄的實例,則是調用navigator.getGamepads()
方法,它返回一個gamepads
數組。
一旦有手柄連接上, gamepads
數組將產生有效的gamepad對象,否則,只能是null。
functiongetGamepad(id)constgamepads=navigator.getGamepads();for(leti=0;i<gamepads.length;++i){letgamepad=gamepads[i];//只有gamepad不為null才有效if(gamepad&&gamepad.id===id)returngamepad;}}//或者寫成這樣: 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.buttons作為gamepad實例的一個重要屬性,代表手柄或遙控器上的所有可用按鈕,返回的是由一個或多個GamepadButton對象組成的數組。

GamepadButton顧名思義指的是gamepad上的按鈕實例,我們可以該實例獲取按鈕的狀態,比如是否被點擊。

由於gamepad的構造都不盡相同,如果想識別Gamepad.buttons
中確認鍵或者返回鍵對象,可以通過GamepadButton.id
的值來判斷。
下面是利用pressed
實現tap事件的代碼,這裡定義的tap事件,是指手指按下按鈕瞬間產生的觸發事件,不按壓或持續按壓過程不會產生tap。
update() {constbutton=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.axes
返回的是gamepad控制元件的軸數據集,如手柄上的手搖桿Thumbstick
、遙控器上的觸控板Touchpad
都是具有雙軸向的元件。
當用戶用手指推進搖桿或者輕觸觸控板時,都可以用一個二維笛卡爾坐標[x,y]
來表示當前搖桿或觸控板被觸發的方位,如下圖,返回一個-1.0 ~1.0的double數值組,一般將按水平、豎直的順序排序,如axes[0]表示x軸位置、axes[1]表示y軸位置。

update() {constaxes=this.gamepad.axes;//獲取軸向數組constx=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軸的位移,控制畫廊的顯示位置,當搖桿向左推時,畫廊也向左移動。
GamepadPose
gamepad.pose
屬性返回的GamepadPose
對象,與頭顯的VRPose
對像類似, GamepadPose
訪問的是VR手柄的傳感器(加速計和陀螺儀),可以直接獲取gamepad的方向、位置、速度和加速度等訊息。

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還提供了一系列運動屬性:角加速度、角速度、線性速度、線性加速度,我們可以根據這些屬性進行更豐富的物理行為,比如使用加速度×質量來計算物體受力情況,適用於諸如砍殺、擊球等複雜運動形式,這裡就不展開細說了。
小結
至此,WebVR事件開發基礎已經講完,接下來,我將對各主流VR類型進行針對性實現,根據交互的複雜性,將按照Cardboard→Gear VR→Daydream→Oculus Rift 由屌絲到高富帥的路線。

WebVR開發傳送門:
WebVR開發教程——交互事件(三)Cardboard與註視
WebVR開發教程——深度剖析關於WebVR的開發調試方案以及原理機制
WebVR開發教程——標准入門使用Three.js開發WebVR場景的入門教程