---
# System prepended metadata

title: VR

---

# VR

[toc]

# VR 環境設置

1. `Project Setting / XR Plug-in Management` 下載 OpenXR

    ![](https://i.imgur.com/kmFyQYu.png)

    ![](https://i.imgur.com/G4unuNG.png)

    ![](https://i.imgur.com/6XzwuqL.png)


2. 將 `Project Setting / OpenXR` 打開後在 `Interaction Profiles` 加入自己的VR裝置

    將 `Render Mode` 改成 `Multi Pass`

    ![](https://i.imgur.com/1q3TENd.png)
    
    
3. 下載 XR Interaction Toolkit
    
    ![](https://i.imgur.com/93ZC4Px.png)

# VR基礎設備設定
## VR headset 頭盔轉動

1. 建立一個空物件叫 `VR Rig` ，並未這個空物件加入新元件 `XR Origin`

    ![](https://i.imgur.com/aDw84Nh.png)
    
2. 為 `VR Rig` 物件新增一個子物件名為 `Camera Offset`

    ![](https://i.imgur.com/QBv899U.png)

3. 為 `Camera Offset` 物件新增一個Camera子物件並命名為 `VR Camera`
    
    ![](https://i.imgur.com/mSG3zQA.png)
    
    ![](https://i.imgur.com/243lapM.png)

4. 在`VR Camera` 底下新增一個新的元件 `Traked Pose Driver`
    
    ![](https://i.imgur.com/c1sN48U.png)

5. 將 `VR Rig` 底下的 `XR Origin` 元件分別放入 `Camera Offset` 、 `VR Camera` 進去
    
    ![](https://i.imgur.com/jUnYZLp.png)

6. 將`VR Rig` 的 `XR Origin` 元件的欄位 Tracking Origin Mode 改為 Floor

    ![](https://i.imgur.com/bPD69U3.png)


## VR 左右手

1. 在 `Camera Offset` 底下建立兩個空的子物件分別命名為 `Left Hand` 、 `Right Hand`

2. 選左右手物件並將兩個物件都加入 `XR Controller (Device-based)`

    ![](https://i.imgur.com/7hfs5HD.png)

3. 將 `Controller Node` 改成左右手

    ![](https://i.imgur.com/0E4nNKa.png)


## 製作暫時的方塊雙手

1. 在 `Hierarchy` 新增一個空的物件叫 `VR Controller`，並在該物件底下新增一個子物件 3D Cube，確保`VR Controller` 、 Cube 的 `Position` 都是`(0,0,0)`。

    ![](https://i.imgur.com/g5kJBgE.png)
    
    Cube參考大小
    
    ![](https://i.imgur.com/7nEIRfv.png)


2. 將 `VR Controller` 拉到 `Project` 變成 `Prefab`

3. 將Prefab放到左右上

    ![](https://i.imgur.com/wPSAj4u.png)

    
- 常見問題: 手臂過遠或不精準
   
    - 解決1: 確保Cube在一個空物件底下(`VR Controller`)
    
    - 解決2: 確保`VR Controller` 、 Cube 的 `Position` 都是`(0,0,0)`


## 雙手抓取物件

1. **左右手** 新增元件 `XR Direct Interactor` 跟 `Sphere Collider`

    ![](https://i.imgur.com/vY3a52w.png)
    
    `Sphere Collider` 設為 trigger :heavy_check_mark: 
    
    ![](https://i.imgur.com/hdZrLXd.png)

2. 將**被抓取**的物體加上 `Collider` 、 `Rigibody` 、`XR Grab Interactable`
    
    ![](https://i.imgur.com/Ur6i5S2.png)


## VR Input

找到手把的輸入值跟對應名稱

![](https://i.imgur.com/ebZN8Mx.png)

![](https://i.imgur.com/ePgSy2c.png)

`Window / Analysis / XR Interaction Debugger` 打開後按下 play ，操控手把即可觀察數值以及對應名稱

![](https://i.imgur.com/kMf0V3l.png)


HTC VIVE 不知道為何沒辦法看到abxy按鈕的數值，但是當加入Oculus之後就可以從Oculus裡面觀察到按鈕是有被使用的，且對應的名稱是什麼。

![](https://i.imgur.com/PlEApCu.png)


### 對應的手把按鈕

[載入兩個 package](https://drive.google.com/drive/folders/166jb0M8mDYT1YaYR9RrtabZbpPuiMpEd?usp=sharing) : `Oculus Hands` 、 `VR Controllers Model-FIXED`



```csharp=
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;

public class Open_Menu : MonoBehaviour
{
    InputDevice targetDevice;
    InputDeviceCharacteristics desiredCharacteristics = InputDeviceCharacteristics.Right | InputDeviceCharacteristics.Controller;
    bool get = false;
    private void Start()
    {
        StartCoroutine("wait");
    }
    // Update is called once per frame
    void Update()
    {
        if (get)
        {
            targetDevice.TryGetFeatureValue(CommonUsages.primaryButton, out bool _primaryButton);
            if (_primaryButton)
            {
                Debug.Log("案到了");
            }
            
        }

    }

    IEnumerator wait()
    {
        yield return new WaitForSeconds(1f);
        List<InputDevice> devices = new List<InputDevice>();
        //「InputDevices」 class to access input devices that are currently connected to the XR system.

        InputDevices.GetDevicesWithCharacteristics(desiredCharacteristics,devices);   //To get a list of all connected devices, use InputDevices.GetDevices

        if (devices.Count > 0)
        {
            targetDevice = devices[0];
        }
        get = true;
    }
}

```


- PrimaryButton (HTC Vive 沒有支援這個按鈕)

    ```csharp
    InputDevice.TryGetFeatureValue(CommonUsages.primaryButton, out bool primaryButtonValue);
    ```
    
- triggerButton

    ```csharp=
    InputDevice.TryGetFeatureValue(CommonUsages.trigger, out float triggerValue);
    ```
    
- primary2DAxis 蘑菇頭
    
    ```csharp=
    targetDevice.TryGetFeatureValue(CommonUsages.primary2DAxis, out Vector2 primary2DAxisValue);
    ```

### 放入搖桿建模替代方形手

1. 建立兩個空物件分別叫 `Left Hand Presence` 、`Right Hand Presence`，並將以下程式碼都加入兩個物件底下。

```csharp=
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;

public class HandPresence : MonoBehaviour
{
    public bool showController = false;     //展示手或搖桿其中一個
    public InputDeviceCharacteristics controllerCharacteristics;
    public List<GameObject> controllerPrefabs;  //搖桿模型
    public GameObject handModelPrefab;  //手模型

    private InputDevice targetDevice;
    private GameObject spawnedController;
    private GameObject spawnedHandModel;

    void Start()
    {
        StartCoroutine(GetDevices(1.0f));
    }

    void Update()
    {
        if (showController)
        {
            spawnedHandModel.SetActive(false);
            spawnedController.SetActive(true);
        }
        else
        {
            spawnedHandModel.SetActive(true);
            spawnedController.SetActive(false);
        }
    }
    IEnumerator GetDevices(float delayTime)
    {
        //Vive啟動的時間會晚一點所以不能放在start()裡面執行
        yield return new WaitForSeconds(delayTime); 
        
        List<InputDevice> devices = new List<InputDevice>();

        //controllerCharacteristics設為右手跟遊戲控制器，則為在devices這個List裡面抓取有「右手」跟「遊戲控制器」特徵的InputDevice

        //controllerCharacteristics設為左手跟遊戲控制器，則為在devices這個List裡面抓取有「左手」跟「遊戲控制器」特徵的InputDevice
        
        InputDevices.GetDevicesWithCharacteristics(controllerCharacteristics, devices);

        foreach (var item in devices)
        {
            Debug.Log(item.name + item.characteristics);
        }

        if (devices.Count > 0)  
        {
            targetDevice = devices[0];
            GameObject prefab = controllerPrefabs[0];
            spawnedController = Instantiate(prefab, transform); //生成左右手
        }

        spawnedHandModel = Instantiate(handModelPrefab, transform);    //生成手
    }
}


```

2. 在 Inspector 選擇特徵 
    
    - 左手搖桿
        ![](https://i.imgur.com/IH2BahR.png)
    - 左手加入搖桿的建模
        ![](https://i.imgur.com/9ZCyTYD.png)


    - 右手搖桿
    
        ![](https://i.imgur.com/jRLP1va.png)
    
    - 右手加入搖桿的建模
        
        ![](https://i.imgur.com/CsTClLS.png)

3. 將 Hierarchy 的兩個物件 `Left Hand` 、`Right Hand` 底下的元件`Model Prefab` 放入搖桿的建模

    ![](https://i.imgur.com/9AY8DdZ.png)
    
    ![](https://i.imgur.com/3oluPZo.png)


成品:
    
![](https://i.imgur.com/Zu1H5zC.png)


# 手部動畫

使用Blend Tree完成

# Teleportation

1. 在`VR Rig`這個物件裡面加入兩個元件`Locomotion System` 、 `Teleportation Provider`

    且不用幫這兩個元件的欄位放上物件，直接按play這兩個元件會自動抓取物件
    
    ![](https://i.imgur.com/tA5lTFb.png)

2. 在`Camera Offset`物件新增一子物件`XR / Ray Interactor`，並將名稱改為`Right Teleport Ray`

    ![](https://i.imgur.com/qdykdEm.png)

3. 確保`Right Teleport Ray`底下的XR Controller元件是(Device-based)，並將Controller Node 改為右手
    
    ![](https://i.imgur.com/Uq3rVSY.png)

4. 將想要移動的區域加上元件`Teleportation Area`，並確保一定要有Box Collider

    ![](https://i.imgur.com/e56CaIY.png)


# 與物件的互動

## 開門

1. 將整個門的物件加上此元件 `XR Grab Interactable`

2. 接著我們要限制玩家只能在手把物件上才能抓到整個門
    
    為門把加入Box Collider    
    
    ![](https://i.imgur.com/el8iRSX.png)
    
    最後將門把加入整個門的`XR Grab Interactable`元件的Collider裡面，就可以限制只有用門把才可以抓取整個後門了
    
    ![](https://i.imgur.com/0rfFPZv.png)

3. 新增新的Layer，名為`VR Grab Ignore Ray`。

    當然我們開門並不想用射線開門，我們想用手的建模開門比起射線會有更多真實感。
    
    ![](https://i.imgur.com/eDTcKyb.png)

4. 打開`Right Teleport Ray`物件並找到`Raycast Mask`欄位，確保`VR Grab Ignore Ray`層沒有被勾選，這樣射線只能用在UI、Ground層
    
    ![](https://i.imgur.com/Wpa5Olz.png)


5. 在門上新增一元件 `Hinge Joint` ，將開門的方式限制只能Y方向旋轉

    ![](https://i.imgur.com/69ogFDI.png)

6. 將門的`XR Grab Interactable` 元件裡的Movement Type改成**Velocity Tracking**，這樣會有物理系統的表現。

    ![](https://i.imgur.com/5wEhJRe.png)

## VR UI 互動

1. Canvas 的 `Render Mode` 必須是 `World Space`。

2. 為Canvas加入一個元件名子為`Tracked Device Graphic Raycaster`後不需要對元件做任何事。

3. 將EventSystem新增一元件`XR UI Input Module`，並把不需要的刪除

![](https://i.imgur.com/H0bjit5.png)
