往期回顧:
Unity VR 開發(fā)教程 OpenXR+XR Interaction Toolkit (一) 安裝和配置
Unity VR 開發(fā)教程 OpenXR+XR Interaction Toolkit (二) 手部動畫
Unity VR 開發(fā)教程 OpenXR+XR Interaction Toolkit (三) 轉(zhuǎn)向和移動
Unity VR 開發(fā)教程 OpenXR+XR Interaction Toolkit (四) 傳送
Unity VR 開發(fā)教程 OpenXR+XR Interaction Toolkit (五) UI
此教程相關(guān)的詳細(xì)教案,文檔,思維導(dǎo)圖和工程文件會放入 Spatial XR 社區(qū)。這是一個高質(zhì)量知識星球 XR 社區(qū),博主目前在內(nèi)擔(dān)任 XR 開發(fā)的講師。此外,該社區(qū)提供教程答疑、及時交流、進(jìn)階教程、外包、行業(yè)動態(tài)等服務(wù)。
社區(qū)鏈接:
Spatial XR 高級社區(qū)(知識星球)
Spatial XR 高級社區(qū)(愛發(fā)電)
交互一般需要兩個對象:一個是可交互的對象(Interactable),一個是發(fā)起交互的對象(Interactor,一般是玩家自己)。本系列教程中的傳送功能也是交互的一種方式,可傳送的地面是可交互的對象,手部發(fā)出的傳送射線是發(fā)起交互的對象。而這篇教程將要介紹的是如何在 VR 世界中直接用雙手與物品進(jìn)行交互,此時物品是可交互的對象,手是發(fā)起交互的對象。
??教程說明
使用的 Unity 版本: 2021.3.5
使用的 VR 頭顯: Oculus Quest 2
教程使用的 XR Interaction Toolkit 版本:2.3.2(此教程盡量考慮了向上兼容,如果有過期的地方,歡迎大家指出)
項目源碼(持續(xù)更新):https://github.com/YY-nb/Unity_XRInteractionToolkit2.3.2_Demo
前期的配置:環(huán)境配置參考教程一,手部模型參考教程二。本篇教程的場景基于上一篇教程搭建的場景進(jìn)行延伸。
最終實(shí)現(xiàn)的效果:
??VR 交互的類型
VR 中的交互一般分為三種類型:
- Hover (懸停) :一般指的是發(fā)起交互的對象停留在可交互對象的交互區(qū)域。以手與物品交互為例,假設(shè)物品表面為可交互區(qū)域,當(dāng)手觸摸到物品(懸停在物品的可交互區(qū)域),則視為觸發(fā)了 Hover。
- Grab(抓?。?/strong>:這個概念好理解,就是把物品抓起來。
- Use(使用):“使用” 是基于 “抓取” 的。有時候我們可以繼續(xù)使用正在抓取的物體,觸發(fā)它的一些特性。比如抓取一把槍視為 Grab,然后按下槍的扳機(jī)發(fā)射子彈則視為 Use。
接下來我會詳細(xì)講解這三種類型的用法。那么首先我們需要擁有可交互對象和發(fā)起交互的對象,讓交互的條件成立,然后再具體實(shí)現(xiàn)交互的類型。
??發(fā)起交互的對象(Interactor)
?XR Direct Interactor 腳本
因?yàn)楸窘坛虉鼍爸械?XR Origin 沿用了上一篇 UI 教程中的游戲物體,所以我們先回顧一下 XR Origin 目前的結(jié)構(gòu):
類似的,我們很容易想到在 LeftHand Controller 和 RightHand Controller 下創(chuàng)建子物體,然后添加專門負(fù)責(zé)抓取的腳本,因?yàn)樽ト』旧鲜怯檬直?Grip 鍵,而且綁定了“按下 Grip 鍵” 的 Input Action 已經(jīng)添加到了父物體的 XR Controller 的 Select Action 中,所以子物體無需額外添加 XR Controller
為了讓雙手成為發(fā)起交互的對象,我們需要用到 XR Direct Interactor 腳本。
我們可以在 LeftHand Controller 和 RightHand Controller 下分別創(chuàng)建 Direct Interactor 子物體,然后在 Direct Interactor 上添加 XR Direct Interactor 腳本:
?添加可交互區(qū)域
XR Direct Interactor 需要一個 Trigger 類型的碰撞體,作為可交互的區(qū)域,并且這個碰撞體需要和 XR Direct Interactor 掛載到同一個游戲物體上。因此我這邊直接在左手和右手的 Direct Interactor 上分別添加一個 Sphere Collider,調(diào)整 Radius,并且把 Is Trigger 勾選。那么當(dāng)可交互對象進(jìn)入這個可交互區(qū)域后,就可以進(jìn)行交互。
??可交互的對象(Interactable)
首先我們創(chuàng)建一個可交互的物品,我這邊用紅色方塊來表示。
?添加剛體
因?yàn)槭峙c物品的交互基本上是基于物理效果的,所以我們要為可交互的物體添加剛體。
?XR Simple Interactable 腳本
最簡單的可交互腳本就是 XR Simple Interactable,但是它沒有自帶抓取的功能。
我們在方塊上添加 XR Simple Interactable 腳本:
雖然 XR Simple Interactable 的官方文檔沒有說明需要添加剛體,但是實(shí)測后發(fā)現(xiàn)物體有了剛體后,XR Simple Interactable 腳本才會生效。
另外要注意的是,掛載可交互腳本的游戲物體還需要一個碰撞體。游戲運(yùn)行后,這個碰撞體會自動賦給 XR Simple Interactable 的 Colliders 數(shù)組。
?Interactable Events
為了讓 XR Simple Interactable 的效果可視化,我們可以添加幾個功能:
1)當(dāng)手觸碰到方塊時,方塊的顏色發(fā)生改變。而這個功能也是 VR 交互方式中很常見的 Hover,即懸停在物品的可交互區(qū)域。
2)觸碰到方塊后,按下手柄 Grip 鍵,方塊的顏色變成藍(lán)色。
3)觸碰到方塊后,按住手柄的 Grip 鍵,再按下手柄的 Trigger 鍵,方塊的顏色變回紅色。
以上的三個功能也分別模擬了 VR 中常見的三種交互方式:懸停(Hover)、抓?。℅rab)、使用(Use)。但是因?yàn)?XR Simple Interactable 腳本的局限性,我們不能真正將物品抓起來,而只能模擬 “抓取” 發(fā)生的事件。
這時候就要用到 XR Simple Interactable 腳本中的 Interactable Events,里面包含了交互時會發(fā)生的一些事件,可以看到里面有一個 Hover Entered,也就是開始懸停在可交互區(qū)域觸發(fā)的事件。然后我們可以手動設(shè)置更改方塊的材質(zhì),我這邊想讓方塊被觸碰后變成黃色。
(也許有小伙伴會注意到 First Hover Entered。它的作用是只能某個手占用了相應(yīng)的事件,比如一只手 Hover 的時候,另一個手過來 Hover 就不會觸發(fā)這個事件。后面我們會看到的 First Select Entered 也是類似的道理)
然后,我們要實(shí)現(xiàn)的第二個和第三個功能分別對應(yīng)了 Select Entered 和 Activated 這兩個事件。同樣地,我們在 Inspector 面板中手動綁定事件:
看到 Select 和 Activate,是不是覺得有些眼熟呢?我們打開 XR Interaction Toolkit 中自帶的輸入配置文件 XRI Default Input Actions:
可以看到
XRI LeftHand Interaction 或者 XRI RightHand Interaction 下就有 Select 和 Activate。Select 動作綁定的是 “按下 Grip 鍵” 這個操作,Activate 動作綁定的是 “按下 Trigger 鍵” 這個操作。
而 Interaction Events 中的 Select 和 Activate 使用的就是 XRI Default Input Actions 配置文件中的這兩個動作,并且要注意的是:Activate 動作必須要以 Select 動作的發(fā)生為前提,Interactable Events 中的所有事件都是以“與可交互對象發(fā)生了交互”為前提。因此,當(dāng)我們的手觸碰到方塊后按下手柄的 Grip 鍵,視為發(fā)生了 Select 動作,觸發(fā)了 “方塊的顏色變成藍(lán)色” 這個事件;在手觸碰到方塊后按下手柄的 Grip 鍵的前提下,繼續(xù)按下 Trigger 鍵,視為發(fā)生了 Activate 動作,觸發(fā)了 “方塊的顏色變成紅色” 這個事件。
這時候,也許有人會有疑問:在上一篇 UI Demo 中,我們也有用到 Activate 這個動作,作用是按下 Trigger 鍵與 UI 進(jìn)行交互。雖然 XR Controller 中的 Select Action 綁定的是 Select 這個動作,正常來說得先按下Grip 鍵才能觸發(fā) Select 動作??墒菍?shí)際上當(dāng) UI 射線射到 UI 上時,射線的顏色變成了白色,說明此時已經(jīng)進(jìn)入了選中的狀態(tài)。我們?yōu)槭裁床恍枰劝聪?Grip 鍵呢?
這是因?yàn)?Canvas 上的 Tracked Device Graphic Raycaster 腳本的特性。這個腳本能讓 UI 被射線響應(yīng),當(dāng)射線射到 UI 上時,自動進(jìn)入選中的狀態(tài),也就是觸發(fā)了 Select 動作。然后在 Select 動作發(fā)生的前提下,我們按下 Trigger 鍵就能與 UI 進(jìn)行交互。因此 UI Demo 中的 Activate 也是以 Select 為前提。
?XR Grab Interactable 腳本
給可交互的物體添加上這個腳本,就能實(shí)現(xiàn)真正的抓取。因?yàn)樽ト?yīng)的是 XRI Default Input Actions 配置文件中的 Select 動作,而 Select 動作綁定的是 “按下手柄的 Grip 鍵” 這個操作,所以當(dāng)手部靠近可交互物體時,按下手柄的 Grip 鍵就能抓取物體。
注:要想使用 XR Grab Interactable 腳本,必須給物體添加剛體組件。不過即使之前沒有剛體,添加 XR Grab Interactable 腳本也會自動給物體加上剛體。
?Movement Type(Instantaneous, Kinematic,Velocity Tracking )
在 XR Grab Interactable 腳本中,比較重要的是三種 Movement Type:Instantaneous, Kinematic 和 Velocity Tracking
為了以示區(qū)分,我這邊創(chuàng)建了三個掛載 XR Grab Interactable 腳本的方塊,紅色方塊對應(yīng) Instantaneous,黑色方塊對應(yīng) Kinematic,綠色方塊對應(yīng) Velocity Tracking
Instantaneous:
物體的移動位置和姿態(tài)完全跟隨了 Interactor(手)的移動。它是通過在每一幀更新 Position 和 Rotation 進(jìn)行移動,所以看上去物體跟隨手部的移動是幾乎沒有延遲的。但是這種移動方式?jīng)]有運(yùn)用物理剛體的效果(即使物體上有剛體)。此時抓取的物體會穿過帶有碰撞體的桌子,并且和其他剛體方塊的碰撞效果也不是基于物理的。
Kinematic:
通過 Kinematic Rigidbody(運(yùn)動剛體)進(jìn)行移動,跟隨手部移動的過程中會有一些延遲。移動過程中物體不受力和碰撞的作用。所以此時物體觸碰其他碰撞體不會受到反作用力,比如物體還是能穿過帶有碰撞體的桌子。但是可以對其他剛體(Kinematic Rigidbody 除外)施加物理效果,比如此時移動的 Kinematic 方塊能夠推動其他放置在桌子上的方塊。
Velocity Tracking:
通過設(shè)置剛體的速度和角速度進(jìn)行移動。跟隨手部移動的過程中會有一些延遲。移動過程中帶有剛體的物理效果,比如會和帶有碰撞體的桌子發(fā)生碰撞,也可以對其他剛體產(chǎn)生力的效果。
?Attach Transform 抓取點(diǎn)
XR Grab Interactable 腳本中有一個 Attach Transform 變量可以賦值,作為物體的抓取點(diǎn)。如果沒有賦值,將默認(rèn)以物體的 position 作為抓取點(diǎn)。
這個變量有時候很有用。比如抓取一把槍,那么物品的抓取點(diǎn)應(yīng)該位于槍柄上。
現(xiàn)在我們造一把簡易的槍,來看看 Attach Transform 怎么使用,當(dāng)然大家也可以用自己的模型資源。
然后添加碰撞體,剛體以及 XR Grab Interactable 腳本。這里我先把 Movement Type 設(shè)為 Instantaneous。此時如果運(yùn)行程序,會發(fā)現(xiàn)抓取槍的抓取點(diǎn)不是我們想要的樣子。
這時候,Attach Transform 就派上了用場。我們可以在槍的游戲物體下創(chuàng)建一個子物體,叫做 Attach Point。
然后將 Attach Point 賦給 Attach Transform:
接下來我們運(yùn)行程序,動態(tài)調(diào)整 Attach Point 的 Position 和 Rotation,直到手能握住槍柄,復(fù)制 Attach Point 的 Transform組件,退出程序后將其粘貼至 Attach Point 原來的 Transform 組件。
現(xiàn)在,抓取點(diǎn)的效果看起來稍微好了一點(diǎn),大家也可以自行優(yōu)化。但是這里還有一個問題,剛剛的 Attach Point 對應(yīng)的是右手的抓取點(diǎn),如果我用左手抓取槍,會發(fā)現(xiàn) Attach Point 的位置是不對的。具體的解決方法我會放在下一個部分的 “優(yōu)化一:左右手抓取” 進(jìn)行詳細(xì)說明。
值得注意的是,僅設(shè)置 Attach Transform 只是粗略地抓取。因?yàn)樗谖锢硇Ч巷@得不是很真實(shí),可以看到物體和手之間還是有穿模的現(xiàn)象,因?yàn)槲覀冇玫氖俏杖氖植縿赢媮碜鳛樽ト〉氖謩?。如果要?shí)現(xiàn)精細(xì)的抓取,也就是根據(jù)不同物體的形狀和大小匹配不同的抓取手勢,可以參考我的這篇教程:Unity VR 開發(fā)教程 OpenXR+XR Interaction Toolkit(九)根據(jù)不同物體匹配對應(yīng)的抓取手勢,不過還是建議大家按順序把我的教程看完。
關(guān)于 XR Grab Interactable 腳本的其他變量設(shè)置,大家可以參考官方文檔:
https://docs.unity3d.com/Packages/com.unity.xr.interaction.toolkit@2.3/manual/xr-grab-interactable.html
?代碼實(shí)現(xiàn) Use 功能(制作簡易手槍)
接著上面槍的例子,我們要怎么實(shí)現(xiàn)抓起槍后按下手柄 Trigger 鍵進(jìn)行射擊呢?這個功能實(shí)際上就是 VR 交互中的 Use 功能。
聯(lián)想 XR Simple Interactable 和 Interactable Events 的部分,因?yàn)?XR Grab Interactable 腳本中也有一模一樣的 Interactable Events,所以我們可以在 Inspector 面板中綁定 Activate 觸發(fā)時的事件。不過,現(xiàn)在我想展示如何用代碼來進(jìn)行 Interactable Events 事件的綁定。
??核心腳本
我們創(chuàng)建一個腳本,叫做 GunController,把它掛載到槍的游戲物體上。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
public class GunController : MonoBehaviour
{
public GameObject bullet;
public Transform spawnPoint;
public float fireSpeed = 40;
void Start()
{
XRGrabInteractable grabbable = GetComponent<XRGrabInteractable>();
grabbable.activated.AddListener(FireBullet);
}
private void FireBullet(ActivateEventArgs arg)
{
GameObject spawnBullet = Instantiate(bullet,spawnPoint.position,spawnPoint.rotation);
spawnBullet.GetComponent<Rigidbody>().velocity = spawnPoint.forward * fireSpeed;
Destroy(spawnBullet,5);
}
}
其中最重要的部分就是獲取 XR Grab Interactable 中的 activated 事件,然后通過 AddListener 綁定事件觸發(fā)的函數(shù)。
??制作子彈(碰撞檢測方式設(shè)為 Continous Dynamic)
然后我們可以創(chuàng)建一個子彈的 Prefab(需要剛體和碰撞體):
這里有個小坑需要注意一下,就是我們最好要把子彈 Rigidbody 的 Collision Detection 設(shè)為 Continous Dynamic,否則因?yàn)樽訌検歉咚龠\(yùn)動的,有時候會檢測不到和剛體的碰撞,造成子彈直接從物體中間穿過去。
??制作子彈發(fā)射位置
再創(chuàng)建一個槍的子物體,叫做 Spawn Point,作為子彈生成的位置。該物體的 z 軸箭頭方向?qū)?yīng)子彈發(fā)射的方向。
然后把子彈和 Spawn Point 賦給 Gun Controller:
這時候,槍的功能就制作好了。我們試著運(yùn)行程序:
大功告成!??
??優(yōu)化一:左右手抓?。ㄅ袛嗄闹皇峙c物體交互)
到目前為止,我們還有一個問題沒有被解決,就是槍的 Attach Transform 的為止只適用于右手的抓取。我們希望左右手的抓取點(diǎn)都是正確的,但是槍的 XR Grab Interactable 腳本只能有一個 Attach Transform。那么我們其實(shí)可以動態(tài)地去切換左右手對應(yīng)的 Attach Transform。
首先,我們要準(zhǔn)備好左右手對應(yīng)的 Attach Point,作為切換用的 Attach Transform:
然后,我們需要修改之前寫的 GunController 腳本 ( 方法一,不推薦):
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
public class GunController : MonoBehaviour
{
public GameObject bullet;
public Transform spawnPoint;
public float fireSpeed = 40;
private Transform leftHandAttachPoint;
private Transform rightHandAttachPoint;
private XRGrabInteractable grabbable;
void Start()
{
leftHandAttachPoint = transform.Find("LeftHand Attach Point");
rightHandAttachPoint = transform.Find("RightHand Attach Point");
grabbable = GetComponent<XRGrabInteractable>();
grabbable.selectEntered.AddListener(ChangeAttachTransform);
grabbable.activated.AddListener(FireBullet);
}
private void ChangeAttachTransform(SelectEnterEventArgs arg)
{
Transform interactor = arg.interactorObject.transform;
if (interactor.name == "LeftHand Controller")
{
grabbable.attachTransform = leftHandAttachPoint;
}
else if (interactor.name == "RightHand Controller")
{
grabbable.attachTransform = rightHandAttachPoint;
}
}
private void FireBullet(ActivateEventArgs arg)
{
GameObject spawnBullet = Instantiate(bullet,spawnPoint.position,spawnPoint.rotation);
spawnBullet.GetComponent<Rigidbody>().velocity = spawnPoint.forward * fireSpeed;
Destroy(spawnBullet,5);
}
}
核心思想就是給 XR Grab Interactable 的 Select Entered 事件綁定事件觸發(fā)的函數(shù),通過判斷是哪一個 Interactor 來決定切換成哪一個 Attach Transform。
剛剛我們將左右手切換抓取的功能寫在了槍的控制器中,但是這樣代碼的耦合性可能會比較高,因?yàn)槌藰?,可能還會有其他的物體可以用左右手切換抓取。如果想要讓左右手抓取的腳本更為通用,我們可以新建一個腳本繼承 XR Grab Interactable (方法二):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
public class XRGrabInteractableTwoAttach : XRGrabInteractable
{
public Transform leftAttachTransform;
public Transform rightAttachTransform;
protected override void OnSelectEntered(SelectEnterEventArgs args)
{
if(args.interactorObject.transform.CompareTag("Left Hand"))
{
attachTransform = leftAttachTransform;
}
else if(args.interactorObject.transform.CompareTag("Right Hand"))
{
attachTransform = rightAttachTransform;
}
base.OnSelectEntered(args);
}
}
核心思想類似,我們是重寫了 XR Grab Interactable 當(dāng)中的 OnSelectEntered 方法,它會在 Select Entered 事件觸發(fā)時被調(diào)用。
然后,我們把 Gun 上的 XR Grab Interactable 腳本替換成 XRGrabInteractableTwoAttach,而之前 GunController 當(dāng)中和左右手抓取相關(guān)的代碼就可以刪除了。
另外,我這邊將左右手 Attach Transform 的賦值改為了拖動賦值,并且用 Tag 來判斷是哪一個 Interactor,所以不要忘了在編輯器中進(jìn)行賦值,并且給左手和右手的 Direct Interactor 加上 Tag 。
?第一次抓取或第一次切換抓取位置錯誤解決方法(兩種方法)
注:如果 XR Interaction Toolkit 的版本在 2.2 及以上,需要額外進(jìn)行一些設(shè)置,否則每只手在第一次抓取或者第一次切換抓取的時候抓取點(diǎn)的位置是錯誤的。下面我會介紹兩種方法:
? 方法一
我們需要在可抓取物體 Gun 上添加 XRSingleGrabFreeTransformer 腳本:
然后游戲運(yùn)行時,會自動將 XR Single Grab Free Transformer 腳本添加到 XR Grab Interactable Two Attach 腳本:
? 方法二
如果用這個方法,則不需要在可抓取物體 Gun 上添加 XR Single Grab Free Transformer 腳本,而是對 XR Grab Interactable Two Attach 腳本的代碼進(jìn)行修改:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
public class XRGrabInteractableTwoAttach : XRGrabInteractable
{
public Transform leftAttachTransform;
public Transform rightAttachTransform;
public override Transform GetAttachTransform(IXRInteractor interactor)
{
Transform i_attachTransform = null;
if (interactor.transform.CompareTag("Left Hand"))
{
i_attachTransform = leftAttachTransform;
}
if (interactor.transform.CompareTag("Right Hand"))
{
i_attachTransform = rightAttachTransform;
}
return i_attachTransform != null ? i_attachTransform : base.GetAttachTransform(interactor);
}
}
這個時候我們重寫的是 GetAttachTransform 方法,而不是原來的 OnSelectEntered 方法。因?yàn)榭床坏?XRI 底層的具體實(shí)現(xiàn)方法,所以目前尚不清楚為什么這樣改能夠解決問題,不過經(jīng)過測試,這種改法是奏效的,也歡迎研究出來的小伙伴和我一起探討一下。
最終效果:
??優(yōu)化二:防止傳送射線誤觸發(fā)遠(yuǎn)距離抓?。↖nteraction Layer Mask)
如果大家的項目中沿用了傳送的功能,那么會發(fā)現(xiàn)一個 BUG:當(dāng)向前推動手柄搖桿發(fā)射傳送射線的時候,如果射線剛好射到可交互的物品上,這個物品會 “附著”在射線上,跟隨射線移動,效果就和抓取一樣,我們稱這種現(xiàn)象為遠(yuǎn)距離抓取,它是由射線來控制抓取,這一部分我會在下一篇教程中進(jìn)行詳細(xì)說明。
因?yàn)?XR Grab Interactable 的抓取對應(yīng)的是 Select 這個動作,而激活傳送射線也是對應(yīng) Select ,所以此時我們是誤打誤撞觸發(fā)了遠(yuǎn)距離抓取,因此我們要想辦法讓傳送射線不觸發(fā)遠(yuǎn)距離抓取。實(shí)現(xiàn)方式也很簡單,我們需要對之前傳送的相關(guān)組件進(jìn)行一些設(shè)置。
首先我們找到負(fù)責(zé)發(fā)射傳送射線的 XR Ray Interactor 腳本,將關(guān)注點(diǎn)放到 Interaction Layer Mask 上。
Interaction Layer Mask 相當(dāng)于交互的過濾器,表示交互的層級。當(dāng)發(fā)起交互的對象(Interactor) 和可交互的對象(Interactable)的 Interaction Layer Mask 至少有一個是相同的,那么它們是可以被交互的。
XR Ray Interactor 作為 Interactor,它的層級是 Everything。然后我們找到作為 Interactable 的 Teleport Area,可以看到它的層級是 Default。
因?yàn)?Default 包含在 Everything 中,所以滿足 Interactor 和 Interactable 的 Interaction Layer Mask 至少有一個是相同的條件,即二者可以交互。
我們再看手與物品交互過程中的 XR Direct Interactor 和 XR Grab Interactable,它們的層級也分別是 Everything 和 Default。所以物品的 Default 也包含在傳送射線的 Everything 中,傳送的射線是能夠與物品進(jìn)行交互的。
那么我們可以單獨(dú)為傳送設(shè)置一個層級。我們點(diǎn)擊 XR Ray Interactor 的 Interaction Layer Mask,再選擇 Add Layer,我們新建一個 Teleport 層級:
然后將與傳送有關(guān)的 XR Ray Interactor 和 Teleport Area 的 Interaction Layer Mask 改成 Teleport:
然后把 XR Direct Interactor 的 Interaction Layer Mask 改為 Default,這樣用于抓取的 Interactor 和 Interactable 的層級是相同的,不會對其他的交互方式產(chǎn)生干擾。當(dāng)然,我們也可以為抓取單獨(dú)設(shè)立一個 Interaction Layer Mask。重要的是搞清楚 Interaction Layer Mask 的作用。
現(xiàn)在再次運(yùn)行程序,這個 BUG 就消失啦!此時傳送射線就只能和 Teleport Area 進(jìn)行交互。??
?其他功能一:將與物體接觸的地方作為抓取點(diǎn)(Dynamic Attach)
我們之前介紹的抓取功能通過設(shè)置 Attach Transform,確定了物體被抓取后的位置與朝向??梢园l(fā)現(xiàn)無論從物體上的哪個部位進(jìn)行抓取,被抓取后的姿態(tài)都是相同的。
有時候,我們并不想要這種抓取方式,而是希望手能直接抓在抓取的部位上。XR Grab Interactable 腳本也提供了這個功能。
現(xiàn)在,我創(chuàng)建一個細(xì)長的 Cube,作為該功能的測試物體。
添加碰撞體,剛體。然后添加 XR Grab Interactable 腳本,勾選其中的 Use Dynamic Attach :
勾選后會自動跳出 Match Position (匹配抓取時的 Position),Match Rotation(匹配抓取時的 Rotation),Snap To Collider(抓在物體的碰撞體上)。這些選項可以根據(jù)需求進(jìn)行更改。
效果:
?XR Tint Interactable Visual 腳本
這個腳本可以掛載到可交互對象上,當(dāng)發(fā)起交互的對象(Interactor)懸停(Hover)或者選中(Select 動作觸發(fā),對應(yīng) Input System 中設(shè)置的 Select 映射關(guān)系,一般和 “按下手柄 Grip 鍵” 進(jìn)行綁定)可交互的對象時,能夠暫時改變可交互對象的顏色。
調(diào)整 Tint Color 能夠設(shè)置想要改變的顏色;勾選 Tint On Hover 能夠在 Hover 的時候改變顏色;勾選 Tint On Selection 能夠在 Select 的時候改變顏色。
??取消身體和可抓取物體的物理碰撞
因?yàn)?XR Origin 上有個 Character Controller 組件,是個碰撞體,所以玩家的身體是能夠和其他碰撞體發(fā)生碰撞的。但是這個時候會有個 BUG,尤其是進(jìn)行持續(xù)移動的時候,因?yàn)榭勺ト∥矬w的剛體會和 Character Controller 發(fā)生物理作用,所以移動過程中將可抓取物體靠近自己的身體時會產(chǎn)生推力,造成詭異的移動現(xiàn)象。因此,我們要取消 Character Controller 和剛體的物理碰撞。
首先,我們要設(shè)置 XR Origin 和可抓取物體的 Layer
我這里將 XR Origin 的 Layer 設(shè)為 Player,因?yàn)橹恍枰O(shè)置 Character Controller 所在物體的 Layer,所以我們無需設(shè)置子物體的 Layer,因此選 No:
然后將所有可抓取的物體 Layer 設(shè)為 Interactable,這時候可以更改物體及其所有子物體的 Layer:
Layer 設(shè)置完畢后我們打開 Unity 編輯器上方菜單欄的 Edit -> Project Settings -> Physics,找到 Layer Collision Matrix,將 Player 和 Interactable 的交叉點(diǎn)取消勾選:
現(xiàn)在,我們的身體就不會和可抓取物體發(fā)生物理碰撞了。
?? XR Interaction Group
XR Interaction Toolkit 2.3 新出了一個 XR Interaction Group 組件。這個 Group 能夠管理多個 Interactor,并且當(dāng)其中一個 Interactor 生效時,Group 內(nèi)的其他 Interactor 會暫時失效。
舉個例子,假如一個 XR Interaction Group 中有 Direct Interactor 和 Ray Interactor 。當(dāng) Direct Interactor 抓取了物體后,Ray Interactor 就無法發(fā)出射線。
回到本篇教程,此時我們的手部已經(jīng)有了一個 Direct Interactor 和 UI Interactor,如果沒有 XR Interaction Group,我們在抓取了物品后面向 UI,手部還是能夠發(fā)出 UI 射線的。如果我們想要在抓取物體的時候讓 UI 射線暫時失效,可以在 Left/RightHand Controller 物體上添加 XR Interaction Group 組件:
然后點(diǎn)擊兩次 Starting Group Members 的 "+"號:
將 Direct Interactor 物體和 UI Ray Interactor 物體拖到 Group 中 (這里需要先點(diǎn)擊 Starting Group Members 下的 “+” 號,然后再拖物體,如果直接將物體拖入 Inspector 面板是不行的):
這時候運(yùn)行游戲,就能達(dá)到我們想要的效果了。
??XR Direct Interactor 腳本中的 Select Action Trigger
XR Direct Interactor 腳本中有個名叫 Select Action Trigger 的參數(shù)也很重要。
它決定了 XR Controller 的 Select Action 要如何被觸發(fā)。
每個參數(shù)的意思可以參考官方文檔:https://docs.unity3d.com/Packages/com.unity.xr.interaction.toolkit@2.3/manual/xr-direct-interactor.html
但是官方的解釋可能有些難懂,我這里就配合圖文,講一下我個人的理解。
首先講一下 Toggle 和 Sticky,這兩個相對而言比較好理解。我們就以抓取這個交互為例,抓取的 Select Action 綁定的是 “按下手柄抓取鍵” 這個動作。
如果選擇了 Toggle,靠近物體按下手柄抓取鍵,物體會被抓在手上,但是這時候即使松開了抓取鍵,物體還是會被抓在手上,只有等到下一次按下抓取鍵,物體才會被釋放。如果選擇了 Sticky,靠近物體按下手柄抓取鍵,物體會被抓在手上,即使松開了抓取鍵,物體還是會被抓在手上,但是只有等到下一次按下抓取鍵并松開抓取鍵,物體才會被釋放。
Toggle:
Sticky:
接下來說一說 State 和 State Change 的區(qū)別。官方的解釋看起來可能不大好理解。從實(shí)際使用體驗(yàn)上來看,在可抓取物體的 Select Mode 選擇了 Single, 也就是只允許單手抓取的前提下,對這個物體進(jìn)行左右手切換抓取,就能看出顯著的區(qū)別。
如果選擇了 State,會有一只手無法接管另一只手的抓取權(quán),可能出現(xiàn)在左手,也可能出現(xiàn)在右手。舉個例子,可能右手柄按著抓取鍵將物體抓在手中的時候,左手柄按下抓取鍵無法接管抓取權(quán),也就是無法切換到左手進(jìn)行抓取,這種情況下只有先松開右手手柄抓取鍵才會進(jìn)行切換抓取。
如果選擇了 State Change,則可以隨意切換抓取。因?yàn)橥扑]大家在抓取功能上使用 State Change。
State:
State Change:文章來源:http://www.zghlxwxcb.cn/news/detail-511046.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-511046.html
到了這里,關(guān)于Unity VR 開發(fā)教程 OpenXR+XR Interaction Toolkit (六)手與物品交互(觸摸、抓?。┑奈恼戮徒榻B完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!