1. 程式人生 > >HTC Vive unity 教程【轉載】

HTC Vive unity 教程【轉載】

HTC Vive 是一個虛擬現實頭盔,由 HTC 和 Valve 公司製造。它提供一種在虛擬世界中的浸入式體驗,而不是螢幕頭像。

如果你是一個 Unity 開發者,在虛擬現實遊戲中使用 HTC Vive 非常簡單——你可以認為 HTC Vive 和 Unity 是天生一對。

在這篇 HTC Vive 教程中,你會學習如何在 Unity 遊戲中整合 HTC Vive。包括:

  • 下載和配置 StreamVR
  • 處理控制器輸入
  • 在 VR 中和物理物件互動
  • 建立一支鐳射筆
  • 瞬移

在本文最後,你將對未來體驗有一個粗略的瞭解。讓我們開始吧!

注:每個人在戴著頭戴式顯示器都會對運動和旋轉產生不同的反應。如果你是第一此穿戴此類裝置,當感覺不適時請放鬆並深呼吸。大部分人很快就會適應 VR。開頭幾次如果你不適應請不要著急——它很快就會過去。

開始

在正式開始學習之前,你必須擁有下列條件:

  • 一臺支援 VR 的 Windows PC。
  • 在機器上安裝有 Unity 5.5 或更高版本。
  • 一套完整的 HTC Vive 硬體,並配置和升級好軟體。
  • 安裝 Steam 和 SteamVR。請參考這份指南,它將指引你完成硬體安裝,並提供 Steam 和 SteamVR 的下載連結。
  • 熟悉基本的 Unity 使用:[Introduction to Unity: Getting Started](Introduction to Unity: Getting Started)。

確認 HTC Vive 已經開啟並連線!

下載開始專案。解壓縮到任意目錄並用 Unity 開啟。在專案視窗中看一眼資料夾:

每個資料夾都和對應的資源一一對應:

  • Materials: 場景所用到的材質,包括藍色小球。 Models: 所有的模型。 Physics Materials: 彈性小球的物理材質。 Prefabs: 預製件。 Scenes: 遊戲場景。 Scripts: 全部指令碼。 Textures: 場景中所有物件共有的單一紋理。

看一看場景檢視,按 play 按鈕運行遊戲:

這裡不會有太多內容,因為場景中還沒有加入 VR 控制。你需要將 SteamVR 新增到專案中,以便將 Vive 連線到 Unity。

設定 StreamVR

SteamVR SDK 是一個由 Valve 提供的官方庫,以簡化 Vive 開發。當前在 Asset 商店中是免費的,它同時支援 Oculus Rift 和 HTC Vive。

開啟 Asset 商店,在頂部工具欄中選擇 Window > Asset Store:

等商店頁面載入完,在搜尋欄中輸入 StreamVR 並回車。上下滾動瀏覽搜尋結果,點選 StreamVR Plugin,會開啟它的商店頁面:

點選 Download 按鈕,然後靜靜等待。等下載完成,你看到匯入包對話方塊。

點選右下角的 Import,匯入包:

等匯入完成,你會看到下列提示:

點選 I Made a Backup 按鈕,讓編輯器對指令碼進行預編。幾秒後會看到這個視窗:

這是 SteamVR 外掛的介面。它會列出一些編輯器設定,這些設定能夠提升效能和相容性。

當你開啟一個新專案並匯入 SteamVR 時,你會在這裡看到幾個選項。因為開始專案已經優化過,這裡我們只需要禁用解析度對話方塊(resolution dialog)即可。點選 Accept All 按鈕,執行所有推薦的修改。關閉 Asset 商店回到場景檢視。在專案視窗中,我們現在多了一個新資料夾 SteamVR:

開啟這個資料夾,看一眼內容。我們會從 Prefabs 檔案中新增一個 VR GameObjects 到場景中。

同時選中 [CameraRig] 和 [SteamVR] ,將它們拖到結構視窗:

[SteamVR] 負責幾件事情。它在玩家開啟系統選單並將物理重新整理率和繪圖系統進行同步時讓遊戲自動暫停。它還負責處理“房間規模 VR 動作”的平滑。在檢視器面板中檢視屬性:

[CameraRig] 更有趣,因為它控制著 Vive 頭盔和控制器。選擇 [CameraRig] ,在檢視器面板中設定它的位置為 (X:0, Y:0, Z:-1.1),將攝像機放到桌子後面。

從結構檢視中刪除主攝像,因為這會干擾 [CameraRig] 和它的相機。

開啟手柄,檢視螢幕。拿起手柄,四處移動。你會看到在場景檢視中看到虛擬手柄也會隨之移動:

當 SteamVR 外掛檢測到手柄,它會創建出虛擬手柄。虛擬手柄被對映為 [CameraRig] 的子節點:

現在——繼續在場景檢視中——從結構檢視中選擇 Camera(eye),小心地拿起你的頭盔顯示器的頂部皮帶,移動並微微旋轉,同時觀察場景檢視:

攝像機和頭盔顯示器是連線在一起的,它會準確地捕獲頭盔的移動。

現在將頭盔顯示器戴到頭上,拿起手柄,在房間裡四處走動感受一下。

如果你想和物體進行互動,那麼你會大失所望——什麼也不會發生。要新增運動跟蹤之外的功能,需要編寫一點指令碼。

處理輸入

拿起一隻手柄,仔細觀察。每個控制器上有這些按鈕:

Touchpad 既是可以做模擬搖桿也可以當做按鈕。當移動或旋轉手柄時,手柄會有速度和旋轉速度感應,當和物體互動時這會非常有用。

讓我們來編寫一些程式碼!在 Scripts 資料夾中建立一個新的 C# 指令碼,取名為 ViveControllerInputTest 然後用任意程式碼編輯器開啟它。 刪除 Start() 方法,在 Update() 方法之上新增下列程式碼:


// 1
private SteamVR_TrackedObject trackedObj;
// 2
private SteamVR_Controller.Device Controller
{
    get { return SteamVR_Controller.Input((int)trackedObj.index); }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

我們在這裡進行了如下操作:

  1. 對正在被跟蹤的物件進行一個引用。在這裡,也就是一隻手柄。
  2. Device 屬效能夠很方便地訪問到這個手柄。通過所跟蹤的物件的索引來訪問控制器的 input,並返回這個 input。

頭盔和手柄都是被跟蹤的物件——他們在真實事件中的移動和旋轉都會被 HTC Vive 跟蹤到並傳遞到虛擬世界。

在 Update() 方法上方新增方法:

void Awake()
{
    trackedObj = GetComponent<SteamVR_TrackedObject>();
}
  • 1
  • 2
  • 3
  • 4

當指令碼載入時,trackedObj 會被賦值為 SteamVR_TrackedObject 物件,這個物件和手柄是關聯的:

現在你已經能夠訪問手柄了,你可以讀取到它的輸入。在 Update() 方法中新增:

// 1
if (Controller.GetAxis() != Vector2.zero)
{
    Debug.Log(gameObject.name + Controller.GetAxis());
}

// 2
if (Controller.GetHairTriggerDown())
{
    Debug.Log(gameObject.name + " Trigger Press");
}

// 3
if (Controller.GetHairTriggerUp())
{
    Debug.Log(gameObject.name + " Trigger Release");
}

// 4
if (Controller.GetPressDown(SteamVR_Controller.ButtonMask.Grip))
{
    Debug.Log(gameObject.name + " Grip Press");
}

// 5
if (Controller.GetPressUp(SteamVR_Controller.ButtonMask.Grip))
{
    Debug.Log(gameObject.name + " Grip Release");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

上述程式碼包含了所有當玩家在 VR 中時你夠訪問到大部分方法。它將 GameObject 的名字輸出到控制檯,以便區分左右手柄。程式碼的解釋如下:

  1. 獲取手指在 touchpad 上的位置並輸出到控制檯。
  2. 當你按下扳機時,這會列印到控制檯。扳機有一個專門的方法用於判斷它是否被按下:GetHairTrigger(), GetHairTriggerDown() 和 GetHairTriggerUp()。
  3. 如果鬆開扳機,這會列印到控制檯。
  4. 如果按下抓取(grip)鍵,這會列印到控制檯。GetPressDown 方法是用於判斷某個按鈕已經被按下的標準方法。
  5. 如果釋放抓取鍵,這會列印到控制檯。GetPressUp 方法是用於判斷某個按鈕是否已經被釋放的標準方法。

來測試一下指令碼。儲存指令碼,返回 Unity 編輯器。 在結構檢視中選中兩個手柄,拖動剛才建立的指令碼到檢視器中,為它們新增 ViveControllerInputTest 元件。

再次運行遊戲,拿起兩隻手柄,觀察控制檯中的輸出:

按下按鈕,扳機並在 touchpad 上滑動,你會看到控制檯會輸出每個我們註冊的動作:

這僅僅是最基本的輸入。現在我們可以將虛擬世界操縱在我的手心了——差不多這個意思啦!

在物理物件上應用手柄

VR 提供了許多我們在真實世界中不可能實現的能力,比如撿起一個物體,檢視它們並扔到地上,不需要你負責清理。

通過使用觸發器碰撞機和編寫少量指令碼,HTC Vive 能夠建立後顧無憂的虛擬體驗。

在結構檢視中選中兩個手柄,為它們新增剛性體。(Add Component > Physics > Rigidbody)

勾上 Is Kinematic,反選 Use Gravity:

為兩個手柄新增一個盒子碰撞體 (Add Component > Physics > Box Collider) 並勾上 Is Trigger。

預設的碰撞體有點大,我們需要重新指定大小和位置。設定中心為 (X:0, Y:-0.04, Z:0.02),大小為 (X:0.14, Y:0.07, Z:0.05)。這裡需要將值精確到兩位數,否則都會影響到手柄的最終效果。

運行遊戲,從結構檢視中選擇一隻手柄,並拿起真正的手柄。觀察場景檢視,然後將焦點置於你正在拿著的那隻手柄上(按F)。將碰撞體正好放在手柄的頂端部分,這個部分是你用於抓握物體的地方。

不編寫指令碼,碰撞體僅僅是一個無用的方塊——在 Scripts 資料夾中建立一個新指令碼,取名為 ControllerGrabObject 然後開啟它。、

刪除 Start() 方法並在這裡新增這段你已經熟悉的程式碼:

private SteamVR_TrackedObject trackedObj;

private SteamVR_Controller.Device Controller
{
    get { return SteamVR_Controller.Input((int)trackedObj.index); }
}

void Awake()
{
    trackedObj = GetComponent<SteamVR_TrackedObject>();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

這段程式碼和你在輸入測試中的程式碼是一樣的。這裡獲取了手柄,然後儲存到一個變數中以備後用。

在 trackedObj 下面新增變數:

// 1
private GameObject collidingObject; 
// 2
private GameObject objectInHand;
  • 1
  • 2
  • 3
  • 4

這兩個變數的作用分別是:

  1. 一個 GameObject,用於儲存當前與之碰撞的觸發器(trigger),這樣你才能抓住這個物件。
  2. 一個 GameObject,用於儲存玩家當前抓住的物件。

在 Awake() 方法後新增:

private void SetCollidingObject(Collider col)
{
    // 1
    if (collidingObject || !col.GetComponent<Rigidbody>())
    {
        return;
    }
    // 2
    collidingObject = col.gameObject;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

這個方法接受一個碰撞體作為引數,並將它的 GameObject 儲存到 collidingObject 變數,以便抓住和釋放這個物件。同時:

  1. 如果玩家已經抓著某些東西了,或者這個物件沒有一個剛性體,則不要將這個 GameObject 作為可以抓取目標。
  2. 將這個物件作為可以抓取的目標。

現在,新增觸發器方法:

// 1
public void OnTriggerEnter(Collider other)
{
    SetCollidingObject(other);
}

// 2
public void OnTriggerStay(Collider other)
{
    SetCollidingObject(other);
}

// 3
public void OnTriggerExit(Collider other)
{
    if (!collidingObject)
    {
        return;
    }

    collidingObject = null;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

當觸發器碰撞體進入、退出另一個碰撞體時,這些方法將被觸發。

  1. 當觸發器碰撞體進入另一個碰撞體時,將另一個碰撞體作為可以抓取的目標。
  2. 和第一段類似(第一段註釋 //1),但不同的是玩家已經將手柄放在一個物件上並持續一段時間。如果沒有這段程式碼,碰撞會失敗或者會導致異常。
  3. 當碰撞體退出一個物件,放棄目標,這段程式碼會將 collidingObject 設為 null 以刪除目標物件。

下面的程式碼用於抓住一個物件:

private void GrabObject()
{
    // 1
    objectInHand = collidingObject;
    collidingObject = null;
    // 2
    var joint = AddFixedJoint();
    joint.connectedBody = objectInHand.GetComponent<Rigidbody>();
}

// 3
private FixedJoint AddFixedJoint()
{
    FixedJoint fx = gameObject.AddComponent<FixedJoint>();
    fx.breakForce = 20000;
    fx.breakTorque = 20000;
    return fx;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

在這裡,我們:

  1. 在玩家手中的 GameObject 轉移到 objectInHand 中,將 collidingObject 中儲存的 GameObject 移除。
  2. 新增一個連線物件,呼叫下面的 FixedJoint 方法將手柄和 GameObject 連線起來。
  3. 建立一個固定連線並加到手柄中,並設定連線屬性,使它堅固,不那麼容易斷裂。最後返回這個連線。

被抓住的東西也要能夠被放下。下面的程式碼放下一個物體:

private void ReleaseObject()
{
    // 1
    if (GetComponent<FixedJoint>())
    {
        // 2
        GetComponent<FixedJoint>().connectedBody = null;
        Destroy(GetComponent<FixedJoint>());
        // 3
        objectInHand.GetComponent<Rigidbody>().velocity = Controller.velocity;
        objectInHand.GetComponent<Rigidbody>().angularVelocity = Controller.angularVelocity;
    }
    // 4
    objectInHand = null;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

這段程式碼將被抓物件的固定連線刪除,並在玩家扔出去時控制它的速度和角度。這裡關鍵的是手柄的速度。如果沒有這個,扔出的東西會直直地往下掉,不管你用多大的力扔它。相信我,這絕對是錯誤的。

程式碼解釋如下:

  1. 確定控制器上一定有一個固定連線。
  2. 刪除這個連線上所連的物件,然後銷燬這個連線。
  3. 將玩家放開物體時手柄的速度和角度賦給這個物體,這樣會形成了一個完美的拋物線。
  4. 將 objectInHand 變數置空。

最後,在 Update() 方法中新增程式碼以處理手柄的輸入:

// 1
if (Controller.GetHairTriggerDown())
{
    if (collidingObject)
    {
        GrabObject();
    }
}

// 2
if (Controller.GetHairTriggerUp())
{
    if (objectInHand)
    {
        ReleaseObject();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  1. 當玩家按下扳機,同時手上有一個可以抓取的物件,則將物件抓住。
  2. 當玩家鬆開扳機,同時手柄上連線著一個物體,則放開這個物體。

相信你已經迫不及待地想試一把了吧?儲存指令碼,退出編輯器。

在結構檢視中選中手柄,將新指令碼拖到檢視器中將它新增為一個元件。

開心的時候來了!開啟你的手柄,運行遊戲,戴上頭盔。按下扳機,抓起幾個方塊或者圓球,扔出去。你可能需要適應一下。

你不得不佩服你自己——你真的很棒!但我覺得你應該讓你的 VR 體驗變得更好!

製作一隻鐳射筆

因為種種原因,鐳射筆在 VR 世界中非常有用。你可以用它們去戳破虛擬氣球,做瞄準具使用或者調戲虛擬貓咪。

建立鐳射筆非常簡單。只需要一個方塊和一個指令碼。在結構檢視中建立一個方塊 (Create > 3D Object > Cube)。

為它取名 Laser,設定它的位置為 (X:0, Y:5, Z:0),縮放為 (X:0.005, Y:0.005, Z:0) ,並去掉 Box Collider 元件。讓它居中,你會看到他漂浮在其他物件之上:

鐳射不可能有陰影,它們只會有一種顏色,因此我們可以用一個不反光材質實現這個效果。

在 Materials 資料夾下建立一個新材質,取名為 Laser,修改它的著色器為 Unlit/Color ,設定它的 Main Color 為大紅色:

通過將材質拖到場景檢視的 Laser 上即可分配新材質。當然,也可以將材質拖到結構檢視的 Laser 上。

最後,將 Laser 拖到 Prefabs 資料夾,然後從結構檢視中刪掉 Laser 物件。

現在,在 Scripts 資料夾下建立一個新指令碼,名為 LaserPointer,並開啟它。新增你早已熟悉的程式碼:

private SteamVR_TrackedObject trackedObj;

private SteamVR_Controller.Device Controller
{
    get { return SteamVR_Controller.Input((int)trackedObj.index); }
}

void Awake()
{
    trackedObj = GetComponent<SteamVR_TrackedObject>();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在 trackedObj 下面新增變數:

// 1
public GameObject laserPrefab;
// 2
private GameObject laser;
// 3
private Transform laserTransform;
// 4
private Vector3 hitPoint;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  1. 這個變數用於引用 Laser 預製件。
  2. 這個變數用於引用一個 Laser 例項。
  3. 一個 Transform 元件,方便後面適用。
  4. 鐳射擊中的位置。

用這個方法顯示一束鐳射:

private void ShowLaser(RaycastHit hit)
{
    // 1
    laser.SetActive(true);
    // 2
    laserTransform.position = Vector3.Lerp(trackedObj.transform.position, hitPoint, .5f);
    // 3
    laserTransform.LookAt(hitPoint); 
    // 4
    laserTransform.localScale = new Vector3(laserTransform.localScale.x, laserTransform.localScale.y,
        hit.distance);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

這個方法使用一個 RaycastHit 作為引數,因為它會包含被擊中的位置和射擊的距離。

程式碼解釋如下:

  1. 顯示鐳射。
  2. 鐳射位於手柄和投射點之間。我們可以用 Lerp 方法,這樣我們只需要給它兩個端點,以及一個距離百分比即可。如果我們設定這個百分比為 0.5,也就是 50%,這會返回一箇中點的位置。
  3. 將鐳射照射到 hitPoint 的位置。

在 Update() 方法中新增下列程式碼,獲得玩家的輸入:

// 1
if (Controller.GetPress(SteamVR_Controller.ButtonMask.Touchpad))
{
    RaycastHit hit;

    // 2
    if (Physics.Raycast(trackedObj.transform.position, transform.forward, out hit, 100))
    {
        hitPoint = hit.point;
        ShowLaser(hit);
    }
}
else // 3
{
    laser.SetActive(false);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  1. 如果 touchpad 被按下…
  2. 從手柄發射鐳射。如果鐳射照射到某樣物體,儲存射到的位置並顯示鐳射。
  3. 當玩家放開 touchpad,隱藏鐳射。

在空的 Start() 方法中新增程式碼:

// 1
laser = Instantiate(laserPrefab);
// 2
laserTransform = laser.transform;
  • 1
  • 2
  • 3
  • 4
  1. 製造出一束新的鐳射,然後儲存一個它的引用。
  2. 儲存鐳射的 transform 元件。

儲存指令碼,返回編輯器。在結構檢視中選中兩個手柄,將鐳射的指令碼拖進檢視器中以新增一個元件。

現在從 Prefabs 資料夾中將 Laser 預製件拖到檢視器的 Laser 欄中:

儲存專案,重新運行遊戲。拿起手柄,戴上頭盔,按下 touchpad,鐳射出現了:

在繼續之前,右擊輸入測試元件,選擇 Remove Component,從手柄中刪除它們。

之所以要刪除輸入測試元件,因為會在繪製每一幀時向控制檯中輸出字串。這會影響效能,在 VR 中每毫秒都會受影響。為了方便測試我們可以這樣做,但在真正的遊戲中這是不應該的。

接下來是通過鐳射在房間中進行瞬移!

移動

在 VR 中移動不像驅使玩家前進那麼簡單,這樣做會極易引起玩家眩暈。更可行的辦法是使用瞬移。 從玩家的視覺感知來說,寧可接收位置的突然改變,而不是漸進式的改變。在 VR 裝置中輕微的改變都會讓你的速度感和平衡感徹底失控,還不如直接讓你來到一個新的地方。

要顯示你最終位於什麼地方,你你可以使用 Prefabs 資料夾中的大頭釘或標記。

標記是一個簡單的、不反光的圓環:

要使用標記,你需要修改 LaserPointer 指令碼,開啟這個指令碼,在類宣告中新增變數:

// 1
public Transform cameraRigTransform; 
// 2
public GameObject teleportReticlePrefab;
// 3
private GameObject reticle;
// 4
private Transform teleportReticleTransform; 
// 5
public Transform headTransform; 
// 6
public Vector3 teleportReticleOffset; 
// 7
public LayerMask teleportMask; 
// 8
private bool shouldTeleport;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

每個變數的用途如下:

  1. 這是 [CameraRig] 的 transform 元件。
  2. 一個對傳送標記預製件的引用。
  3. 一個傳送標記例項的引用。
  4. 一個傳送標記的 transform 的引用。
  5. 玩家的頭(攝像機)的引用。
  6. 標記距離地板的偏移,以防止和其他平面發生“z-緩衝衝突”。
  7. 一個層遮罩,用於過濾這個地方允許什麼東西傳送。
  8. 如果為 true,表明找到一個有效的傳送點。

在 Update() 方法中,將這一句:

if (Physics.Raycast(trackedObj.transform.position, transform.forward, out hit, 100))
  • 1

替換為這句,以便將 LayerMask 加入到判斷中:

if (Physics.Raycast(trackedObj.transform.position, transform.forward, out hit, 100, teleportMask))
  • 1

這確保鐳射只能點到你能夠傳送過去的 GameObjects 上。 仍然在 Update() 方法中,在 ShowLaser() 一句後新增:

// 1
reticle.SetActive(true);
// 2
teleportReticleTransform.position = hitPoint + teleportReticleOffset;
// 3
shouldTeleport = true;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

程式碼解釋如下:

  1. 顯示傳送標記。
  2. 移動傳送標記到鐳射點到的地方,並新增一個偏移以免 z 緩衝衝突。
  3. 將 shouldTeleport 設為 true,表明找到了一個有效的瞬移位置。

仍然在 Update 方法,找到 laser.SetActive(false); 一句,在後面新增:

reticle.SetActive(false);
  • 1

如果目標地點無效,隱藏傳送標記。

新增下列方法,進行傳送:

private void Teleport()
{
    // 1
    shouldTeleport = false;
    // 2
    reticle.SetActive(false);
    // 3
    Vector3 difference = cameraRigTransform.position - headTransform.position;
    // 4
    difference.y = 0;
    // 5
    cameraRigTransform.position = hitPoint + difference;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

真正的傳送只需要 5 行程式碼嗎?讓我們解釋一下:

  1. 將 shouldTeleport 設為 false。表明傳送進行中。
  2. 隱藏傳送標記。
  3. 計算從玩家頭盔到攝像機中心的座標偏移。
  4. 將這個差中的 y 座標設定為0,因為我們不考慮玩家頭部有多高。
  5. 移動相機到照射點加上所算出來的座標偏移。如果不加上這個偏移,玩家會傳送到一個錯誤的地方。看下面的例子:

看到了沒有,這個偏移起到了一個關鍵的作用,讓我們精確地定位攝像機的位置並將玩家放到他們想去的地方。

在 Update() 的檢查 touchpad 按鍵的 if else 語句之外新增程式碼:

if (Controller.GetPressUp(SteamVR_Controller.ButtonMask.Touchpad) && shouldTeleport)
{
    Teleport();
}
  • 1
  • 2
  • 3
  • 4

如果玩家鬆開 touchpad,同時傳送位置有效的話,對玩家進行傳送。

最後,在 Start() 方法中新增程式碼:

// 1
reticle = Instantiate(teleportReticlePrefab);
// 2
teleportReticleTransform = reticle.transform;
  • 1
  • 2
  • 3
  • 4
  • 5
  1. 建立一個標記點,並將它儲存到 reticle 變數。
  2. 儲存 reticle 的 transform 元件。

儲存指令碼,返回 Unity。 在結構檢視中選中兩個手柄,會發現多了幾個新欄位:

將 [CameraRig] 拖到 Camera Rig Transform 欄,將 TeleportReticle 從 Prefabs 資料夾拖到 Teleport Reticle Transform 欄,將 Camera (head) 拖到 Head Transform 欄。

將 Teleport Reticle Offset 設為 (X:0, Y:0.05, Z:0) ,Teleport Mask 設為 CanTeleport。CanTeleport 不是預設層— 它是專門為這個教程建立的。這個層裡面只有 Floor 和 Table 物件。

現在運行遊戲,用鐳射照射在地板上進行瞬移。

這個示例已經完成,準備盡情地遊戲吧!

結束

你可以在這裡下載完成後的專案。在本教程中,你學會了:

  • 下載並配置 SteamVR。
  • 處理 HTC View 的手柄輸入。
  • 在 VR 中與物理物件互動。
  • 實現一隻鐳射筆。
  • 進行傳送。

這個專案只是一個開始——開始在你自己的專案中使用它!我很想看到你最終完成的作品。 如果你喜歡這個教程,並向學習更多內容,你可以閱讀我們的這本書:Unity Games by Tutorials,那裡會有更多關於虛擬現實遊戲的內容,包括對 Oculus Rift 的支援。

要理解這本書到底說了些什麼,最簡單的法子莫過於觀看這個視訊:

謝謝觀賞,希望你喜歡這篇教程,就像我很享受寫它時所帶來的樂趣一樣。 如果有任何建議、問題或者你想戰士對示例專案所進行改進,請在下面留言。

				<script>
					(function(){
						function setArticleH(btnReadmore,posi){
							var winH = $(window).height();
							var articleBox = $("div.article_content");
							var artH = articleBox.height();
							if(artH > winH*posi){
								articleBox.css({
									'height':winH*posi+'px',
									'overflow':'hidden'
								})
								btnReadmore.click(function(){
									articleBox.removeAttr("style");
									$(this).parent().remove();
								})
							}else{
								btnReadmore.parent().remove();
							}
						}
						var btnReadmore = $("#btn-readmore");
						if(btnReadmore.length>0){
							if(currentUserName){
								setArticleH(btnReadmore,3);
							}else{
								setArticleH(btnReadmore,1.2);
							}
						}
					})()
				</script>
				</article>