【Hololens開發】Holograms 101E: Introduction with Emulator(使用模擬器運行)

本文我們將通過Unity創建一個包括:凝視(gaze)、手勢(gesture)、語音輸入(voice)、空間聲音(spatial sound)和空間映射(spatial mapping)的完整的 Hololens 項目,並在 Hololens Emulator 模擬器上運行。

譯者注:本文與 Holograms 101 類似,區別在於部署部分,本文 101E 是使用模擬器運行,101 是直接使用 HoloLens。

點擊查看原文

目錄:

Chapter0 - 預先準備

開發環境

一個正確配置Hololens 開發環境的 Win10 PC機。

項目文件

  • 下載項目文件,要求使用 Unity5.5。
    • 如果你還在使用 Unity5.4, 試試這個
  • 解壓文件,並重命名文件夾爲 Origami

Chapter1 - “Holo” World

在這個章節中,我們將新建一個Unity項目,並且走一遍build和deploy的流程。

  • 準備
    • 打開 Unity
    • 點擊 Open
    • 找到你之前解壓的 Origami 文件夾。
    • 選中 Origami,然後點擊 Select Folder
    • 保存當前 Scene : File > Save Scene As
    • 把 Scene 命名爲 Origami ,然後單擊 Save
  • 設置 Main Camera
    • 選中 Main Camera
    • 設置其 Transform 組件中 Position 屬性爲 (0, 0, 0)。
    • 找到 Clear Flags 屬性, 把其值從 Skybox 改爲 Solid color
    • 修改 Background 屬性,其顏色 RGBA 爲(0, 0, 0, 0)。
    • 修改 Clipping Planes 屬性的 Near 值爲 0.85。(譯者注:參考Holograms 100)
  • 設置場景
    • Hierarchy 中, 單擊 Create > Create Empty
    • 右鍵單擊新的 GameObject 選擇 Rename。 將 GameObject 重命名爲 OrigamiCollection。
    • Project 面板中的 Holograms 文件夾裏:
      • 拖拽 Stage 進入 Hierarchy ,並作爲 OrigamiCollection 的子物體。
      • 拖拽 Sphere1 進入 Hierarchy, 並作爲 OrigamiCollection 的子物體。
      • 拖拽 Sphere2 進入 Hierarchy, 並作爲 OrigamiCollection 的子物體。
    • 右鍵單擊 Directional Light 物體,選擇 Delete 刪除。
    • Holograms 文件夾中, 拖拽 LightsHierarchy 面板的根部。
    • 選中 OrigamiCollection
    • 修改其 Transform 組件中的 Position 屬性爲 (0, -0.5, 2.0)。
    • 單擊 Play 按鈕,看看現在效果如何。
    • 再次單擊 Play 按鈕,退出預覽模式。
  • 導出項目到Visual Studio 2015
    • 選擇 File > Build Settings
    • 運行平臺Platform 修改爲 Windows Store 並且單擊 Switch Platform。.
    • 設置 SDKUniversal 10Build TypeD3D
    • UWP SDK 可以選 Latest installed(譯者注:最好與你裝VS2015時安裝的那個版本一致,否則VS會提示項目需要更新)
    • 勾上 Unity C# Projects
    • 單擊 Add Open Scenes ,添加當前場景.
    • 單擊 Player Settings….
      • Inspector 面板裏, 選中 Windows Store logo。接着展開 Publishing Settings
      • Capabilities 部分, 選中 MicrophoneSpatialPerception
  • 回到 Build Settings 窗口, 單擊 Build
  • 新建一個文件夾,命名爲 APP
  • 單擊選擇 App 文件夾。
  • 單擊 Select Folder 按鈕。
  • 當Unity 完成 Building 的時候,會自動打開一個資源管理器。
  • 打開 APP 文件夾。
  • 打開生成的 Visual Studio Solution
  • 在VS工具欄中,把 target 從 Debug 改爲 Release,從 ARM 改爲 x86
  • 單擊 本地計算機(Local Machine) 旁邊的小箭頭,將部署目標變爲 Hololens Emulator
  • 點擊 調試(Debug) > 開始執行不調試(Debug without debugging)
  • 等待一會後,Emulator 將會打開 Origami 項目。當第一次運行Emulator時,它可能需要加載15分鐘左右。當開始後,注意不要關閉它。



Hololens Emulator 效果圖

Chapter 2 - Gaze(凝視)

在這個章節中,我們將介紹Hololens中的三種交互方式之一,Gaze(凝視)。

  • Holograms 文件夾中拖拽 Cursor 物體進入 Hierarchy
  • Scripts 文件夾中新建一個 Script,命名爲 WorldCursor
  • WorldCursor 添加給 Cursor
  • 雙擊 WorldCursor ,在VS中編輯腳本。
  • 複製並粘貼以下腳本,並保存。
using UnityEngine;

public class WorldCursor : MonoBehaviour
{
    private MeshRenderer meshRenderer;

    // Use this for initialization
    void Start()
    {
        // Grab the mesh renderer that's on the same object as this script.
        meshRenderer = this.gameObject.GetComponentInChildren<MeshRenderer>();
    }

    // Update is called once per frame
    void Update()
    {
        // Do a raycast into the world based on the user's
        // head position and orientation.
        var headPosition = Camera.main.transform.position;
        var gazeDirection = Camera.main.transform.forward;

        RaycastHit hitInfo;

        if (Physics.Raycast(headPosition, gazeDirection, out hitInfo))
        {
            // If the raycast hit a hologram...
            // Display the cursor mesh.
            meshRenderer.enabled = true;

            // Move thecursor to the point where the raycast hit.
            this.transform.position = hitInfo.point;

            // Rotate the cursor to hug the surface of the hologram.
            this.transform.rotation = Quaternion.FromToRotation(Vector3.up, hitInfo.normal);
        }
        else
        {
            // If the raycast did not hit a hologram, hide the cursor mesh.
            meshRenderer.enabled = false;
        }
    }
}
  • 再次build該項目,並在 VS 中部署到模擬器運行查看效果(模擬器可以不用關閉~以便於下一次調試)。



Gaze 效果圖 注意圖中箭頭所指

Chapter3 - Gestures(手勢)

在本章節中,我們將添加手勢控制的功能。當用戶選擇到一個場景中的紙球時,我們將使紙球基於Unity中的物理引擎下落。

  • Scripts 文件夾下,新創建一個腳本 GazeGestureManager
  • 把腳本 GazeGestureManager 添加給 OrigamiCollection 物體。
  • 編輯 GazeGestureManager 腳本,添加以下代碼:
using UnityEngine;
using UnityEngine.VR.WSA.Input;

public class GazeGestureManager : MonoBehaviour
{
    public static GazeGestureManager Instance { get; private set; }

    // Represents the hologram that is currently being gazed at.
    public GameObject FocusedObject { get; private set; }

    GestureRecognizer recognizer;

    // Use this for initialization
    void Start()
    {
        Instance = this;

        // Set up a GestureRecognizer to detect Select gestures.
        recognizer = new GestureRecognizer();
        recognizer.TappedEvent += (source, tapCount, ray) =>
        {
            // Send an OnSelect message to the focused object and its ancestors.
            if (FocusedObject != null)
            {
                FocusedObject.SendMessageUpwards("OnSelect");
            }
        };
        recognizer.StartCapturingGestures();
    }

    // Update is called once per frame
    void Update()
    {
        // Figure out which hologram is focused this frame.
        GameObject oldFocusObject = FocusedObject;

        // Do a raycast into the world based on the user's
        // head position and orientation.
        var headPosition = Camera.main.transform.position;
        var gazeDirection = Camera.main.transform.forward;

        RaycastHit hitInfo;
        if (Physics.Raycast(headPosition, gazeDirection, out hitInfo))
        {
            // If the raycast hit a hologram, use that as the focused object.
            FocusedObject = hitInfo.collider.gameObject;
        }
        else
        {
            // If the raycast did not hit a hologram, clear the focused object.
            FocusedObject = null;
        }

        // If the focused object changed this frame,
        // start detecting fresh gestures again.
        if (FocusedObject != oldFocusObject)
        {
            recognizer.CancelGestures();
            recognizer.StartCapturingGestures();
        }
    }
}
  • 再新建一個腳本,命名爲 SphereCommands
  • 將腳本 SphereCommands 添加給 Sphere1Sphere2 物體。
  • 編輯 SPhereCommands 腳本,添加以下代碼:
using UnityEngine;

public class SphereCommands : MonoBehaviour
{
    // Called by GazeGestureManager when the user performs a Select gesture
    void OnSelect()
    {
        // If the sphere has no Rigidbody component, add one to enable physics.
        if (!this.GetComponent<Rigidbody>())
        {
            var rigidbody = this.gameObject.AddComponent<Rigidbody>();
            rigidbody.collisionDetectionMode = CollisionDetectionMode.Continuous;
        }
    }
}
  • 導出項目,部署到 Hololens Emulator上進行測試。
  • 凝視一個紙球。
  • 按下 Spacebar 鍵,用來模擬選擇的手勢。觀察效果,小球落下。

Chapter4 - Voice 聲音控制

在本章節中,我們將添加兩種聲音控制命令

  • “Reset World”:讓已經降落的小球回到一開始的位置。
  • “Drop Sphere”:讓小球降落

  • 首先,新建一個腳本 SpeechManager
  • 把腳本 SpeechManager 添加給 OrigamiCollection 物體。
  • 打開腳本 SpeechManager 並添加如下代碼:
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Windows.Speech;

public class SpeechManager : MonoBehaviour
{
    KeywordRecognizer keywordRecognizer = null;
    Dictionary<string, System.Action> keywords = new Dictionary<string, System.Action>();

    // Use this for initialization
    void Start()
    {
        keywords.Add("Reset world", () =>
        {
            // Call the OnReset method on every descendant object.
            this.BroadcastMessage("OnReset");
        });

        keywords.Add("Drop Sphere", () =>
        {
            var focusObject = GazeGestureManager.Instance.FocusedObject;
            if (focusObject != null)
            {
                // Call the OnDrop method on just the focused object.
                focusObject.SendMessage("OnDrop");
            }
        });

        // Tell the KeywordRecognizer about our keywords.
        keywordRecognizer = new KeywordRecognizer(keywords.Keys.ToArray());

        // Register a callback for the KeywordRecognizer and start recognizing!
        keywordRecognizer.OnPhraseRecognized += KeywordRecognizer_OnPhraseRecognized;
        keywordRecognizer.Start();
    }

    private void KeywordRecognizer_OnPhraseRecognized(PhraseRecognizedEventArgs args)
    {
        System.Action keywordAction;
        if (keywords.TryGetValue(args.text, out keywordAction))
        {
            keywordAction.Invoke();
        }
    }
}
  • 打開腳本 SphereCommands ,更新代碼如下:
using UnityEngine;

public class SphereCommands : MonoBehaviour
{
    Vector3 originalPosition;

    // Use this for initialization
    void Start()
    {
        // Grab the original local position of the sphere when the app starts.
        originalPosition = this.transform.localPosition;
    }

    // Called by GazeGestureManager when the user performs a Select gesture
    void OnSelect()
    {
        // If the sphere has no Rigidbody component, add one to enable physics.
        if (!this.GetComponent<Rigidbody>())
        {
            var rigidbody = this.gameObject.AddComponent<Rigidbody>();
            rigidbody.collisionDetectionMode = CollisionDetectionMode.Continuous;
        }
    }

    // Called by SpeechManager when the user says the "Reset world" command
    void OnReset()
    {
        // If the sphere has a Rigidbody component, remove it to disable physics.
        var rigidbody = this.GetComponent<Rigidbody>();
        if (rigidbody != null)
        {
            DestroyImmediate(rigidbody);
        }

        // Put the sphere back into its original local position.
        this.transform.localPosition = originalPosition;
    }

    // Called by SpeechManager when the user says the "Drop sphere" command
    void OnDrop()
    {
        // Just do the same logic as a Select gesture.
        OnSelect();
    }
}
  • 導出項目,部署到 Hololens Emulator上進行測試。
  • 調整視點,凝視一個小球。然後說:”Drop Sphere!”
  • 說 “Reset World” 讓小球回到原來的位置。

Chapter5 - Spatial sound 空間聲音

在本章節中,我們將爲應用添加音樂,並給關鍵動作添加音效。我們將使用 Spatial sound 來給聲音一個三維空間中具體的方位。

  • 在 Unity Editor 中,選擇 Edit > Project Settings > Audio
  • 找到 Spatializer Plugin 選項並選擇 MS HRTF Spatializer
  • Holograms 文件夾拖拽 Ambience 物體到 Hierarchy 面板中的 OrigamiCollection 物體上。
  • 選中 OrigamiCollection 物體,找到 Audio Source 組件,修改以下屬性:
    • 勾選 Spatialize
    • 勾選 Play on Awake
    • Spatial Blend 的滑塊拖到最右,使其值爲 3D
    • 勾選 Loop
    • 展開 3D Sound Settings,把 Doppler Level 的值修改爲 0.1
    • Volume Rolloff 設置爲 Logarithmic Rolloff
    • Max Distance 設置爲 20
  • 新建一個腳本命名爲 SphereSounds
  • 把腳本 SphereSounds 添加給 Sphere1Sphere2
  • 打開腳本添加如下代碼:
using UnityEngine;

public class SphereSounds : MonoBehaviour
{
    AudioSource audioSource = null;
    AudioClip impactClip = null;
    AudioClip rollingClip = null;

    bool rolling = false;

    void Start()
    {
        // Add an AudioSource component and set up some defaults
        audioSource = gameObject.AddComponent<AudioSource>();
        audioSource.playOnAwake = false;
        audioSource.spatialize = true;
        audioSource.spatialBlend = 1.0f;
        audioSource.dopplerLevel = 0.0f;
        audioSource.rolloffMode = AudioRolloffMode.Logarithmic;
        audioSource.maxDistance = 20f;

        // Load the Sphere sounds from the Resources folder
        impactClip = Resources.Load<AudioClip>("Impact");
        rollingClip = Resources.Load<AudioClip>("Rolling");
    }

    // Occurs when this object starts colliding with another object
    void OnCollisionEnter(Collision collision)
    {
        // Play an impact sound if the sphere impacts strongly enough.
        if (collision.relativeVelocity.magnitude >= 0.1f)
        {
            audioSource.clip = impactClip;
            audioSource.Play();
        }
    }

    // Occurs each frame that this object continues to collide with another object
    void OnCollisionStay(Collision collision)
    {
        Rigidbody rigid = this.gameObject.GetComponent<Rigidbody>();

        // Play a rolling sound if the sphere is rolling fast enough.
        if (!rolling && rigid.velocity.magnitude >= 0.01f)
        {
            rolling = true;
            audioSource.clip = rollingClip;
            audioSource.Play();
        }
        // Stop the rolling sound if rolling slows down.
        else if (rolling && rigid.velocity.magnitude < 0.01f)
        {
            rolling = false;
            audioSource.Stop();
        }
    }

    // Occurs when this object stops colliding with another object
    void OnCollisionExit(Collision collision)
    {
        // Stop the rolling sound if the object falls off and stops colliding.
        if (rolling)
        {
            rolling = false;
            audioSource.Stop();
        }
    }
}
  • 導出項目,部署到 Hololens Emulator上進行測試。
  • 戴上耳機體驗3D效果,靠近遠離感受音量的變化。

Chapter6 - Spatial mapping 空間映射

在本章節,我們將通過 Spatial mapping 去實現把場景中的物體放置在真實世界的真實物體之上。

  • Holograms 文件夾中的 Spatial Mapping 拖入 Hierarchy
  • 選中 Spatial Mapping,修改其以下屬性:
    • 勾選 Draw Visual Meshes
    • Draw Material 指定 wireframe 材質。
  • 導出項目,部署到 Hololens Emulator上進行測試。
  • 當應用運行時,可以觀察到一個提前掃描過的房間的網格以 wireframe 材質的樣式出現了。
  • 觀察小球是如何掉出桌子,掉到地上的。

現在我將教給你如何讓 ORIgamiCollections 移動到一個新的位置。

  • 新建一個腳本,命名爲 TapToPlaceParent
  • 把腳本拖給 Stage 物體。
  • 打開腳本進行編輯,添加如下代碼:
using UnityEngine;

public class TapToPlaceParent : MonoBehaviour
{
    bool placing = false;

    // Called by GazeGestureManager when the user performs a Select gesture
    void OnSelect()
    {
        // On each Select gesture, toggle whether the user is in placing mode.
        placing = !placing;

        // If the user is in placing mode, display the spatial mapping mesh.
        if (placing)
        {
            SpatialMapping.Instance.DrawVisualMeshes = true;
        }
        // If the user is not in placing mode, hide the spatial mapping mesh.
        else
        {
            SpatialMapping.Instance.DrawVisualMeshes = false;
        }
    }

    // Update is called once per frame
    void Update()
    {
        // If the user is in placing mode,
        // update the placement to match the user's gaze.

        if (placing)
        {
            // Do a raycast into the world that will only hit the Spatial Mapping mesh.
            var headPosition = Camera.main.transform.position;
            var gazeDirection = Camera.main.transform.forward;

            RaycastHit hitInfo;
            if (Physics.Raycast(headPosition, gazeDirection, out hitInfo,
                30.0f, SpatialMapping.PhysicsRaycastMask))
            {
                // Move this object's parent object to
                // where the raycast hit the Spatial Mapping mesh.
                this.transform.parent.position = hitInfo.point;

                // Rotate this object's parent object to face the user.
                Quaternion toQuat = Camera.main.transform.localRotation;
                toQuat.x = 0;
                toQuat.z = 0;
                this.transform.parent.rotation = toQuat;
            }
        }
    }
}
  • 導出項目,部署到 Hololens Emulator上進行測試。
  • 現在,你可以移動物體到一個新的位置:首先凝視物體,然後使用選擇手勢(Spacebar),把視點移動到新的位置後,再次使用選擇手勢即可。

The end

到此結束啦~

你已經學會了:

  • 如何在Unity裏創建一個Hololens應用。
  • 如何使用gaze, gesture, voice, sounds, 以及 spatial mapping。
  • 如何導出項目、部署應用到模擬器上。

相信你現在已經準備好開發自己的 Hololens 應用啦~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章