如今大部分VR搭配gamepad手柄,用戶經過手持手柄能夠與虛擬場景進行交互。 就如headset頭盔同樣,gamepad手柄也有3-DoF和6-DoF的兩種類型:javascript
相比headset傳感器輸入產生的交互,gamepad還多了各類輸入元件,如按鈕、touchpad觸控板或thumbstick手搖桿等。 因而,根據手柄輸入硬件又可將gamepad事件分爲三類:java
Gamepad API是一個HTML5接口,讓開發者能夠經過js訪問遊戲手柄,使用Gamepad API的第一步是獲取gamepad實例。數組
一個典型的gamepad通常都會有button按鈕和axes control控制單元,而VR gamepad則是在前二者的基礎上,加上對傳感器的支持。this
屬性 | 說明 |
---|---|
id | string類型,包含手柄的標識信息。 |
connected | bool類型,反映手柄是否處於鏈接狀態 |
buttons | 返回GampadButton 對象數組,即手柄上的全部可用按鈕 |
axes | 返回double類型數組,數組元素爲手柄控制元件上各軸向數值 |
pose | 返回一個GamepadPose 對象,包含手柄的方向和位置信息 |
獲取headset實例須要調用navigator.getVRDisplays()
方法,一樣,獲取一個手柄的實例,則是調用navigator.getGamepads()
方法,它返回一個gamepads
數組。 一旦有手柄鏈接上,gamepads
數組將產生有效的gamepad對象,不然,只能是null。google
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實例。3d
接下來,我將針對gamepad實例的buttons
, axes
, pose
三個重要屬性進行介紹,它們對應的是手柄按鈕、控制元件、傳感器三類組件,是實現gamepad交互事件的三大法寶。code
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軸的位移,控制畫廊的顯示位置,當搖桿向左推時,畫廊也向左移動。
gamepad.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軸的線性速度 |
只有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 ); // 將位置矩陣賦值給遙控器模型 }
GamepadPose還提供了一系列運動屬性:角加速度、角速度、線性速度、線性加速度,咱們能夠根據這些屬性進行更豐富的物理行爲,好比使用加速度×質量來計算物體受力狀況,適用於諸如擊劍、擊球等複雜運動形式,這裏就不展開細說了。