2016 年 9 月

第 31 卷,第 9 期

本文章是由機器翻譯。

Unity - 建置虛擬實境應用程式

Tim Kulp

虛擬實境 (VR) 應用程式開發人員提供獨一無二的機會和挑戰。在一方面,沒有機會來建置嶄新的體驗,透過現有的 2D UX 原型解除繫結。相反地,是要建立超過一個 2D 螢幕 3D 檢視的絕佳 VR 體驗的挑戰。幸運的是,應用程式開發及 UI 設計的一些基本概念是類似的例如建立主要/詳細資料檢視中,跨應用程式透過服務收到來自使用者的輸入和通訊。在本文中我將探討這些主題,從建置 VR 一個商務應用程式,如此您就可以了解如何開始您自己 VR 應用程式的內容。

為什麼 VR 嗎?

它是絕佳的工具,例如 Unity 和相對較小的 VR 服務商場的可用性與輸入 VR 應用程式開發的好時機。考慮目前的行動裝置應用程式市集空間。有數百萬個應用程式和新的應用程式有很多競賽。VR 市場空間仍然很小,不過,會更容易突顯出來,並取得通知。市場大小之上沒有目前的早期採用者 VR 內容的絕佳慾。如何建置應用程式在此空間仍然是新的挑戰,不斷演進的最佳作法。VR 開發早期採用者有助於未來的這個平台。它是處於 VR 令人興奮的時機。

建置 Samsung 齒輪 VR 等 Google 黏貼 VR 裝置的應用程式是供開發人員開始建置其內容,低成本分配通道是絕佳的進入點。如果使用向混合現實的心,VR 應用程式便可以移植到 HoloLens 因為工具相同,而且內容可以有名稱相同。唯一的差別在於程式碼來執行工作 (例如視線 vs。Raycast,這我在本文稍後將探討),並運用一些 HoloLens 獨特功能,例如語音、 空間的對應和筆勢。與一些參考資料和實驗,您可以將 VR 應用程式轉換至 HoloLens 應用程式。

我不會詳述 VR 專案所設定的完整詳細資料。相反地,我建議您瀏覽 Unity Asset Store 中的 Unity VR 範例專案 (bit.ly/1qYLBX9)。這篇文章中建置的專案會從頭開始,但是會使用從 Unity VR 範例專案的指令碼利用標準的工作 (例如進行互動的物件) 的現有程式碼。建議您建立空白的 Unity 專案並匯入 Unity VR 的範例會使用這篇文章中建置專案以外的其他參考。Unity VR 範例有很多絕佳的內容,但已建置為獨立應用程式和執行包含場景外的場景需要一些額外的設定。開啟且可因為它有幾個將用於此專案中的指令碼,保留 Unity VR 範例。這篇文章,我將使用 Unity 5.3 啟用,讓我可以建置 Samsung 齒輪 VR 裝置的應用程式的 Android 工具。

Contoso 旅遊

Contoso 旅行是讓旅遊的系統管理員,如其小組成員,請參閱預約的公司差旅系統。差旅系統管理員可以檢視由機場,搜尋特定的城市,旅行者,並檢視旅行路線詳細資料。在應用程式中,我要建立豐富的體驗,顯示的旅行者 contoso 空間的關聯性。通常這會是特定業務應用程式、 行動或 Web,傳遞給使用者,透過瀏覽器。在本文中,我將建置此應用程式中 VR 深入體驗給使用者,請使用 avatar 代表旅行者,並顯示人員 3D 空間中的相互關聯。

VR 開啟這類應用程式的許多可能性。差旅系統管理員的觀點來看,VR 可以更加個人化的體驗,更吸引人的旅行者的 avatar,而不是只顯示點地圖或清單上的名稱。旅行者,如 VR 可以提供旅行者瀏覽 office 或網站,了解在遠端位置中的預覽。這意思是,使用者就可享有的沉浸式體驗。

開始

您會想要準備即行動 VR 體驗此專案。若要開始,建立名為 ContosoTravelVR 新 3D Unity 專案。首先,變成較慢的畫面播放速率世界的時機。在 [編輯 |專案設定 |偵測器,設定固定 Timestep = 0.01666666,設定最大允許 Timestep 0.1666666 與時幅 = = 1。允許的最大 Timestep 變慢 Unity 的物理時間以便物理計算並不會影響畫面播放速率。雖然物理時間和畫面播放速率不直接相關,物理計算所需的 CPU 功能可能會產生問題,處理框架。設定最大允許 Timestep 放上物理更新花多少時間的最大限制。請記住,在行動 VR 世界中,裝置會繪製兩個映像每個框架︰ 一個左的眼睛和一個右邊的眼睛。在畫面播放速率越高處理複雜的計算可能會導致裝置,例如行動電話,以取得框架不同步,造成眼睛看到的是不同。造成 disorientation 技術中的使用者。很明顯地,這是重要避免絕佳使用者體驗。避免使用任何可能會影響效能,例如高多邊形的物件、 長時間執行的指令碼或鎖定的執行緒計數。

接下來,更新行動 VR 播放程式設定。若要這樣做,請移至編輯 |專案設定 |播放程式,然後選取 [其他設定] 區段。這個區段中沒有名為虛擬實際上支援的設定。核取方塊,以確保世界視為 VR 應用程式。這項設定會自動轉換主攝影機物件已經存在於中 Unity VR 觀景窗。

現在,應用程式設定,設定在場景。在 [階層] 視窗中,加入 cube 中包含的小數位數 x = 10,y = 0.1 和 z = 10。為 cube 的位置重設為 x = 0,y = 0 且 z = 0,置中在世界簡維的 cube。這會提供您的世界,可讓使用者的空間和接地,以及一個地方就某方面來說,全球的獨立實體地板。就像在 2D 應用程式,您需要授與使用者的內容和應用程式的互動會發生的位置。如此可協助引導本身在應用程式的使用者。所有動作 ContosoTravel,會都發生此 floor 物件的範圍內。

在這個世界中,您需要將封送處理整個應用程式資料的物件。階層視窗中建立空的遊戲物件,並將物件重新命名為 TravelerManager。此物件將成為應用程式中的所有旅行資料來源。當選取 TravelerManager 遊戲物件時,按一下 [新增元件 |新的指令碼。指令碼 TravelerManager 然後按一下 [建立和新增。這會建立新的指令碼來處理 TravelerManager 的邏輯。

差旅應用程式,您需要查看旅行世界。這麼做,請將新的遊戲物件加入至階層,並呼叫 TravelerTemplate。這是代表系統中每個旅行者顯示的物件。如果您熟悉 prefabs,使用您所設計的字元或其他資產存放區。我的應用程式中,我使用我會提供有趣的塊狀外觀全球資產存放區中找到一些低階多種字元。遊戲的任何物件將適用於 TravelerTemplate — prefab、 cube、 球體等等。TravelerTemplate 加入之後,停用遊戲物件,按一下 [偵測器] 視窗中的遊戲的物件名稱旁的核取方塊。這會讓 TravelerTemplate 從螢幕中消失。

最後,下列指令碼從 Unity VR 範例專案加入至您的專案︰ VREyeRaycast、 VRInput、 VRInteractiveItem 和 Reticle。我在本文中,使用,但是它們可以幫我很多程式碼撰寫應用程式的更新版本中,我將討論這些指令碼的確切的目的。

設定 TravelerManager 類別

在 Unity 中,按兩下 TravelerManager 指令碼在 MonoDevelop 或 Visual Studio 中開啟它。加入程式碼 [圖 1 元件。

[圖 1 TravelerManager 指令碼元件

public GameObject TravelerTemplate;
public List<Traveler> TravelerList;
void Awake () {
  TravelerList = new List<Traveler>();
}
public void LoadTravelers() {
  try
  {
    // Add GetTravelers here
  }
  catch(Exception ex)
  {
    Debug.Log(ex.ToString());
  }
}

TravelerTemplate 會保留用於建立在螢幕上的旅行者 Prefab。請記住,TravelerTemplate 可以是任何遊戲物件︰ cube、 球體,prefab 字元等等。此外,將此變數標示為公用,值可以設定在 Unity 設計工具,而不需要重新編寫程式碼 TravelerManager 讓設計小組控制旅行者範本。您也可以建立兩個清單中,一個用於儲存建立 GameObjects,和其他維護資料的每個旅行旅行者。接下來,您將新增載入旅行者和應用程式中不提供使用者輸入的機制。

連結按鈕

與設定基本世界的情況下,您需要向使用者取得應用程式進行收集的輸入。首先,請加入一個按鈕,將會觸發 TravelerManager 指令碼元件的 LoadTravelers 方法。為什麼不只是載入旅行者預設? 在此情況下,您會想要提供點的方向,讓使用者了解應用程式中的狀況。VR 的版本太新,讓使用者有預先定義的期望的情形發生時載入世界。UI 指示會幫助使用者引導及 acclimate 新世界的理想工具。

應用程式的根目錄上建立新的空白遊戲物件並呼叫它 GUI。此遊戲的物件會保留應用程式的 UI 元件。選取的 GUI 遊戲物件,加入新的畫布物件。畫布物件保留在螢幕上的所有 UI 元件。畫布物件建立之後,請移至 [偵測器,並將呈現的模式變更為全局空間。將卸離相機中的畫布,並讓內容在世界中,相對於放置卡住相機。現在將新的按鈕物件加入至畫布上。設定按鈕的文字為 「 負載旅行者 」。 變更按鈕的顯示轉換為 x = 1.87,y =。 05,z = 0 且寬度 = 9。高度 = 3,小數位數為 x = 0.25,y = 0.25,讓較小且使用者的檢視中的按鈕。

選取按鈕物件階層架構視窗,然後選取 (在 [偵測器] 視窗中) 上按一下清單上的加號,即可新增 click 事件。按鈕可以具有許多 click 事件,如有需要可讓您使用單一按鈕來觸發許多不同的事件。這是適用於觸發動作,然後停用或隱藏] 按鈕。每個 click 事件需要遊戲物件,以及提供給該遊戲的物件定義的函式。加入 click 事件之後, TravelerManager 遊戲將物件拖曳到 click 事件的物件欄位。一旦設定 TravelerManager,函式清單會顯示 LoadTravelers 方法。如果函式清單沒有顯示 LoadTravelers,您要建置 Visual Studio 中的程式碼專案,並再試一次選取 LoadTravelers。選取加號上按一下 [事件清單,以新增另一個事件。事件中,選取 GameObject.isActive,並保留空白核取方塊,按一下 [拖曳按鈕物件階層中的新物件欄位。這表示,當按一下按鈕時,Unity 會先執行 LoadTravelers,然後將按鈕 isActive 值為 false 表示隱藏按鈕。使用者的觀點而言,此按鈕會提供初始的指示。這些指示完成後,讓您將它們移除不再需要的指示。

從服務取得資料

現代化的應用程式從許多不同地方取得其資料。在此應用程式,旅行資料是由 travel.contoso.com API 提供。從 URL 取得資料是在 Unity 中,讓建置資料豐富體驗的應用程式非常簡單。TravelerManager 指令碼底端加入新的類別稱為旅行者。這會是您從旅行者服務的資料物件︰

public class Traveler{
  public int Id { get; set; }
  public string Name { get; set; }
  public string Title { get; set; }
  public string DepartureCity { get; set; }
  public string ArrivalCity { get; set; }
}

現在,您需要取得資料來填滿此類別。建立新的方法呼叫 GetTravelers LoadTravelers 下。這個方法將連線到 API 端點、 下載 API 所提供的 JSON 物件,然後將結果傳送到螢幕上顯示的資料要求結果的另一種方法︰

private IEnumerator GetTravelers(){
  var www = new WWW("https://travel.contoso.com/api/Traveler");
  yield return www;
  if (www.isDone)
  DisplayTravelers(www.text);
}

現在 LoadTravelers 的 「 在此新增 GetTravelers 」 的註解取代下列程式碼行︰

StartCoroutine(GetTravelers());

LoadTravelers 會呼叫 GetTravelers,會從 API 中提取資料。API 中的資料會傳送至 DisplayTravelers 方法,以便進一步處理。若要啟動這種情況,請使用 StartCoroutine,執行 GetTravelers 時不需鎖定的執行緒,直到執行完成為止,基本上凍結應用程式。建立此流程順利的程式碼是以確保應用程式不會降低影格數所採取的步驟。共同常式啟動並暫停每個畫面格,讓方法,而不用鎖定應用程式跨越多個框架的方法。StartCoroutine 是很適合用來執行可能是長時間執行,例如,等待要從 Web 服務傳回資料的方法。共同常式也可以用於大量物件上的方法。例如,如果旅行服務傳回數百個旅行的人-共同常式可能適用於呼叫動作之間所有的人,可能是要模擬的旅行者自己的外觀。如需有關共同常式的詳細資訊,請參閱 Unity 文件,網址 bit.ly/2ahxSBu

在 GetTravelers WWW 物件從 Unity 用於連接到提供的 URL。使用產生的關鍵字,直到它已完成檢查之後屬性會傳回 WWW 物件。從 URL 下載完成後,之後將會等於 true,這就將 DisplayTravelers 方法的 API 呼叫所傳回的文字。WWW 物件可用來張貼資料,以及取得資料,使應用程式可能會有在未來將加入旅行者功能。WWW 物件 Unity 站台上的完整文件位於 bit.ly/2alFoML

然後從 API 取得的結果,並將它轉換成使用者將能夠在應用程式中看到。您可以在不同的位置,在地圖上顯示旅行者之前,您需要在世界中建立這些位置。返回 Unity 設計工具,在階層樹狀結構的根目錄上建立空的遊戲物件。呼叫空遊戲物件位置的容器。在位置 X = 0,Y = 0,Z = 0 (世界的中心) 的位置容器設定。與所選位置容器,建立一些空白遊戲物件子系 fragments floor cube 發行項在啟動時建立。這些空白 GameObjects 代表機場。最低限度值,就像對應。在 DisplayTravelers 方法中,您可以查看旅行者的目的地城市並加以這些空白遊戲物件的其中一個比對。重新命名某些機場名稱空白遊戲物件。我使用 BWI、 MSP 和 SFO。若要完成機場的安裝程式,您會建立標記遊戲物件呼叫機場,讓您可以搜尋單一標記值來尋找所有機場項目。建立遊戲物件即可移至最上層的標籤方的 [偵測器物件,按一下 [標記] 下拉式清單並選取新標記。選取所有空的遊戲物件後,可讓您設定標記的所有物件一次,您可以建立新的標記。

加入程式碼所示 [圖 2 getTravelers 方法之後。

[圖 2 Unity 遊戲物件轉換成 JSON 旅行者物件

private void DisplayTravelers(string json){
  var jsonTravelers = new JSONObject(json);
  foreach (var jsonTraveler in jsonTravelers.list){
    // Convert jsonobject to traveler object
    Traveler traveler = ConvertJSON(jsonTraveler);
    GameObject travelerRep =
      Instantiate(TravelerTemplate,
      new Vector3(0f, 0.05f, 0f),
      TravelerTemplate.transform.rotation) as GameObject;
    travelerRep.name = traveler.Name;
    GameObject[] airports = GameObject.FindGameObjectsWithTag("Airport");
    foreach (var airport in airports) {
      if (airport.name == traveler.ArrivalCity) {
        travelerRep.transform.position =
          new Vector3(airport.transform.position.x, 0.05f,
          airport.transform.position.z);
    break;
  }
  }
  TravelerList.Add(traveler);
  }
}

使用免費的 JSONObject 指令碼從 Unity Asset store (bit.ly/2akN9p9),您就可以將旅行 C# 程式碼物件轉換成 JSON 文字。從該處使用具現化方法,建立一份 TravelerTemplate、 將它設定為預設的座標,並命名遊戲物件旅行者的名稱。這是重要的應用程式的更新版本中,它也用來識別旅行者從旅行者清單。接下來,您會找到所有遊戲物件標記為機場。這會提供物件的機場集合的物件,您可以瀏覽並尋找相符的物件相對應的機場位置放置旅行者的名稱。使用您稍早設定的標記,可讓您輕鬆地找到機場遊戲物件。在方法結束時,您將旅行者物件加入 TravelerList,讓您可以參考應用程式中稍後的旅行者資料。現在您有您的世界中顯示的旅行者的主要清單。它是時間,讓使用者選取旅行,因此您可以顯示該旅行者的詳細資訊。

選取旅行者

考慮到目前為止已建立的世界,是將來自 avatar 像旅行者說他們出差到語音泡泡詳細資料檢視的好方法。這會是一個有趣的方法時提供所選的旅行者非常接近的 UI 中顯示詳細資料。

VR,在選取旅行,即可查看物件。若要知道是否使用者正在看著旅行您需要使用 Raycast 物件。Raycast 是不可見的資料交換 emanates 相機中,可讓系統知道時與資料交換交集的 collider 元件在其上的物件。使用 Raycast 可讓使用者查看應該提供動作的物件時偵測到您的程式碼。您稍早匯入 VREyeRaycast 指令碼,它會實作在攝影機上建立 Raycast 所需的所有邏輯。加入主攝影機 VREyeRaycast 指令碼。您也需要將 Reticle 指令碼加入至主攝影機提供 UI 元件,以顯示使用者正在尋找的位置。

攝影機上 Raycast 安裝程式,您需要更新知道如何回應與 Raycast 交集 TravelerTemplate。若要這樣做,請加入 TravelerTemplate 中 VRInteractiveItem 指令碼。這是指令碼的快速安裝程式使用的事件,像是 Raycast 時,於物件,或 Raycast 離開物件,或甚至擷取 click 事件。

接下來,您會建立名為 TravelerInteraction,VRInteractiveItem 指令碼中實作事件的指令碼。此應用程式,您將使用 OnOver、 OnOut 和 OnClick 事件。當使用者查看旅行者物件時,旅行者將會變更外觀。當使用者離開看起來時,旅行者物件會改回來。當使用者查看旅行物件並按幾下時,會出現一個對話方塊,顯示旅行者的名稱、 標題和目的地。以下是 TravelerInteraction 指令碼︰

public class TravelerInteraction : MonoBehavior{
  public VRInteractiveItem InteractiveItem;
  public Shader Highlight;
  public TravelerManager TravelerManager;
  public Canvas Dialog;
  private Shader standardShader;
  private bool isDialogShowing;
}

VRInteractiveItem 被為了使用 VREyeRaycast 和指派 VRInteractiveItem 事件的方法。反白顯示是將用於使用者查看的物件時,反白顯示的旅行者物件的自訂著色器。自訂著色器將不會進入這裡,因為它們就很複雜的主題。若要深入了解,請參閱文件上的 Unity 站台 bit.ly/2agfb7q。TravelerManager 是 TravelerManager 指令碼,將會用來查詢有關所選的旅行者從 TravelerManager.TravelerList 集合的詳細資料的參考。最後一個公用屬性這裡是畫布遊戲物件,將用來顯示有關旅行時按下詳細資料。私用變數包含一般著色器停止查看旅行者和一個布林值,來判斷是否會顯示對話方塊之後,傳回旅行者物件的參考。

若要開始,已啟用將方法加入至 TravelerInteraction 類別,以連接至 TravelerInteraction 類別內方法 VRInteractiveItem 類別的事件︰

private void OnEnable(){
  InteractiveItem.OnOver += HandleOver;
  InteractiveItem.OnOut += HandleOut;
  InteractiveItem.OnClick += HandleClick;
}

HandleOver 使用自訂著色器變更的著色器的轉譯器元件。反白顯示屬性可讓使用 Unity Editor 旅行者反白顯示設定著色器設計工具。

private void HandleOver(){
  var renderer =
    gameObject.GetComponentInChildren<Renderer>();
  renderer.material.shader = Highlight;
}
private void HandleOut(){
  var renderer =
    gameObject.GetComponentInChildren<Renderer>();
  renderer.material.shader = standardShader;
}

當使用者查看旅行時,反白顯示著色器會套用至旅行者區別其他旅行者。這對使用者提供視覺化佇列,不論旅行者目前有使用者的視線。當使用者離開尋找 HandleOut 方法會將觸發,並傳回 standardShader 值旅行者。

現在,您要按一下旅行者,向使用者顯示的旅行者資訊。若要這樣做,首先您要加入 System.Linq using 陳述式中的 TravelerInteractive 類別,使您可以尋找旅行者遊戲物件的名稱。此外,您需要將文字中的元件] 對話方塊中,所使用的 UnityEngine.UI 命名空間中所示 [圖 3

[圖 3 顯示對話方塊,按一下物件時

private void HandleClick(){
  if (!isDialogShowing){
    var textComponent =
      Dialog.gameObject
      .GetComponentInChildren<Text>();
    var travelerInfo =
      TravelerManager.TravelerList.SingleOrDefault(x => x.Name ==
      this.gameObject.name);
    textComponent.text = String.Format(“{0}\n{1}”,
      travelerInfo.Name, travelerInfo.DestCity);
    Dialog.gameObject.SetActive(true);
    isDialogShowing = true;}
  else{
    Dialog.gameObject.SetActive(false);
    isDialogShowing = false;}
}

在此程式碼區塊中,首先決定是否對話方塊顯示與否。如果顯示,則會停用,以便隱藏檢視] 對話方塊。如果沒有顯示對話方塊,然後取得文字物件,並將其值設定為名稱和目的地的旅行者。當您最初建立旅行者物件時,您做為旅行名稱遊戲名稱物件用來代表旅行者。使用 LINQ,您可以搜尋旅行清單傳回旅行者的詳細資訊,該名稱。為了單純起見,您使用旅行者的名稱,但在大型且複雜的系統中,唯一的識別碼會是最理想的使用,因此每個旅行者是唯一的。快速提示在 LINQ 中,請記住,效能在建置行動的 VR 經驗的索引鍵。LINQ 能夠更精彩的內容,但是有額外負荷,因此要注意的其中 LINQ 可用,並判斷是否需要或只是為了方便起見。這裡 LINQ 用於以求簡單明瞭。一旦將文字設為 「 旅行者的名稱和目的地縣/市,對話方塊會啟用 (這是畫布物件) 來向使用者顯示文字。

總結

在本文中我檢閱建置 UI,從服務取得資料並進行互動 VR 世界中物件的基本概念。使用這些技術,ContosoTravel 世界增長到豐富的應用程式使用搜尋,可能更多層級來代表世界和動畫,讓 avatar (例如周圍自己) 的更生動的區域。探索這些今天建置 VR 世界並準備明天的混合式的實際使用者的基本概念。


Tim Kulp 是 bwell 巴爾的摩,好在原則工程師 他是 Web、 行動和 UWP 應用程式開發人員,以及作者、 複製、 dad 和 「 wannabe Mad 科學家 Maker。 」 找到他的 Twitter: @seccode 或透過 LinkedIn: linkedin.com/in/timkulp

感謝下列 Microsoft 技術專家來檢閱這份文件︰ Adam Tuliper