【Hololens开发】Holograms 101: Introduction with Device(使用Hololens)

本文我们将通过Unity创建一个包括:凝视(gaze)、手势(gesture)、语音输入(voice)、空间声音(spatial sound)和空间映射(spatial mapping)的完整的 Hololens 项目,并直接在 Hololens 上运行。

译者注:本文与 Holograms 101E 类似,区别在于部署部分,本文 101 是使用Hololens,101E 是使用模拟器。
点击查看原文

目录:

Chapter0 - 预先准备

开发环境

一个正确配置Hololens 开发环境的 Win10 PC机。

一个 Hololens 设备。

项目文件

  • 下载项目文件,要求使用 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 ,添加当前场景.
    • 单击 Build
    • 新建一个文件夹,命名为 APP
    • 单击选择 App 文件夹。
    • 单击 Select Folder 按钮。
    • 当Unity 完成 Building 的时候,会自动打开一个资源管理器。
    • 打开 APP 文件夹。
    • 打开生成的 Visual Studio Solution
    • 在VS工具栏中,把 target 从 Debug 改为 Release,从 ARM 改为 x86
  • 单击 本地计算机(Local Machine) 旁边的小箭头,将部署目标变为 Remote Machine
    • 设置 Address 值为你的 Hololens 的 name 或者 IP address。如果你不知道自己的 Hololens的 IP address,在 Hololens 中打开 Settings > Network & Internet > Advanced Options查看,或者直接询问 Cortana:”Hey Cortana, What’s my IP address?”。
    • 如果你是通过USB连接 Hololens 的,你应该将部署目标选择为 Device,而不是 Remote Machine
    • Authentication Mode 选择为 Universal
    • 点击 Select
  • 点击 调试(Debug) > 开始执行不调试(Debug without debugging)
  • 等待一会后,就可以戴上你的 Hololens 开始体验了。



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 中部署到 Hololens 中。



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 上进行测试。
  • 凝视一个纸球。
  • 做出选择的手势。观察效果,小球落下。

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 上进行测试。
  • 凝视一个小球。然后说:”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 上进行测试。
  • 四处走动,靠近远离感受音量的变化。

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 上进行测试。
  • 现在,你可以移动物体到一个新的位置:首先凝视物体,然后做出选择手势,把视点移动到新的位置后,再次做出选择手势即可。

Chapter7 - Holographic fun

本章将带你做出一个全息的地狱入口!(scary face)

  • Holograms 文件夹中拖拽 UnderworldOrigamiCollection 上作为子物体。
  • 新建一个脚本,命名为 HitTarget
  • HitTarget 脚本添加给 Target 物体。(就是那个蓝色的风扇)
  • 打开 HitTarget 脚本进行编辑,添加如下代码:
using UnityEngine;

public class HitTarget : MonoBehaviour
{
    // These public fields become settable properties in the Unity editor.
    public GameObject underworld;
    public GameObject objectToHide;

    // Occurs when this object starts colliding with another object
    void OnCollisionEnter(Collision collision)
    {
        // Hide the stage and show the underworld.
        objectToHide.SetActive(false);
        underworld.SetActive(true);

        // Disable Spatial Mapping to let the spheres enter the underworld.
        SpatialMapping.Instance.MappingEnabled = false;
    }
}
  • 再次在 Unity 中,选择 Target 物体。
  • 现在 TargetInspector 面板中有两个 公有变量 需要填充物体。
    • Underworld 物体拖拽到 Underworld 变量上。
    • Stage 物体拖拽到 Stage 变量上。
  • 导出项目,部署到 Hololens 上进行测试。
  • 现在,把 OrigamiCollection 放在地板上,然后使一个纸球掉落。
  • 当纸球击中蓝色的风扇时,会发生一次爆炸。之后,Collection消失了,取而代之的是一个地狱的大门!

The end

到此结束啦~

你已经学会了:

  • 如何在Unity里创建一个Hololens应用。
  • 如何使用gaze, gesture, voice, sounds, 以及 spatial mapping。
  • 如何导出项目、部署应用到 Hololens 上。

相信你现在已经准备好开发自己的 Hololens 应用啦~

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值