# VR
[toc]
# VR 環境設置
1. `Project Setting / XR Plug-in Management` 下載 OpenXR



2. 將 `Project Setting / OpenXR` 打開後在 `Interaction Profiles` 加入自己的VR裝置
將 `Render Mode` 改成 `Multi Pass`

3. 下載 XR Interaction Toolkit

# VR基礎設備設定
## VR headset 頭盔轉動
1. 建立一個空物件叫 `VR Rig` ,並未這個空物件加入新元件 `XR Origin`

2. 為 `VR Rig` 物件新增一個子物件名為 `Camera Offset`

3. 為 `Camera Offset` 物件新增一個Camera子物件並命名為 `VR Camera`


4. 在`VR Camera` 底下新增一個新的元件 `Traked Pose Driver`

5. 將 `VR Rig` 底下的 `XR Origin` 元件分別放入 `Camera Offset` 、 `VR Camera` 進去

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

## VR 左右手
1. 在 `Camera Offset` 底下建立兩個空的子物件分別命名為 `Left Hand` 、 `Right Hand`
2. 選左右手物件並將兩個物件都加入 `XR Controller (Device-based)`

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

## 製作暫時的方塊雙手
1. 在 `Hierarchy` 新增一個空的物件叫 `VR Controller`,並在該物件底下新增一個子物件 3D Cube,確保`VR Controller` 、 Cube 的 `Position` 都是`(0,0,0)`。

Cube參考大小

2. 將 `VR Controller` 拉到 `Project` 變成 `Prefab`
3. 將Prefab放到左右上

- 常見問題: 手臂過遠或不精準
- 解決1: 確保Cube在一個空物件底下(`VR Controller`)
- 解決2: 確保`VR Controller` 、 Cube 的 `Position` 都是`(0,0,0)`
## 雙手抓取物件
1. **左右手** 新增元件 `XR Direct Interactor` 跟 `Sphere Collider`

`Sphere Collider` 設為 trigger :heavy_check_mark:

2. 將**被抓取**的物體加上 `Collider` 、 `Rigibody` 、`XR Grab Interactable`

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


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

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

### 對應的手把按鈕
[載入兩個 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 選擇特徵
- 左手搖桿

- 左手加入搖桿的建模

- 右手搖桿

- 右手加入搖桿的建模

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


成品:

# 手部動畫
使用Blend Tree完成
# Teleportation
1. 在`VR Rig`這個物件裡面加入兩個元件`Locomotion System` 、 `Teleportation Provider`
且不用幫這兩個元件的欄位放上物件,直接按play這兩個元件會自動抓取物件

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

3. 確保`Right Teleport Ray`底下的XR Controller元件是(Device-based),並將Controller Node 改為右手

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

# 與物件的互動
## 開門
1. 將整個門的物件加上此元件 `XR Grab Interactable`
2. 接著我們要限制玩家只能在手把物件上才能抓到整個門
為門把加入Box Collider

最後將門把加入整個門的`XR Grab Interactable`元件的Collider裡面,就可以限制只有用門把才可以抓取整個後門了

3. 新增新的Layer,名為`VR Grab Ignore Ray`。
當然我們開門並不想用射線開門,我們想用手的建模開門比起射線會有更多真實感。

4. 打開`Right Teleport Ray`物件並找到`Raycast Mask`欄位,確保`VR Grab Ignore Ray`層沒有被勾選,這樣射線只能用在UI、Ground層

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

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

## VR UI 互動
1. Canvas 的 `Render Mode` 必須是 `World Space`。
2. 為Canvas加入一個元件名子為`Tracked Device Graphic Raycaster`後不需要對元件做任何事。
3. 將EventSystem新增一元件`XR UI Input Module`,並把不需要的刪除
