一、概述
筆者之前使用SteamV1.x+VRTK開發(fā)項(xiàng)目,發(fā)現(xiàn)SteamVR2.0與之前有重大更新,主要原因是隨著越來越多的VR設(shè)備推出,控制器類型逐漸趨向于碎片化,
每當(dāng)有新的控制器發(fā)布,都會(huì)給開發(fā)者帶來一些額外的工作量:游戲項(xiàng)目需要修改交互代碼以適配新的設(shè)備;意思就是每套VR硬件設(shè)備就有自身一套VR SDK,
所以,當(dāng)現(xiàn)有VR應(yīng)用程序移植到另外一個(gè)VR平臺(tái)的時(shí)候,需要針對(duì)目標(biāo)平臺(tái)進(jìn)行交互適配;從而增加開發(fā)者的工作量;SteamVR2.0的推出意味著能夠使開發(fā)者
在編程中專注于用戶的動(dòng)作,而不是具體的控制器按鍵。(具體適配如何留待日后驗(yàn)證,目前只做整理)
二、重要更新:Input System
SteamVR 2.0 的重要更新是加入了 Input System。推出Input System的目的,是為了更加符合OpenXR標(biāo)準(zhǔn),以及配合即將正式推出的Knuckles控制器。
目前多數(shù)主流VR平臺(tái)均加入了OpenXR開放標(biāo)準(zhǔn),如下圖所示:

Input System 與之前處理用戶輸入有顯著的不同,使用 SteamVR Input System,開發(fā)人員可以在應(yīng)用程序之外定義默認(rèn)的動(dòng)作并與按鍵進(jìn)行綁定,
而不需要將輸入視為某一特定設(shè)備的特定按鍵。這樣新的設(shè)備可以快速適配應(yīng)用程序,無需更改代碼。比如,當(dāng)開發(fā)者檢測(cè)玩家是否抓取某個(gè)物體的時(shí)候,
不是檢測(cè)Vive控制器的Trigger鍵或Oculus Touch控制器的Grip鍵是否被按下,而是檢測(cè)預(yù)定義的"Grab"動(dòng)作是否為True即可。作為開發(fā)者,可以在
SteamVR中為Grab動(dòng)作設(shè)置默認(rèn)按鍵和閾值,當(dāng)程序運(yùn)行時(shí),也可修改這些數(shù)值以滿足玩家的個(gè)人偏好?;谶@種機(jī)制,不光能夠解決控制器碎片化的問題,
也可以快速適配未來發(fā)布的設(shè)備。
三、動(dòng)作(Actions)
Input System 的核心概念是動(dòng)作(Action),基于動(dòng)作的輸入系統(tǒng)對(duì)于游戲引擎來說更有意義, Unreal一直在沿用這種方案,而Unity目前在開發(fā)中的輸入
系統(tǒng)也將遵循這一原則。開發(fā)者需要放棄之前關(guān)于“按下某個(gè)按鍵發(fā)生什么事情”的思想,取而代之的是使用“做出某個(gè)動(dòng)作發(fā)生什么事情”的思想。
SteamVR 2.0將動(dòng)作抽象為以下6種類型,簡介如下:
Boolean類型的動(dòng)作代表只有兩種狀態(tài)的動(dòng)作——True或False,比如抓?。℅rab)動(dòng)作,只有抓取或未抓取兩種狀態(tài),不存在中間狀態(tài)。在Unity中對(duì)應(yīng)類為
SteamVR_Action_Boolean。
Single類型的動(dòng)作能夠返回0~1之間的數(shù)值,比如 Trigger 鍵按下到松開的過程。在Unity中對(duì)應(yīng)類為SteamVR_Action_Single。
Vector2類型動(dòng)作能夠返回二維數(shù),比如Touchpad上的觸摸或手柄搖桿。使用這樣的數(shù)值能夠控制物體在四個(gè)方向的運(yùn)動(dòng),典型的應(yīng)用時(shí)使用Touchpad控制無
人機(jī)或小車的運(yùn)動(dòng)。在Unity中對(duì)應(yīng)類為SteamVR_Action_Vector2。
Vector3類型的動(dòng)作能夠返回三維數(shù)值,在Unity中對(duì)應(yīng)類為SteamVR_Action_Vector3。
Pose類型的動(dòng)作表示三維空間中的位置和旋轉(zhuǎn),一般用于跟蹤VR控制器。在Unity中對(duì)應(yīng)類為SteamVR_Action_Pose。
Skeleton類型的動(dòng)作能夠獲取用戶在持握手柄控制器時(shí)的手指關(guān)節(jié)數(shù)據(jù),通過返回?cái)?shù)據(jù),結(jié)合手部渲染模型,能夠更加真實(shí)的呈現(xiàn)手部在虛擬世界的姿態(tài),雖
然不及像LeapMotion等設(shè)備獲取手指輸入那樣精確,但是足以獲得良好的沉浸感。在Unity中對(duì)應(yīng)類為SteamVR_Action_Skeleton。
3.1 骨骼輸入
對(duì)于骨骼輸入,Knuckles控制器為SteamVR體驗(yàn)帶來了手指跟蹤功能,能夠估算用戶手指的位置,然后將數(shù)據(jù)傳遞給驅(qū)動(dòng)程序,驅(qū)動(dòng)程序?qū)⑵鋵?duì)應(yīng)解析到手部模
型的31塊骨骼上,從而給用戶帶來更好的沉浸式體驗(yàn)。該功能并不是Knuckles所獨(dú)有,SteamVR還能夠?yàn)镠TC Vive和Oculus Touch這樣的設(shè)備提供手指狀態(tài)
估算,比如判斷手部是否打開,手指是否放置在Touchpad上。同時(shí)Valve公司還將與Microsoft展開合作,以增加對(duì)Windows MR控制器的支持。
默認(rèn)情況下,運(yùn)行Interaction System示例場(chǎng)景中能夠同時(shí)看到手部和控制器模型,此外,還有一個(gè)輔助組件SteamVR_Behaviour_Skeleton,如下圖所示。
有關(guān)該組件的使用,可以參考Interaction System的示例場(chǎng)景。

3.2 振動(dòng)輸出
以上是介紹的都是輸入動(dòng)作,另外,目前還有一種輸出動(dòng)作——振動(dòng),用于觸發(fā)VR控制器上的觸覺反饋,調(diào)用方法如下代碼所示:
SteamVR_Input._default.outActions.Haptic.Execute(float secondsFromNow, float durationSeconds, float frequency, float amplitude, SteamVR_Input_Sources inputSource);
四. 動(dòng)作集(Action Sets)
動(dòng)作通過動(dòng)作集進(jìn)行邏輯上的分組,以方便進(jìn)行組織和管理。在Unity中對(duì)應(yīng)的類為SteamVR_ActionSet。在不同的場(chǎng)景或應(yīng)用程序之間可以切換使用不同的動(dòng)作集,
比如,應(yīng)用程序中有一個(gè)場(chǎng)景是在地球上拾取并投擲物體,而另一個(gè)場(chǎng)景則是在太空中飛行,那么這兩個(gè)場(chǎng)景可以使用不同的動(dòng)作集。同時(shí),當(dāng)針對(duì)新設(shè)備進(jìn)行
交互適配時(shí),開發(fā)者只需對(duì)動(dòng)作進(jìn)行配置,而不必修改項(xiàng)目代碼。比如,使用 Vive 控制器時(shí),定義了一個(gè)Fire動(dòng)作,當(dāng)需要支持 Rift Touch 時(shí),只需通過
配置Touch控制器上符合 Fire 動(dòng)作的鍵值即可。
SteamVR插件默認(rèn)包含了三套動(dòng)作集default、platformer、buggy,開發(fā)者也可以在SteamVR Input窗口中自行添加或刪除動(dòng)作集。
使用組件SteamVR_ActivateActionSetOnLoad可以在場(chǎng)景中自動(dòng)激活和停用指定的動(dòng)作集。對(duì)應(yīng)激活和停用的方法是在Start()和OnDestroy()中實(shí)現(xiàn)。
如下圖所示:

五、 SteamVR Input 窗口
在Unity編輯器中,使用 Window > SteamVR Input 命令,打開SteamVR Input 窗口。在SteamVR 2.0中,使用SteamVR Input窗口作為入口,
對(duì)所有動(dòng)作進(jìn)行管理。初次導(dǎo)入SteamVR 2.0并運(yùn)行程序時(shí),會(huì)彈出一個(gè)對(duì)話框,提示沒有actions.json文件,并詢問是否要使用默認(rèn)值。如下圖所示:

選擇Yes,會(huì)將默認(rèn)的actions.json文件以及一些常見的控制器相關(guān)綁定文件復(fù)制到當(dāng)前項(xiàng)目的根目錄下,如下圖所示:

這些文件將在程序運(yùn)行時(shí)被載入進(jìn)來,并在程序最終構(gòu)建時(shí)被復(fù)制到與可執(zhí)行文件同級(jí)的目錄下。復(fù)制完成后,SteamVR Input窗口將讀取文件信息并展示其包
含的動(dòng)作集合以及動(dòng)作集合下的所有動(dòng)作。如下圖所示:

在Actions欄的右下角,可以點(diǎn)擊加減號(hào)按鈕添加或刪除動(dòng)作。每個(gè)動(dòng)作具有名稱(Name)、類型(Type)、本地化字符串(localization strings)等字段。
其中,類型對(duì)應(yīng)上節(jié)介紹的動(dòng)作類型;本地化字符串是面向用戶進(jìn)行綁定的動(dòng)作名稱,開發(fā)者可以通過SteamVR API直接訪問動(dòng)作以及動(dòng)作集。
當(dāng)點(diǎn)擊Save and Generate按鈕后,插件將為動(dòng)作以及動(dòng)作集生成可編程訪問的對(duì)象類,將它們放置在項(xiàng)目的SteamVR_input目錄下,如下圖所示:

這些對(duì)象可以在相關(guān)組件的下拉列表中進(jìn)行選擇,如下圖所示:

在項(xiàng)目代碼中,使用SteamVR_Input類可以靜態(tài)引用每個(gè)動(dòng)作和動(dòng)作集,在每個(gè)動(dòng)作集中,可以找到其包含的動(dòng)作的引用,如下代碼所示:
void Update()
{
if(SteamVR_Input._default.inActions.Teleport.GetStateUp(SteamVR_Input_Sources.Any)) {
Teleport();
}
}
以上代碼實(shí)現(xiàn)的功能為:當(dāng)檢測(cè)到任意控制器發(fā)出default動(dòng)作集中包含的Teleport動(dòng)作時(shí),執(zhí)行Teleport()函數(shù)。
六、測(cè)試動(dòng)作
選擇 Window > SteamVR Input Live View 命令,即可打開一個(gè)測(cè)試輸入窗口。運(yùn)行程序,此時(shí)該窗口將實(shí)時(shí)展示所有動(dòng)作集合的狀態(tài)。如下圖所示:

當(dāng)一個(gè)動(dòng)作的值發(fā)生變化時(shí),對(duì)應(yīng)右側(cè)會(huì)突出顯示綠色,然后逐漸消失。
七、 動(dòng)作綁定
創(chuàng)建動(dòng)作以后,需要將動(dòng)作進(jìn)行默認(rèn)綁定。打開VR控制器,保持SteamVR客戶端開啟,在SteamVR Input窗口中,點(diǎn)擊Open binding UI按鈕,此時(shí)將使用操作
系統(tǒng)默認(rèn)的網(wǎng)頁瀏覽器打開SteamVR動(dòng)作綁定頁面。在此頁面中選擇需要綁定的控制器,點(diǎn)擊Edit按鈕,進(jìn)入綁定編輯頁面,如下圖所示:

在動(dòng)作編輯頁面中,點(diǎn)擊每個(gè)按鍵旁邊的加號(hào)按鈕, 彈出窗口會(huì)詢問將此按鍵綁定為哪種模式,如下圖所示:

通常情況下,Single類型的動(dòng)作可以設(shè)置為TRIGGER,Boolean類型的動(dòng)作可以設(shè)置為BUTTON。設(shè)定以后,在按鍵左下角點(diǎn)擊編輯按鈕,在顯示為None的位置
指定相應(yīng)的動(dòng)作,如下圖所示:

所有動(dòng)作綁定完畢,點(diǎn)擊頁面底部的Replace Default Binding按鈕,會(huì)將綁定設(shè)置保存到配置文件當(dāng)中。在程序運(yùn)行時(shí),無論是開發(fā)者還是游戲玩家,都可以
再次修改綁定配置,并且能夠反映到游戲當(dāng)中。默認(rèn)情況下,每個(gè)控制器上相同的按鍵綁定相同的動(dòng)作,如果想為每個(gè)控制器設(shè)置不同的動(dòng)作,比如,左手控制
器按下Touchpad實(shí)現(xiàn)傳送,而右手控制器按下Touchpad使用指針選擇物體,那么在這種情況下,可以取消勾選頁面中的Mirror Mode復(fù)選框,然后分別為每個(gè)
控制器指定不同的動(dòng)作。

在綁定編輯界面中同樣可以進(jìn)行動(dòng)作測(cè)試,點(diǎn)擊頁面底部的Input Debugger按鈕進(jìn)入測(cè)試頁面,在此頁面中,將實(shí)時(shí)顯示各動(dòng)作的狀態(tài)、動(dòng)作綁定的按鍵、控
制器的實(shí)時(shí)數(shù)據(jù)等信息,如下圖所示:

八、 在代碼中使用動(dòng)作
此處以訪問platformer動(dòng)作集下的Move動(dòng)作為例,演示如何獲取動(dòng)作狀態(tài),執(zhí)行以下步驟:
打開SteamVR示例場(chǎng)景Simple Sample,選擇游戲?qū)ο骩CameraRig],為其掛載SteamVR_ActivateActionSetOnLoad組件,并指定啟用platformer動(dòng)作集,
如下圖所示:

新建C#腳本,并命名為GetMoveAction.cs,編寫代碼如下所示:
using UnityEngine;
using Valve.VR;
public class GetMoveAction : MonoBehaviour
{
void Update()
{
if (SteamVR_Input.platformer.inActions.Move.GetChanged(SteamVR_Input_Sources.Any))
{
Vector2 pos = SteamVR_Input.platformer.inActions.Move.GetAxis(SteamVR_Input_Sources.Any);
Debug.Log(pos);
}
}
}
返回場(chǎng)景,將此腳本掛載到任意游戲?qū)ο笊希4鎴?chǎng)景運(yùn)行程序,在任意控制器上觸摸Touchpad時(shí),將在控制臺(tái)輸出如下信息:

九、Interaction System 的變化
從功能層面來看,本次更新在Interaction System中并沒有添加太多特性,這從Interaction System的更新文檔中也可得出此結(jié)論,如下圖所示:

Interaction System的示例場(chǎng)景進(jìn)行了重新設(shè)計(jì),如下圖所示:

在目前的示例場(chǎng)景中,除包含SteamVR 2.0之前的大部分演示外,還加入了一些新的特性。比如,對(duì)投擲(Throwing)部分做了擴(kuò)展、添加了遙控器和遙控車、
簡易的推動(dòng)按鈕、手榴彈,以及一些關(guān)于Hand的示例。
在Throwing部分中,目前的示例能夠?qū)崿F(xiàn)基于速度估計(jì)的交互,類似于另外一個(gè)VR開發(fā)工具NewtonVR能夠?qū)崿F(xiàn)的效果——被抓取的物體不會(huì)直接穿過固定靜止的
障礙物,而是跟隨Hand沿障礙物邊界滑動(dòng)。所有這些交互相關(guān)的參數(shù)都可以在Throwable組件中進(jìn)行設(shè)置。
Remotes部分有兩個(gè)簡易控制器,提供了一種與物體進(jìn)行間接交互的演示。如下圖所示。

在場(chǎng)景中的Skeleton部分,可以通過點(diǎn)擊按鈕來顯示和隱藏控制器模型,以及切換不同的手部模型,如下圖所示:

隨著版本的更新,Interaction System已經(jīng)隨之支持Input System。在源代碼中,可以看到Interaction System 開始使用 Input System 配置的行為進(jìn)
行交互驅(qū)動(dòng),如下圖所示:

十、VRTK 是否能夠繼續(xù)結(jié)合 SteamVR 2.0 使用
通過以上論述可見,如果VRTK沒有新版更新的話,理論上來說并不能使用 VRTK(當(dāng)前版本為 3.2.1) 結(jié)合 SteamVR 2.0 進(jìn)行開發(fā),除非克隆VRTK源碼,
自行對(duì)其進(jìn)行修改。這在 Asset Store 中也可找到了印證,如下圖所示:

所以,當(dāng)前版本的 VRTK (3.2.1)并不支持 SteamVR 2.0。使用 VRTK 的開發(fā)者可繼續(xù)使用 SteamVR 1.2.3 進(jìn)行開發(fā),SteamVR Plugin 的過往版本可
訪問以下鏈接進(jìn)行下載。
https://github.com/ValveSoftware/steamvr_unity_plugin/releases