開發專欄|Unity+SteamVR開發——交互

2021-02-26 虛擬實境技術及應用

- 正文開始 - 



圖來自:Geeky Gadgets


 前言 

本文使用兩個工具為Unity2018.4.26SteamVR2.6.1,SteamVR2.6.1相比之前的版本有了很大的改變,其中在交互上有了很大的提升,SteamVR2.6.1上給出的案例中提供了拋射物體、線性驅動、環形驅動以及複雜的射箭操作等。儘管給出了諸多的交互案例,但是在實際開發中依然會有新的交互情況出現,在SteamVR2.6.1中沒有詳細的使用說明下,本文首先大概介紹其各種交互案例,然後詳細的介紹其交互的核心組件如Interactable和Hand等,最後結合我使用的案例實現如何動態添加各種交互。


 介紹 

/ 簡單交互Simple Interactable /

如圖1所示,為實現的簡單交互,手觸碰到物體會有使物體呈現黃色輪廓框,然後按下扳機鍵既可以移動物體,但是在這裡手的模型隱藏掉了。該交互方式只需要添加核心交互組件Interactable到父物體上,子物體中有帶碰撞體的就可以實現。

圖1

實現邏輯:

第一步:首先,Hand在激活的時候重複不斷的調用UpdateHovering方法,該方法即處理手懸浮帶Interactable組件的物體:

protected virtual void OnEnable(){inputFocusAction.enabled = true;float hoverUpdateBegin = ((otherHand != null) && (otherHand.GetInstanceID() < GetInstanceID())) ? (0.5f * hoverUpdateInterval) : (0.0f);InvokeRepeating("UpdateHovering", hoverUpdateBegin, hoverUpdateInterval);InvokeRepeating("UpdateDebugText", hoverUpdateBegin, hoverUpdateInterval);}

第二步:在UpdateHovering方法中判斷哪個Interactable物體時和手最近的,判斷的方法為CheckHoveringForTransform,在這個方法中會遍歷子物體中的所有Collider,然後獲取該Collider的父物體上的Interactable組件,如果不為Null,則比較與手的距離,找到最近的那一個,並將其Interactable的實例化對象賦值給Hand中的hoveringInteractable;

第三步:在Hand中hoveringInteractable為Interactable類型的屬性,當該屬性被賦值時會進行廣播消息處理,所有繼承了MonoBehaviour的腳本中定義了 OnHandHoverBegin和OnHandHoverEnd方法的都將被執行。

public Interactable hoveringInteractable{get { return _hoveringInteractable; }set{if (_hoveringInteractable != value){if (_hoveringInteractable != null){if (spewDebugText)HandDebugLog("HoverEnd " + _hoveringInteractable.gameObject);_hoveringInteractable.SendMessage("OnHandHoverEnd", this, SendMessageOptions.DontRequireReceiver);if (_hoveringInteractable != null){this.BroadcastMessage("OnParentHandHoverEnd", _hoveringInteractable, SendMessageOptions.DontRequireReceiver); }}_hoveringInteractable = value;if (_hoveringInteractable != null){if (spewDebugText)HandDebugLog("HoverBegin " + _hoveringInteractable.gameObject);_hoveringInteractable.SendMessage("OnHandHoverBegin", this, SendMessageOptions.DontRequireReceiver);if (_hoveringInteractable != null){this.BroadcastMessage("OnParentHandHoverBegin", _hoveringInteractable, SendMessageOptions.DontRequireReceiver); }}}}}

最後:在該案例的腳本InteractableExample中實現OnHandHoverBegin和OnHandHoverEnd方法。

/ 拋射物體Throwable /

如圖2所示為手抓取物體然後進行拋射的過程,當手抓取物體時會變換為手剛好握住物體的姿態且手指都為靜態的,當手釋放掉物體的時候又恢復到原來的狀態,並且物體拋出去之後具有一定的速度。

圖2

實現邏輯

第一步:同樣需要添加核心交互組件Interactable,並且需要添加SteamVR_Skeleton_Poser、Rigidbody以及Throwable(或子類);

第二步:編輯SteamVR_Skeleton_Poser中所需要的手部姿勢。

1)如圖3所示,點擊Create創建一個新的姿勢,所有的姿勢都是SteamVR_Skeleton_Pose的ScriptableObject,保存後為.asset為後綴的文件,可以通過Resources.Load方法直接加載或者直接拖到面板上使用。

圖3

2) 如圖4所示,勾選Show Right Preview 即對手勢進行編輯,此時可以看到在物體的附近有一個手的模型,如果想在模板的基礎上編輯可以選擇Reference Pose:選擇之後手即可變成模板的樣子。該手部編輯模型會作為子物體出現。

圖4

但是僅僅時在編輯模式下出現,作為編輯使用,編輯完之後需要取消 Show Right Preview的勾選方可正常顯示和使用。

直接調整手的位置和關鍵,使其達到符合要求的握住物體的樣子即可,然後勾選Show Left Preview此時下面的Copy Left pose to Right Hand和Copy Right pose to Right Hand會被激活。注意:因為剛剛編輯的時Right的,因此點右邊下面的Copy Left pose to Right Hand按鈕,將右邊的鏡像處理得到 左邊的數據並覆蓋當前左邊的,點擊之後即可看到兩隻手都以同樣的姿勢握住物體。這裡一定要點對,不然前面的工作會被覆蓋而需要重新做。最後,點擊Save Pose 即可。    

第三步:Throwable編輯,在Throwable腳本中同樣實現了OnHandHoverBegin和OnHandHoverEnd方法,處理握住物體的邏輯。並且還實現了HandHoverUpdate方法,在該方法中首先判斷當前手的按鍵類型,只要不是抓取的按鍵觸發就握住物體,該方法在Hand的Update中被廣播,另外,還有HandAttachedUpdate方法。

protected virtual void HandHoverUpdate( Hand hand ){GrabTypes startingGrabType = hand.GetGrabStarting();if (startingGrabType != GrabTypes.None){        hand.AttachObject( gameObject, startingGrabType, attachmentFlags, attachmentOffset );hand.HideGrabHint();}}

protected virtual void Update(){UpdateNoSteamVRFallback();GameObject attachedObject = currentAttachedObject;if (attachedObject != null){attachedObject.SendMessage("HandAttachedUpdate", this, SendMessageOptions.DontRequireReceiver);}if (hoveringInteractable){hoveringInteractable.SendMessage("HandHoverUpdate", this, SendMessageOptions.DontRequireReceiver);}}

在Throwable中實現HandAttachedUpdate方法,代碼如下:該方法每幀都執行,判斷手的按鍵已經釋放掉了該物體的時執行手放棄物體的操作 hand.DetachObject(gameObject, restoreOriginalParent);,然後Hand的DetachObject方法裡調用廣播函數廣播OnDetachedFromHand,最終在Throwable腳本中的實現的OnDetachedFromHand方法裡處理了最後被扔出去的邏輯。

protected virtual void HandAttachedUpdate(Hand hand){if (hand.IsGrabEnding(this.gameObject)){hand.DetachObject(gameObject, restoreOriginalParent);}if (onHeldUpdate != null)onHeldUpdate.Invoke(hand);}

public void DetachObject(GameObject objectToDetach, bool restoreOriginalParent = true {            ...

if (attachedObjects[index].attachedObject != null){if (attachedObjects[index].interactable == null ||(attachedObjects[index].interactable != null &&attachedObjects[index].interactable.isDestroying == false))attachedObjects[index].attachedObject.SetActive(true);attachedObjects[index].attachedObject.SendMessage("OnDetachedFromHand",this, SendMessageOptions.DontRequireReceiver);}...}

/ 線性驅動LinearDrive /

如圖5所示為線性驅動的效果示意圖,手捂住操作的物體保持姿勢不動,移動手柄,手捂住的物體跟隨運動,但是只保持在橫向的線性位置移動,手握住的物體不會超過該線性區域。

圖5

第一步 :核心組件Interactable當然比不可少,然後實現HandHoverUpdate和HandAttachedUpdate以及OnDetachedFromHand方法,編輯握住物體所需的手勢;

第二步:獲取手部捂住物體之後手移動的參數,計算方法為:獲取手現在的位置和線性起點的位置組成的向量A和終點到起點的向量B,得到向量A和B的點積,然後將這個值作為線性插值的變化因子

protected virtual void HandAttachedUpdate(Hand hand){UpdateLinearMapping(hand.transform);if (hand.IsGrabEnding(this.gameObject)){hand.DetachObject(gameObject);}}protected void UpdateLinearMapping( Transform updateTransform )    {      prevMapping = linearMapping.value;      linearMapping.value = Mathf.Clamp01( initialMappingOffset + CalculateLinearMapping( updateTransform ) );      mappingChangeSamples[sampleCount % mappingChangeSamples.Length] = ( 1.0f / Time.deltaTime ) * ( linearMapping.value - prevMapping );      sampleCount++;if ( repositionGameObject )      {        transform.position = Vector3.Lerp( startPosition.position, endPosition.position, linearMapping.value );      }    }protected float CalculateLinearMapping( Transform updateTransform )    {      Vector3 direction = endPosition.position - startPosition.position;float length = direction.magnitude;      direction.Normalize();      Vector3 displacement = updateTransform.position - startPosition.position;return Vector3.Dot( displacement, direction ) / length;    }

/ 環形驅動CircularDrive /


如圖6所示,其處理邏輯和線性驅動類似,只是在計算物體旋轉上有所差別

 

圖6

/ 懸浮按鈕Hover Button /

如圖7所示,手懸浮在按鈕上,然後向下壓物體可以實現物體按下效果。實現的邏輯和前面的簡單交互類似。

圖7

/ 射箭 /


如圖8所示為雙手射箭的操作,這個交互應該是SteamVR2.6.1這個版本中最複雜的一部分。同樣需要添加Interactable組件。重點是ItemPackageSpawner組件,該組件實現了手用弓箭的所有邏輯。

圖8

1)ItemPackageSpawner實現了HandHoverUpdate方法,並且在面板上勾選了requireGrabActionToTake,因此在手觸碰到弓並且按下抓取的扳機鍵的時候調用SpawnAndAttachObject生成一些列後續操作所需要的包並且這隻手抓住弓。

private void HandHoverUpdate( Hand hand )    {...if ( requireGrabActionToTake )      {GrabTypes startingGrab = hand.GetGrabStarting();if (startingGrab != GrabTypes.None)        {          SpawnAndAttachObject( hand, GrabTypes.Scripted);        }      }    }

SpawnAndAttachObject方法裡先根據ItemPackageType類型來清空手上的東西,然後重新生成一個itemPackage裡面的itemPrefab,然後讓手抓住它,這個時候生成的物體為Longbow,是帶握住手勢的弓,如圖9所示。如果itemPackage的otherHandItemPrefab不為空的話也實例化該物體,並且用另外一隻手抓住它。這裡的otherHandItemPrefab為握住箭的手勢ArrowHand,如圖10所示,在ArrowHand中初始化只保留了一個握住箭的手勢,箭的生成要在其內部 HandAttachedUpdate方法裡實現。

圖9

圖10

private GameObject InstantiateArrow()    {      GameObject arrow = Instantiate( arrowPrefab, arrowNockTransform.position,              arrowNockTransform.rotation ) as GameObject;      arrow.name = "Bow Arrow";      arrow.transform.parent = arrowNockTransform;      Util.ResetTransform( arrow.transform );      arrowList.Add( arrow );while ( arrowList.Count > maxArrowCount )      {        GameObject oldArrow = arrowList[0];        arrowList.RemoveAt( 0 );if ( oldArrow )        {          Destroy( oldArrow );        }      }return arrow;    }private void HandAttachedUpdate( Hand hand ){if ( allowArrowSpawn && ( currentArrow == null ) )   {    currentArrow = InstantiateArrow();    arrowSpawnSound.Play();  }}

 2)、放箭的過程在ArrowHand的HandAttachedUpdate方法中實現,當弓被拉握住箭的手柄按鍵釋放的時候,即射出箭。

private void HandAttachedUpdate( Hand hand )    {...if ( nocked && hand.IsGrabbingWithType(nockedWithType) == false )      {if ( bow.pulled )         {          FireArrow();        }else        {          arrowNockTransform.rotation = currentArrow.transform.rotation;          currentArrow.transform.parent = arrowNockTransform;          Util.ResetTransform( currentArrow.transform );          nocked = false;nockedWithType = GrabTypes.None;          bow.ReleaseNock();          hand.HoverUnlock( GetComponent<Interactable>() );          allowTeleport.teleportAllowed = true;        }        bow.StartRotationLerp();       }}

/ 遠程控制 /

如圖11a和11b所示為操作虛擬手柄遠程控制物體的案例,這兩個案例非常類似,只是處理的過程非常繁瑣,這裡不再詳細展開了,唯一沒有讓我完全搞清楚的地方是,控制虛擬手柄的手部動作是如何做到動態的。跟前面手指靜態的不同,

圖11a

這裡的手姿勢控制未找到SteamVR_Skeleton_Poser的使用。

圖10b

 總結 

交互的核心組件為Interactable,凡是涉及到用手進行交互都需要添加該組件,後面會講射線與物體交互也會用到該組件;

手握住物體的姿勢為SteamVR_Skeleton_Pose,是一個ScriptableObject的資源類,可以在編輯器中進行編輯並且保存為後綴.asset文件,該文件可以實現動態加載;

需要在獲取手和物體的處理邏輯上一定要實現Hand中廣播的方法;

遠程操作的手握住虛擬手柄的姿勢可以動手指,目前還不知道怎麼編輯或設置。

相關焦點

  • Unity3D開發技巧:避開unity編輯器的坑
    總的來講,Unity開發原型和效果、驗證想法,確實是無比便利。可能一個月就把核心玩法做得差不多。強大的編輯器功能讓我們也有很大的可擴展空間來協助我們開發工具。可是編輯器是把雙刃劍。如果提前看清楚有什麼坑在前面,或者其他人踩過什麼坑。我想這會對項目風險的把控會有很大幫助。
  • Unity+JavaScript高效開發3D可視化應用
    SDK 2.0 架構圖從架構圖中,可以了解到,SVE SDK2.0綜合了web3D開發的前沿技術綜合應用開發出來,可以看出包含以下幾點技術應用:1)Unity遊戲引擎應用2)SVE平臺應用3)WebAssembly技術應用4)JavaScript技術應用5)3DGIS技術應用6)傾斜攝影技術應用Unity作為目前市面上主流的3A級遊戲引擎之一,在國內擁有百萬級註冊用戶,很多設計師和遊戲開發者都能夠熟練地使用Unity引擎做資源、效果編輯和應用的交互開發SVE SDK同時藉助Unity完整的生態,創作者很容易的製作出驚豔的交互場景
  • 漫步VR——Unity語音聊天室開發
    安裝:https://unity3d.com/cn/get-unity/download/archive。Unity5.1版本後全力支持VR開發,所以最好下載5.1版本以後的版本。安裝過程是一鍵式安裝。編譯Support包:unity可以很方便移植到多個平臺上,依賴的即是下載安裝對應unity版本的Support-for-Editor。
  • Unity3D VR 教程之VR開發
    基礎開始用unity進行基礎VR開發之前,先確認硬體和軟體已經按照前一篇文章進行了設置。
  • 使用Unity開發谷歌ARCore
    布置開發環境 • 安裝Android Studio 後安裝Android SDK平臺7.0版本(API level 24)或更高。 • 如需升級Android SDK,用Android Stud ...11注意:ARCore現在只是預覽版。在1.0正式版發布之前可能會有巨大的變化。
  • ChinaJoy 2017 | 使用Unity開發坦克AR詳解
    在ChinaJoy 2017的Unity展位上,5分鐘使用Unity製作AR項目吸引了很多策劃、美術以及從未接觸過AR開發的程式設計師們,於現場感受了Unity結合Vuforia的開發魅力。今天這篇文章將為大家分享Unity展位坦克AR項目的詳細開發過程。請注意,本文需要您具備基本的Unity知識,了解Unity腳本、組件、檢視窗口、層級窗口等內容。
  • 小米VR Unity插件開發文檔 小米VR Unity插件怎麼使用與安裝
    小米VR Unity插件簡介開發者可以利用小米VR Unity 插件在Unity裡開發可以運行在安卓手機上的VR應用。開發者開發的VR應用如果運行在推薦的小米手機和MIUI上,可以獲得更好的VR體驗。請使用有較高的硬體配置的小米手機(CPU和GPU),目前支持機型為小米5,5s,5s Plus,Note2這四款小米手機。
  • Unity 2021新產品開發理念前瞻|unity|編輯器|工作流|編程_網易訂閱
    開發理念的轉變,也對我們的工作方式提出了新的要求。發布「開箱即用」的產品,意味著我們每次更新的功能會相應地減少,但是功能的質量會大大地提高,並且在發布前必須經過整體的驗證。為了給開發者帶來真正能幫助到他們的產品,我們的開發團隊規模會變得更大、更全面。尤其是對產品、編程、工程和設計團隊的投入,也會相應地提高,只有這樣,我們才可以專注於提高工作流的質量,提高版本的可靠性。
  • 【VR遊戲開發乾貨】Unity5.3官方VR教程重磅登場-系列1
    PART II 在Unity項目中設置VR首先請下載最新的Oculus runtime 0.8或更高版本(https://developer.oculus.com/downloads/ ),以及最新的Unity5.3或更高版本(http://unity3d.com/cn/get-unity/download )。
  • Unity與Apple合作開發新功能,讓AR開發更容易
    打開APP Unity與Apple合作開發新功能,讓AR開發更容易 工程師周亮 發表於 2018-08-01 10:22:00
  • Unity聯手騰訊遊戲推出防沉迷系統開發工具 你期待嗎?
    竟然剛剛看到騰訊遊戲推出防沉迷系統開發工具,這個到底用處多大,一起來看看吧據騰訊科技消息,實時3D內容創建平臺Unity聯手騰訊遊戲共同推出防沉迷系統開發工具。該工具分為登錄模塊,計時模塊以及充值模塊。目前,該開發工具作為Unity Player身份包的部分內容已經上線,可供開發者在Unity編輯器裡的Package Manager中進行下載。
  • 將坦克大戰帶入現實,Unity AR項目開發詳解
    因為來自東方的神秘力量,天朝小夥伴們可能沒法玩到這款大熱AR遊戲,但今天這篇Unity教程,將告訴你如何使用Unity自己創造出一款AR坦克大戰遊戲,讓你周圍的環境瞬間在5分鐘內變成坦克戰場,現在就打開Unity跟我們一同完成這場開發「閃電戰」吧!請注意,本文需要您具備基本的Unity知識,了解Unity腳本、組件、檢視窗口、層級窗口等內容。
  • 學習unity遊戲開發要看什麼書?
    Unity遊戲引擎集合了開發一個遊戲所需要的大部分工具、組件、環境等內容。就像拍一部電影,我們需要攝像機、搖臂、燈光、挑乾等基礎工具。做遊戲也需要攝像機、燈光、地形、人物控制器等組件。學習unity遊戲開發要看什麼書?
  • 製作人談《蒸汽之城》開發歷程:Unity開發指南
    請注意,這裡我們不會討論使用Unity製作單機遊戲,因為Unity單機或者有社交功能的手遊都有太多成功的巨無霸例子,很多開發者也通過自身經驗表明小團隊使用Unity製作輕量級的單機或社交遊戲並無太大障礙(遊戲列表可以查看官網:http://unity3d.com/gallery/made-with-unity/game-list),下面我們還是以多人在線,需要後臺和大量數據處理的MMORPG
  • 打開虛擬世界的一扇窗:《 Unity AR/VR 開發:從新手到專家》
    具備靈魂和智慧的人工智慧機器人被稱為Cylon。從那年起,我將「賽隆空間」更名為「賽隆網」,並開始著重介紹虛擬實境相關的技術和產品,同時開始發布一些VR/AR開發的深度技術文章。2014年開始決定和機械工業出版社合作出版一本科普類書籍《虛擬實境-引領未來的交互革命》(原名《超越虛擬實境》),以便向大眾普及宣傳虛擬實境的技術和理念。2015年決定成立賽隆空間,並開始真正進入這個行業。
  • Oculus Unity插件通過SteamVR添加了對Windows MR的支持
    在Unity中為Oculus Rift開發遊戲的開發者現在可以添加Windows MR支持,而無需集成單獨的SteamVR插件。而不必管理單獨的Oculus和SteamVR integrations,而是可以堅持使用一個。當然,我們應該注意到SteamVR插件也可以用來支持這兩款頭顯,但是Oculus的Store版本不能使用這個插件。
  • 發布按鈕交互反饋優化開發完成(9.10周四)
    2.5.1版本升級預約流程已發送至客戶群,請及時預約發布按鈕交互反饋優化開發完成,前臺版主積分獎勵功能前端正在開發中;計劃新增簡訊、消息發送記錄功能,用戶登錄日誌功能01 修復/優化1、修復管理後臺用戶管理列表編輯--備註後,無法提交問題;2、修復知識付費專欄商品前臺未顯示虛擬瀏覽量問題;3、修復部分用戶查看我收到的評論,提示網絡繁忙問題;02 正在開發中
  • 使用Unity ECS開發《我的世界》
    直播課程:Facial AR Remote面部捕捉解決方案課程(第一期)直播地址:https://connect.unity.com/events/unitychina-facialarUnity官方教師培訓報名火熱進行中Unity將在10月22-26日,舉辦為期5天的專業的Unity官方教師培訓課程,誠邀廣大教師與Unity一同學習分享最新技術
  • PTC與Unity宣布戰略合作,藉助Vuforia加快增強現實開發
    藉助手持或頭戴設備,Vuforia可為Unity等主流開發工具提供易於操作的工作流程、軟體開發工具包(SDK)和雲服務。Vuforia已被27.5萬餘名開發人員使用,並為App Store和Google Play中的大部分AR應用提供支持。       Unity是創造2D、3D、VR和AR遊戲和體驗的全球領先開發平臺。
  • 玩轉Oculus Quest +Unity開發002-環境配置Mac篇
    系統配置在使用Unity開發Oculus Quest上的應用之前,先介紹下相應的軟硬體配置。以下配置是我個人目前正在用的,不代表其它配置不行。當然,Android Auto和Google Play這種實在沒啥關聯的就算了~最終進入正題,如何在Unity中配置Oculus的開發環境。Step1.