MR Input 213:運動控制器

注意

混合實境學院教學課程的設計是以 HoloLens (第 1 代) 和混合實境沉浸式頭戴裝置為準。 因此,對於仍在尋找這些裝置開發指引的開發人員而言,我們覺得這些教學課程很重要。 這些教學課程不會使用用於 HoloLens 2 的最新工具組或互動進行更新。 系統會保留這些資訊,以繼續在支援的裝置上運作。 已針對 HoloLens 2 公佈一系列新的教學課程

混合實境世界中的動作控制器會新增另一層互動功能。 透過 運動控制器,我們可以以更自然的方式直接與對象互動,類似於我們在真實生活中的實體互動、增加您 app 體驗的沈浸和滿意程度。

在 MR Input 213 中,我們將藉由建立簡單的空間繪製體驗來探索動作控制器的輸入事件。 使用此應用程式時,使用者可以使用不同類型的筆刷和色彩,在三維空間中繪製。

本教學課程涵蓋的主題

MixedReality213 Topic1 MixedReality213 Topic2 MixedReality213 Topic3
控制器視覺效果 控制器輸入事件 自定義控制器和UI
瞭解如何在 Unity 的遊戲模式和運行時間中轉譯動作控制器模型。 瞭解不同類型的按鈕事件及其應用程式。 瞭解如何在控制器頂端重疊 UI 元素,或完全自定義它。

裝置支援

課程 HoloLens 沉浸式頭戴裝置
MR Input 213:運動控制器 ✔️

在您開始使用 Intune 之前

必要條件

請參閱 此頁面上的沉浸式頭戴式裝置安裝檢查清單。

專案檔

  • 下載 專案所需的檔案,並將檔案解壓縮到桌面。

注意

如果您想要先查看原始碼再下載,可以在 GitHub 上取得

Unity 設定

目標

  • 優化 Unity 以進行 Windows Mixed Reality 開發
  • 設定 Mixed Reality相機
  • 設定環境

指示

  • 啟動 Unity。

  • 選取 [開啟]。

  • 流覽至您的桌面,並尋找您先前未封 存的MixedReality213-master 資料夾。

  • 按一下 [選擇資料夾]

  • Unity 完成載入項目檔之後,您將能夠看到 Unity 編輯器。

  • 在 Unity 中,選取 [ 檔案 > 組建設定]。

    MR213_BuildSettings

  • 在 [平臺] 列表中選取 [通用 Windows 平台],然後按兩下 [切換平臺] 按鈕。

  • 將 [目標裝置] 設定為 [任何裝置]

  • 將組建類型設定為 D3D

  • 將 SDK 設定為 最新安裝

  • 檢查 Unity C# 專案

    • 這可讓您修改 Visual Studio 專案中的腳本檔案,而不需重建 Unity 專案。
  • 按兩下 [播放器設定]。

  • 在 [ 偵測器] 面板中,向下捲動到底部

  • 在 XR 設定中,檢查 支援的虛擬實境

  • 在 [虛擬實境 SDK] 底下,選取 [Windows Mixed Reality

    MR213_XRSettings

  • 關閉 [建置設定] 視窗。

專案結構

本教學課程使用 Mixed Reality Toolkit - Unity。 您可以在 此頁面找到版本。

ProjectStructure

已完成參考的場景

  • 您會在 [場景 ] 資料夾底下找到兩個已完成的 Unity 場景。
    • MixedReality213:具有單一筆刷的已完成場景
    • MixedReality213Advanced:使用多個筆刷進行進階設計的已完成場景

本教學課程的新場景設定

  • 在 Unity 中,按兩下 [檔案新增場景]>

  • 刪除 主要相機方向光線

  • [專案] 面板中,搜尋下列預製專案,並將下列預製專案拖曳至 [階層] 面板:

    • Assets/HoloToolkit/Input/Prefabs/MixedRealityCamera
    • Assets/AppPrefabs/Environment

    相機和環境

  • Mixed Reality 工具組中有兩個相機預製專案:

    • MixedRealityCamera.prefab:僅限相機
    • MixedRealityCameraParent.prefab:Camera + Teleportation + Boundary
    • 在本教學課程中,我們將使用 MixedRealityCamera 而不使用遠端傳送功能。 因此,我們新增了簡單的 環境 預製專案,其中包含基本樓層,讓用戶感覺有基礎。
    • 若要深入瞭解 使用MixedRealityCameraParent進行遠端傳送,請參閱 進階設計 - Teleportation 和locomotion

Skybox 設定

  • 按兩下 視窗 > 光源 > 設定

  • 按兩下 [Skybox 材質] 字段右側的圓形

  • 輸入 'gray' 並選取 SkyboxGray (Assets/AppPrefabs/Support/Materials/SkyboxGray.mat)

    設定 skybox

  • 核取 [Skybox ] 選項,以查看指派的灰色漸層 skybox

    切換 skybox 選項

  • 具有MixedRealityCamera、環境及灰色skybox的場景看起來會像這樣。

    MixedReality213 環境

  • 按兩下 [檔案儲存 > 場景] 作為

  • 任何名稱將場景儲存在 Scenes 資料夾下

第 1 章 - 控制器視覺效果

目標

  • 瞭解如何在 Unity 的遊戲模式和運行時間轉譯動作控制器模型。

Windows Mixed Reality 提供控制器視覺效果的動畫控制器模型。 您可以在應用程式中針對控制器視覺效果採取數種方法:

  • 預設值 - 使用預設控制器而不修改
  • 混合式 - 使用預設控制器,但自定義部分元素或重疊 UI 元件
  • 取代 - 針對控制器使用您自己的自定義 3D 模型

在本章中,我們將了解這些控制器自定義的範例。

指示

  • [專案] 面板中,於搜尋方塊中輸入 MotionControllers 。 您也可以在 Assets/HoloToolkit/Input/Prefabs/下找到它。
  • MotionControllers 預製專案拖曳至 [ 階層] 面板。
  • 按兩下 [階層] 面板中的MotionControllers預製專案。

MotionControllers 預製專案

MotionControllers 預製專案具有 MotionControllerVisualizer 腳本,可提供替代控制器模型的位置。 如果您指派自己的自定義 3D 模型,例如手部或字組,並檢查 [一律使用替代左/右模型],您會看到它們,而不是預設模型。 我們將在第 4 章中使用此位置,以筆刷取代控制器模型。

MR213_ControllerVisualizer

指示

  • [偵測器] 面板中,按兩下 MotionControllerVisualizer 腳本以查看Visual Studio中的程序代碼

MotionControllerVisualizer 腳本

MotionControllerVisualizerMotionControllerInfo 類別提供存取 & 修改預設控制器模型的方法。 MotionControllerVisualizer 訂閱 Unity 的 InteractionSourceDetected 事件,並在找到控制器模型時自動具現化它們。

protected override void Awake()
{
    ...
    InteractionManager.InteractionSourceDetected += InteractionManager_InteractionSourceDetected;
    InteractionManager.InteractionSourceLost += InteractionManager_InteractionSourceLost;
    ...
}

控制器模型會根據 glTF 規格來傳遞。 此格式已建立以提供通用格式,同時改善傳輸和解除封裝 3D 資產背後的程式。 在此情況下,我們需要在運行時間擷取和載入控制器模型,因為我們想要盡可能讓用戶體驗順暢,而且不保證使用者可能使用的動作控制器版本。 本課程透過 Mixed Reality Toolkit,使用 Khronos Group 的 UnityGLTF 專案版本。

一旦傳遞控制器,腳本就可以使用 MotionControllerInfo 來尋找特定控制器元素的轉換,讓他們可以正確地定位自己。

在稍後的章節中,我們將瞭解如何使用這些腳本將UI元素附加至控制器。

在某些腳本中,您會找到具有 #if 的程式代碼區塊 !UNITY_EDITORUNITY_WSA。 當您部署至 Windows 時,這些程式代碼區塊只會在 UWP 執行時間上執行。 這是因為 Unity 編輯器和 UWP 應用程式執行時間所使用的 API 集合不同。

  • 儲存 場景,然後按兩下 [播放 ] 按鈕。

您將能夠在頭戴式裝置中看到具有運動控制器的場景。 您可以查看按鈕點擊、遊戲桿移動和觸控板觸控醒目提示的詳細動畫。

MR213_Controller視覺效果預設值

第 2 章 - 將 UI 元素附加至控制器

目標

  • 瞭解動作控制器的元素
  • 瞭解如何將物件附加至控制器的特定部分

在本章中,您將瞭解如何將使用者介面元素新增至控制器,讓使用者可以隨時輕鬆存取及操作。 您也將瞭解如何使用觸控板輸入來新增簡單的色彩選擇器 UI。

指示

  • [專案] 面板中,搜尋 MotionControllerInfo 腳本。
  • 在搜尋結果中,按兩下 MotionControllerInfo 腳本以查看Visual Studio中的程式碼。

MotionControllerInfo 腳本

第一個步驟是選擇要附加UI之控制器的哪個元素。 這些元素定義於MotionControllerInfo.cs中的ControllerElementEnum中。

MR213 MotionControllerElements

  • 家中
  • 功能表
  • 把握
  • 遊戲桿
  • 選取
  • 觸控板
  • 指向姿勢 – 此元素代表指向正向的控制器提示。

指示

  • [專案] 面板中,搜尋 AttachToController 腳本。
  • 在搜尋結果中,按兩下 [AttachToController ] 腳本以查看 Visual Studio 中的程式碼。

AttachToController 腳本

AttachToController 腳本提供簡單的方法,將任何物件附加至指定的控制器手部和元素。

AttachElementToController () 中,

  • 使用 MotionControllerInfo.Handedness 檢查手部
  • 使用 MotionControllerInfo.TryGetElement () 取得控制器的特定元素
  • 從控制器模型擷取專案的轉換之後,將物件底下的 物件父代,並將物件的本機位置設定為零 & 旋轉。
public MotionControllerInfo.ControllerElementEnum Element { get { return element; } }

private void AttachElementToController(MotionControllerInfo newController)
{
     if (!IsAttached && newController.Handedness == handedness)
     {
          if (!newController.TryGetElement(element, out elementTransform))
          {
               Debug.LogError("Unable to find element of type " + element + " under controller " + newController.ControllerParent.name + "; not attaching.");
               return;
          }

          controller = newController;

          SetChildrenActive(true);

          // Parent ourselves under the element and set our offsets
          transform.parent = elementTransform;
          transform.localPosition = positionOffset;
          transform.localEulerAngles = rotationOffset;
          if (setScaleOnAttach)
          {
               transform.localScale = scale;
          }

          // Announce that we're attached
          OnAttachToController();
          IsAttached = true;
     }
}

使用 AttachToController 腳本最簡單的方式是繼承它,如同我們在 ColorPickerWheel 的情況下所做的一樣。只要覆寫 OnAttachToControllerOnDetachFromController 函式,即可在偵測到控制器/中斷連線時執行您的設定/分解。

指示

  • [專案] 面板中,輸入搜尋方塊 ColorPickerWheel。 您也可以在 [資產/AppPrefabs/] 下找到它。
  • ColorPickerWheel 預製專案拖曳至 [階層] 面板。
  • 按兩下 [階層] 面板中的 ColorPickerWheel 預製專案。
  • [偵測器] 面板中,按兩下 ColorPickerWheel 腳本以查看 Visual Studio 中的程式碼。

ColorPickerWheel prefab

ColorPickerWheel 腳本

由於 ColorPickerWheel 繼承 AttachToController,因此會顯示 [偵測器] 面板中的 [手部] 和 [元素]。 我們會將UI附加至左控制器上的 Touchpad 元素。

ColorPickerWheel 腳本

ColorPickerWheel 會覆寫 OnAttachToControllerOnDetachFromController 來訂閱輸入事件,此事件將在下一章中用於使用觸控板輸入的色彩選取。

public class ColorPickerWheel : AttachToController, IPointerTarget
{
    protected override void OnAttachToController()
    {
        // Subscribe to input now that we're parented under the controller
        InteractionManager.InteractionSourceUpdated += InteractionSourceUpdated;
    }

    protected override void OnDetachFromController()
    {
        Visible = false;

        // Unsubscribe from input now that we've detached from the controller
        InteractionManager.InteractionSourceUpdated -= InteractionSourceUpdated;
    }
    ...
}
  • 儲存 場景,然後按兩下 [播放 ] 按鈕。

將物件附加至控制器的替代方法

我們建議您的腳本繼承自 AttachToController ,並覆寫 OnAttachToController。 不過,這可能不一定可行。 替代方式是使用它作為獨立元件。 當您想要將現有的預製專案附加至控制器而不重構腳本時,這非常有用。 只要讓類別先等候IsAttached設定為 true,再執行任何設定。 若要這樣做,最簡單的方式是使用 『Start』 的協同程式。

private IEnumerator Start() {
    AttachToController attach = gameObject.GetComponent<AttachToController>();

    while (!attach.IsAttached) {
        yield return null;
    }

    // Perform setup here
}

第 3 章 - 使用觸控板輸入

目標

  • 瞭解如何取得觸控板輸入數據事件
  • 瞭解如何使用觸控板軸位置資訊,以取得您的應用程式體驗

指示

  • 在 [ 階層] 面板中,按兩下 [ColorPickerWheel]
  • [偵測器] 面板的 [ 動畫工具] 底下,按兩下 ColorPickerWheelController
  • 您將能夠看到已開啟 的動畫工具 索引標籤

使用 Unity 的動畫控制器顯示/隱藏 UI

為了以動畫顯示和隱藏 ColorPickerWheel UI,我們使用 Unity 的動畫系統。 將 ColorPickerWheelVisible 屬性設定為 true 或 false 觸發程式 Show and Hide 動畫觸發程式。 ShowHide 參數是在 ColorPickerWheelController 動畫控制器中定義。

Unity 動畫控制器

指示

  • 在 [ 階層] 面板中,選取 [ColorPickerWheel prefab]
  • [偵測器] 面板中,按兩下 ColorPickerWheel 腳本以查看 Visual Studio 中的程式碼

ColorPickerWheel 腳本

ColorPickerWheel 訂閱 Unity 的 InteractionSourceUpdated 事件,以接聽觸控板事件。

InteractionSourceUpdated () 中,腳本會先檢查以確保它:

  • 實際上是觸控板事件 (obj.state。touchpadTouched)
  • 源自左側控制器 (obj.state.source。手部)

如果兩者都成立,則觸控板位置 (obj.state。touchpadPosition) 指派給 selectorPosition

private void InteractionSourceUpdated(InteractionSourceUpdatedEventArgs obj)
{
    if (obj.state.source.handedness == handedness && obj.state.touchpadTouched)
    {
        Visible = true;
        selectorPosition = obj.state.touchpadPosition;
    }
}

Update () 中,根據 可見 屬性,它會觸發色彩選擇器動畫元件中的顯示和隱藏動畫觸發程式

if (visible != visibleLastFrame)
{
    if (visible)
    {
        animator.SetTrigger("Show");
    }
    else
    {
        animator.SetTrigger("Hide");
    }
}

Update () 中, selectorPosition 是用來在色彩滾輪的網格碰撞器上轉換光線,這會傳回 UV 位置。 接著,這個位置可用來尋找色彩滾輪紋理的圖元座標和色彩值。 這個值可透過 SelectedColor 屬性存取其他腳本。

色彩選擇器轉輪光線傳播

...
    // Clamp selector position to a radius of 1
    Vector3 localPosition = new Vector3(selectorPosition.x * inputScale, 0.15f, selectorPosition.y * inputScale);
    if (localPosition.magnitude > 1)
    {
        localPosition = localPosition.normalized;
    }
    selectorTransform.localPosition = localPosition;

    // Raycast the wheel mesh and get its UV coordinates
    Vector3 raycastStart = selectorTransform.position + selectorTransform.up * 0.15f;
    RaycastHit hit;
    Debug.DrawLine(raycastStart, raycastStart - (selectorTransform.up * 0.25f));

    if (Physics.Raycast(raycastStart, -selectorTransform.up, out hit, 0.25f, 1 << colorWheelObject.layer, QueryTriggerInteraction.Ignore))
    {
        // Get pixel from the color wheel texture using UV coordinates
        Vector2 uv = hit.textureCoord;
        int pixelX = Mathf.FloorToInt(colorWheelTexture.width * uv.x);
        int pixelY = Mathf.FloorToInt(colorWheelTexture.height * uv.y);
        selectedColor = colorWheelTexture.GetPixel(pixelX, pixelY);
        selectedColor.a = 1f;
    }
    // Set the selector's color and blend it with white to make it visible on top of the wheel
    selectorRenderer.material.color = Color.Lerp (selectedColor, Color.white, 0.5f);
}

第 4 章 - 覆寫控制器模型

目標

  • 瞭解如何使用自定義 3D 模型覆寫控制器模型。

MR213_BrushToolOverride

指示

  • 按兩下 [階層] 面板中的 [動作][控制器]。
  • 按兩下 [ 替代右控制器 ] 字段右側的圓形。
  • 輸入 'BrushController',然後從結果中選取預製專案。 您可以在 Assets/AppPrefabs/BrushController 下找到它。
  • 檢查 [一律使用替代右方模型]

MR213_BrushToolOverrideSlot

BrushController 預製物件不需要包含在 [階層] 面板中。 不過,若要簽出其子元件:

  • [專案] 面板中,輸入 BrushController ,並將 BrushController 預製專案拖曳至 [ 階層 ] 面板。

MR213_BrushTool_Prefab2

您會在 BrushController 中找到 Tip 元件。 我們將使用其轉換來啟動/停止繪圖線。

  • [階層] 面板刪除 BrushController
  • 儲存 場景,然後按下 播放 按鈕。 您將可以看到筆刷模型已取代右手動作控制器。

第 5 章 - 使用選取輸入繪製

目標

  • 瞭解如何使用 [選取] 按鈕事件來啟動和停止線條繪圖

指示

  • [專案] 面板中搜尋 BrushController 預製專案。
  • [偵測器] 面板中,按兩下 BrushController 腳本以查看 Visual Studio 中的程式碼

BrushController 腳本

BrushController 訂閱 InteractionManager 的 InteractionSourcePressedInteractionSourceReleased 事件。 觸發 InteractionSourcePressed 事件時,筆刷的 Draw 屬性會設定為 true;觸發 InteractionSourceReleased 事件時,筆刷的 Draw 屬性會設定為 false。

private void InteractionSourcePressed(InteractionSourcePressedEventArgs obj)
{
    if (obj.state.source.handedness == InteractionSourceHandedness.Right && obj.pressType == InteractionSourcePressType.Select)
    {
        Draw = true;
    }
}

private void InteractionSourceReleased(InteractionSourceReleasedEventArgs obj)
{
    if (obj.state.source.handedness == InteractionSourceHandedness.Right && obj.pressType == InteractionSourcePressType.Select)
    {
        Draw = false;
    }
}

Draw 設為 true 時,筆刷會在具現化的 Unity LineRenderer 中產生點。 此預製項目的參考會保留在筆刷的 Stroke Prefab 字段中。

private IEnumerator DrawOverTime()
{
    // Get the position of the tip
    Vector3 lastPointPosition = tip.position;

    ...

    // Create a new brush stroke
    GameObject newStroke = Instantiate(strokePrefab);
    LineRenderer line = newStroke.GetComponent<LineRenderer>();
    newStroke.transform.position = startPosition;
    line.SetPosition(0, tip.position);
    float initialWidth = line.widthMultiplier;

    // Generate points in an instantiated Unity LineRenderer
    while (draw)
    {
        // Move the last point to the draw point position
        line.SetPosition(line.positionCount - 1, tip.position);
        line.material.color = colorPicker.SelectedColor;
        brushRenderer.material.color = colorPicker.SelectedColor;
        lastPointAddedTime = Time.unscaledTime;
        // Adjust the width between 1x and 2x width based on strength of trigger pull
        line.widthMultiplier = Mathf.Lerp(initialWidth, initialWidth * 2, width);

        if (Vector3.Distance(lastPointPosition, tip.position) > minPositionDelta || Time.unscaledTime > lastPointAddedTime + maxTimeDelta)
        {
            // Spawn a new point
            lastPointAddedTime = Time.unscaledTime;
            lastPointPosition = tip.position;
            line.positionCount += 1;
            line.SetPosition(line.positionCount - 1, lastPointPosition);
        }
        yield return null;
    }
}

若要使用色彩選擇器滾輪 UI 中目前選取的色彩, BrushController 必須具有 ColorPickerWheel 對象的參考。 因為 BrushController prefab 會在運行時間具現化為取代控制器,所以場景中的任何物件參考都必須在運行時間設定。 在此情況下,我們使用 GameObject.FindObjectOfType 來尋找 ColorPickerWheel

private void OnEnable()
{
    // Locate the ColorPickerWheel
    colorPicker = FindObjectOfType<ColorPickerWheel>();

    // Assign currently selected color to the brush’s material color
    brushRenderer.material.color = colorPicker.SelectedColor;
    ...
}
  • 儲存 場景,然後按下 播放 按鈕。 您將能夠使用右側控制器上的 [選取] 按鈕繪製線條並繪製。

第 6 章 - 使用 Select 輸入繁衍物件

目標

  • 瞭解如何使用選取和掌握按鈕輸入事件
  • 瞭解如何具現化物件

指示

  • [專案] 面板中,於搜尋方塊中輸入 ObjectSpawner 。 您也可以在 Assets/AppPrefabs/ 下找到它

  • ObjectSpawner 預製專案拖曳至 [階層 ] 面板。

  • 按兩下 [階層] 面板中的 [ObjectSpawner]。

  • ObjectSpawner 有一個名為 Color Source 的字段。

  • 從 [ 階層] 面板,將 ColorPickerWheel 參考拖曳至此字段。

    物件繁衍器偵測器

  • 按兩下 [階層] 面板中的 [ObjectSpawner 預製專案]。

  • [偵測器] 面板中,按兩下 [ObjectSpawner 腳本] 以查看 Visual Studio 中的程式碼。

ObjectSpawner 腳本

ObjectSpawner 會將基本網格 (立方體、球體、圓柱) 的複本具現化到空間中。 偵測到 InteractionSourcePressed 時,它會檢查手部是否為 InteractionSourcePressType.GraspInteractionSourcePressType.Select 事件。

對於 擷取 事件,它會遞增目前網格類型的索引 (球體、立方體、圓柱)

private void InteractionSourcePressed(InteractionSourcePressedEventArgs obj)
{
    // Check handedness, see if it is left controller
    if (obj.state.source.handedness == handedness)
    {
        switch (obj.pressType)
        {
            // If it is Select button event, spawn object
            case InteractionSourcePressType.Select:
                if (state == StateEnum.Idle)
                {
                    // We've pressed the grasp - enter spawning state
                    state = StateEnum.Spawning;
                    SpawnObject();
                }
                break;

            // If it is Grasp button event
            case InteractionSourcePressType.Grasp:

                // Increment the index of current mesh type (sphere, cube, cylinder)
                meshIndex++;
                if (meshIndex >= NumAvailableMeshes)
                {
                    meshIndex = 0;
                }
                break;

            default:
                break;
        }
    }
}

針對 Select 事件,在 SpawnObject () 中,新的物件會具現化、未父代並釋放到世界。

private void SpawnObject()
{
    // Instantiate the spawned object
    GameObject newObject = Instantiate(displayObject.gameObject, spawnParent);
    // Detach the newly spawned object
    newObject.transform.parent = null;
    // Reset the scale transform to 1
    scaleParent.localScale = Vector3.one;
    // Set its material color so its material gets instantiated
    newObject.GetComponent<Renderer>().material.color = colorSource.SelectedColor;
}

ObjectSpawner 會使用 ColorPickerWheel 來設定顯示物件材質的色彩。 繁衍的物件會提供此材質的實例,以便保留其色彩。

  • 儲存 場景,然後按下 播放 按鈕。

您將能夠使用 [擷取] 按鈕來變更物件,並使用 [選取] 按鈕繁衍物件。

建置應用程式並將其部署至 Mixed Reality 入口網站

  • 在 Unity 中,選取 [ 檔案 > 建置設定]。
  • 按兩下 [新增開啟場景 ] 將目前的場景新增至 [建置中的場景]。
  • 按一下 [建置]
  • 建立名為 「App」 的新資料夾
  • 按兩下 [ 應用程式 ] 資料夾。
  • 按一下 [選擇資料夾]
  • 當 Unity 完成時,會出現 檔案總管 視窗。
  • 開啟 [應用程式 ] 資料夾。
  • 按兩下 YourSceneName.sln Visual Studio 方案檔案。
  • 使用 Visual Studio 中的頂端工具列,將目標從 [偵錯] 變更為 [發行 ],並將目標從 ARM 變更為 X64
  • 按兩下 [裝置] 按鈕旁的下拉式箭號,然後選取 [ 本機計算機]。
  • 單擊 [偵錯 - 啟動但不>偵錯] 功能表,或按 Ctrl + F5

現在,應用程式會建置並安裝在 Mixed Reality 入口網站中。 您可以在 Mixed Reality 入口網站中,透過 [開始] 功能表再次啟動它。

進階設計 - 使用星形配置筆刷工具

MixedReality213 Main

在本章中,您將瞭解如何使用自定義筆刷工具集合取代默認動作控制器模型。 如需參考,您可以在 Scenes 資料夾下找到已完成的場景 MixedReality213Advanced

指示

  • [專案] 面板中,在搜尋方塊中輸入 BrushSelector 。 您也可以在 Assets/AppPrefabs/ 下找到它

  • BrushSelector 預製專案拖曳到 [階層 ] 面板。

  • 針對組織,建立名為 Brushes 的空白 GameObject

  • 將下列預製專案從 [專案 ] 面板拖曳到 [筆刷]

    • Assets/AppPrefabs/BrushFat
    • Assets/AppPrefabs/BrushThin
    • Assets/AppPrefabs/Eraser
    • Assets/AppPrefabs/MarkerFat
    • Assets/AppPrefabs/MarkerThin
    • Assets/AppPrefabs/Pencil

    筆刷

  • 按兩下 [階層] 面板中的 [動作][控制器預製專案]。

  • [偵測器] 面板中,取消核取 [動作控制器可視化檢視] 上的 [一律使用替代右方模型]

  • 在 [ 階層] 面板中,按兩下 [BrushSelector]

  • BrushSelector 有一個名為 ColorPicker 的字段

  • 從 [階層] 面板,將 ColorPickerWheel 拖曳至 [偵測器] 面板中的 ColorPicker 字段。

    將 ColorPickerWheel 指派給筆刷選取器

  • 在 [ 階層] 面板的 [BrushSelector prefab] 底下,選取 [功能表 ] 物件。

  • [偵測器] 面板的 [LineObjectCollection ] 元件下,開啟 [物件 ] 陣列下拉式清單。 您會看到 6 個空白位置。

  • 從 [ 階層] 面板,依任何順序將 [ 筆刷 GameObject] 底下的每個預製專案拖曳到這些位置。 (確定您要從場景拖曳預製專案,而不是項目資料夾中的預製專案。)

筆刷選取器

BrushSelector prefab

由於 BrushSelector 繼承 AttachToController,因此它會在 [偵測器] 面板中顯示 [手部] 和 [元素] 選項。 我們選取 了 [向 右] 和 [ 指向姿勢 ],以將筆刷工具附加至右手控制器,並具有正向方向。

BrushSelector 會使用兩個公用程式:

  • 橢圓形:用來在空間中沿著橢圓形產生點。
  • LineObjectCollection:使用任何 Line 類別所產生的點來散發物件 (,例如 Ellipse) 。 這是我們將用來沿著橢圓形放置筆刷的內容。

結合時,這些公用程式可用來建立星形功能表。

LineObjectCollection 腳本

LineObjectCollection 具有沿著其線條散佈之物件的大小、位置和旋轉控件。 這適用於建立星形功能表,例如筆刷選取器。 若要建立筆刷的外觀,當筆刷接近中央選取的位置時相應增加, ObjectScale 曲線會尖峰在中央,並點選邊緣的點選。

BrushSelector 腳本

BrushSelector 的情況下,我們選擇使用程序動畫。 首先,筆刷模型會以 LineObjectCollection 腳本的省略號散發。 然後,每個筆刷都會根據 使用者的 DisplayMode 值,負責維護使用者手上的位置,這會根據選取範圍而變更。 由於用戶選取筆刷時,筆刷位置轉換的機率很高,因此我們選擇了程式性方法。 Mecanim 動畫可以正常處理中斷,但通常比簡單的 Lerp 作業更複雜。

BrushSelector 使用兩者的組合。 偵測到觸控板輸入時,筆刷選項會變成可見,並沿著星形功能表相應增加。 逾時期間之後 (,表示使用者已) 筆刷選項再次縮小選取範圍,只保留選取的筆刷。

可視化觸控板輸入

即使控制器模型已完全取代,在原始模型輸入上顯示輸入也很有説明。 這有助於實作用戶的實際動作。 針對 BrushSelector ,我們選擇在收到輸入時,讓觸控板短暫顯示。 這是藉由從控制器擷取 Touchpad 元素、將其材質取代為自定義材質,然後根據上次收到觸控板輸入將漸層套用至該材質的色彩來完成。

protected override void OnAttachToController()
{
    // Turn off the default controller's renderers
    controller.SetRenderersVisible(false);

    // Get the touchpad and assign our custom material to it
    Transform touchpad;
    if (controller.TryGetElement(MotionControllerInfo.ControllerElementEnum.Touchpad, out touchpad))
    {
        touchpadRenderer = touchpad.GetComponentInChildren<MeshRenderer>();
        originalTouchpadMaterial = touchpadRenderer.material;
        touchpadRenderer.material = touchpadMaterial;
        touchpadRenderer.enabled = true;
    }

    // Subscribe to input now that we're parented under the controller
    InteractionManager.InteractionSourceUpdated += InteractionSourceUpdated;
}

private void Update()
{
    ...
    // Update our touchpad material
    Color glowColor = touchpadColor.Evaluate((Time.unscaledTime - touchpadTouchTime) / touchpadGlowLossTime);
    touchpadMaterial.SetColor("_EmissionColor", glowColor);
    touchpadMaterial.SetColor("_Color", glowColor);
    ...
}

使用觸控板輸入進行筆刷工具選取

當筆刷選取器偵測到觸控板按下的輸入時,它會檢查輸入的位置,以判斷它是否位於左側或右邊。

具有 selectPressedAmount 的筆劃粗細

您可以在 InteractionSourcePressed () 中取得按下金額的模擬值,而不是 InteractionSourcePressType.Select事件。 此值可以在 InteractionSourceUpdated () 中擷 取。

private void InteractionSourceUpdated(InteractionSourceUpdatedEventArgs obj)
{
    if (obj.state.source.handedness == handedness)
    {
        if (obj.state.touchpadPressed)
        {
            // Check which side we clicked
            if (obj.state.touchpadPosition.x < 0)
            {
                currentAction = SwipeEnum.Left;
            }
            else
            {
                currentAction = SwipeEnum.Right;
            }

            // Ping the touchpad material so it gets bright
            touchpadTouchTime = Time.unscaledTime;
        }

        if (activeBrush != null)
        {
            // If the pressed amount is greater than our threshold, draw
            if (obj.state.selectPressedAmount >= selectPressedDrawThreshold)
            {
                activeBrush.Draw = true;
                activeBrush.Width = ProcessSelectPressedAmount(obj.state.selectPressedAmount);
            }
            else
            {
                // Otherwise, stop drawing
                activeBrush.Draw = false;
                selectPressedSmooth = 0f;
            }
        }
    }
}

橡皮擦腳本

Eraser 是一種特殊的筆刷類型,可覆寫基底 BrushDrawOverTime () 函式。 當 Draw 為 true 時,橡皮擦會檢查其提示是否與任何現有的筆刷筆劃交集。 如果這樣做,則會將它們新增至佇列,以縮小並刪除。

進階設計 - Teleportation 和 locomotion

如果您想要允許使用者使用遊戲桿來移動場景,請使用 MixedRealityCameraParent ,而不是 MixedRealityCamera。 您也需要新增 InputManagerDefaultCursor。 由於 MixedRealityCameraParent 已經包含 MotionControllersBoundary 作為子元件,因此您應該移除現有的 MotionControllers和環境預 製專案。

指示

  • 在 [ 階層] 面板中,刪除 MixedRealityCameraEnvironmentMotionControllers

  • [專案] 面板中,搜尋下列預製專案,然後將下列預製專案拖曳至 [階層 ] 面板:

    • Assets/AppPrefabs/Input/Prefabs/MixedRealityCameraParent
    • Assets/AppPrefabs/Input/Prefabs/InputManager
    • Assets/AppPrefabs/Input/Prefabs/Cursor/DefaultCursor

    Mixed Reality 相機父代

  • 在 [ 階層] 面板中,按兩下 [ 輸入管理員]

  • [偵測器] 面板中,向下捲動至 [簡單單一指標選取器 ] 區段

  • 從 [ 階層] 面板,將 DefaultCursor 拖曳至 [數據指標 ] 字段

    指派 DefaultCursor

  • 儲存 場景,然後按下 播放 按鈕。 您將能夠使用遊戲桿來向左/向右或電匯旋轉。

結束

這就是本教學課程的結尾! 您已了解︰

  • 如何在 Unity 的遊戲模式和運行時間中使用動作控制器模型。
  • 如何使用不同類型的按鈕事件及其應用程式。
  • 如何在控制器頂端重疊 UI 元素,或完全自定義它。

您現在已準備好開始使用運動控制器建立自己的沉浸式體驗!

已完成的場景

  • 在 Unity 的 [專案] 面板中,按兩下 [場景] 資料夾。
  • 您會發現兩個 Unity 場景 MixedReality213MixedReality213Advanced
    • MixedReality213:具有單一筆刷的已完成場景
    • MixedReality213Advanced:具有多個筆刷的已完成場景,具有選取按鈕的按下數量範例

另請參閱