HoloLens之Holograms 101学习

Chapter 1 —— “Holo” world

设置相机

  1. Main Camera位置设置(0,0,0);
  2. Clear Flags > Solid Color,Background > RGB(0,0,0,0);

设置场景

  1. 创建空GameObject命名为OrigamiCollection位置设置成(0,-0.5,2.0);
  2. 将Holgrams文件夹内的Stage,Sphere1,Sphere2预制体拖放到OrigamiCollection子级中;
  3. 删除Directional Lights,将Holgrams文件夹内的Lights拖放到层次结构视图中;
  4. 在PayerSettings中Capabilities > Microphone和SpatialPerception功能;

Chapter 1 —— Gaze 凝视

Objectives 目标

  1. 使用锁定的光标可视用户视线;

Instructions 说明

  1. 将Holgrams文件夹内的Cursor预制体拖放层次结构视图根目录中;
  2. 给Cursor对象添加C#脚本,命名为WorldCursor
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class WorldCursor : MonoBehaviour
{
    private MeshRenderer meshRenderer;

    // Use this for initialization
    void Start()
    {
        meshRenderer = this.GetComponentInChildren<MeshRenderer>();
    }

    // Update is called once per frame
    void Update()
    {
        var headPosition = Camera.main.transform.position;
        var gazeDirection = Camera.main.transform.forward;

        RaycastHit hitInfo;
        if(Physics.Raycast(headPosition,gazeDirection,out hitInfo))
        {
            meshRenderer.enabled = true;
            this.transform.position = hitInfo.point;
            this.transform.rotation = Quaternion.FromToRotation(Vector3.up, hitInfo.normal);
        }
        else
        {
            meshRenderer.enabled = false;
        }

    }
}

Chapter 1 —— Gestures 手势

Objectives 目标

  • 利用手势控制全息图;

Instructions 说明

  1. 选中OrigamiCollection对象添加C#脚本,命名为GazeGestureManager;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.WSA.Input;

public class GazeGestureManager : MonoBehaviour
{
    public static GazeGestureManager Instance { get; private set; }
    public GameObject FocusedObject { get; private set; }
    GestureRecognizer recognizer;

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

        //手势识别
        recognizer = new GestureRecognizer();

        //定阅手势按下之后的事件
        recognizer.Tapped += (args) =>
          {
              if (FocusedObject != null)
              {
                  //向聚焦的游戏对象发送OnSelect函数消息;
                  FocusedObject.SendMessageUpwards("OnSelect", SendMessageOptions.DontRequireReceiver);
              }
          };
        recognizer.StartCapturingGestures();
    }

    // Update is called once per frame
    void Update()
    {
        GameObject oldFocusObject = FocusedObject;

        var headPostion = Camera.main.transform.position;
        var gazeDirection = Camera.main.transform.forward;

        RaycastHit hitInfo;
        if (Physics.Raycast(headPostion, gazeDirection, out hitInfo))
        {
            //当射线击中的游戏对象作为聚焦对象;
            FocusedObject = hitInfo.collider.gameObject;
        }
        else
        {
            FocusedObject = null;
        }

        //如果聚焦对象改变了那么重新开始检测新的手势;
        if (FocusedObject != oldFocusObject)
        {
            recognizer.CancelGestures();
            recognizer.StartCapturingGestures();
        }
    }
}
  1. 另外,新建脚本SphereCommands,展开OrigamiCollection对象,选中Sphere1和Sphere2将SphereCommands脚本拖放到选中对象上;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SphereCommands : MonoBehaviour
{
    //当用户执行选择手势时,将会由GazeGestureManager调用
    void OnSelect()
    {
        //如果该对象没有刚体,则添加一个并启用物理特性;
        if(!this.GetComponent<Rigidbody>())
        {
            var rigidbody = this.gameObject.AddComponent<Rigidbody>();
            rigidbody.collisionDetectionMode = CollisionDetectionMode.Continuous;
        }
    }
}

Chapter 1 —— Voice 声音

Objectives 目标

使用“Reset world”将前面掉落的球体返回他的原始位置,然后使用“Drop sphere”使球体落下;

  1. 添加后台侦听的语音命令;
  2. 创建一个对语音命令有反应的全息图;

Instructions 说明

  1. 创建名为SpeechManager的脚本并挂载到OrigamiCollection对象上;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Windows.Speech;

public class SpeechManager : MonoBehaviour
{
    /// <summary>
    /// 单一关键词识别,关键词识别对象
    /// </summary>
    KeywordRecognizer keywordRecognizer = null;
    /// <summary>
    /// 存放关键词的字典
    /// </summary>
    Dictionary<string, System.Action> keywords = new Dictionary<string, System.Action>();

    // Use this for initialization
    void Start()
    {
        //向字典中添加关键词,key为关键词,value为一个匿名action
        keywords.Add("Reset world", () =>
        {
            this.BroadcastMessage("OnReset");
        });

        keywords.Add("Drop Sphere", () =>
        {
            //获取注视的物体
            var focusObject = GazeGestureManager.Instance.FocusedObject;
            //说了Drop Sphere关键词,并且存在注视物体,则发送消息到OnDrop函数;
            if (focusObject!=null)
            {
                focusObject.SendMessage("OnDrop", SendMessageOptions.DontRequireReceiver);
            }
        });

        //初始化识别对象;
        keywordRecognizer = new KeywordRecognizer(keywords.Keys.ToArray());
        //添加关键词代理事件;
        keywordRecognizer.OnPhraseRecognized += KeywordRecognizer_OnPhraseRecognized;
        //开始执行监听,非常重要;
        keywordRecognizer.Start();
    }

    private void KeywordRecognizer_OnPhraseRecognized(PhraseRecognizedEventArgs args)
    {
        System.Action keywordAction;
        //如果识别到的关键字在字典中,则调用该action;
        if(keywords.TryGetValue(args.text,out keywordAction))
        {
            keywordAction.Invoke();
        }
    }
}
  1. 打开挂载到Sphere1和Sphere2的脚本SphereCommands;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SphereCommands : MonoBehaviour
{
    Vector3 originalPosion;

    private void Start()
    {
        originalPosion = this.transform.localPosition;
    }

    //当用户执行选择手势时,将会由GazeGestureManager调用
    void OnSelect()
    {
        //如果该对象没有刚体,则添加一个并启用物理特性;
        if(!this.GetComponent<Rigidbody>())
        {
            var rigidbody = this.gameObject.AddComponent<Rigidbody>();
            rigidbody.collisionDetectionMode = CollisionDetectionMode.Continuous;
        }
    }

    void OnReset()
    {
        var rigidbody = this.GetComponent<Rigidbody>();
        if(rigidbody!=null)
        {
            //是否为刚体,如果启用该参数,对象不会被物理控制,只能通过直接设置位置,旋转和缩放来控制它;
            rigidbody.isKinematic = true;
            Destroy(rigidbody);
        }
        this.transform.localPosition = originalPosion;
    }

    void OnDrop()
    {
        OnSelect();
    }
}

Chapter 1 —— Spatial sound 空间声音

给应用程序添加音乐,并且在某些操作上触发音效,在3D空间中我们将使用Spatial Sound在特定的位置播放声音;

Objectives

  • Hear holograms in your world;

Instructions

  1. Editor > Project Settings >Audio;
  2. 找到Spatializer Plugin设置选择MS HRTF Spatializer;
  3. 从Holograms文件夹中将Ambience拖放到层次结构视图中的OrigamiCollection对象上,会自动挂上AudioSource并且AudioClip属性为Ambience;
  4. 选中OrigamiCollection并找到AudioSource,需要修改一些属性;
    • 选中Spatialize属性;
    • 选中Play On Awake;
    • Spatial Blend将滑块拖到右侧,拖动到3D;
    • 选中Loop属性;
    • 展开3D Sound Settings,将Doppler Level输入为0.1;
    • 将Volume Rolloff设置为Logarithmic Rolloff;
    • 将Max Distance设置为20;
  5. 设置完成后,创建一个名为SphereSounds的脚本,并把脚本分别挂到Sphere1和Sphere2上;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SphereSounds : MonoBehaviour
{
    AudioSource impactAudioSource = null;
    AudioSource rollingAudioSource = null;

    bool rolling = false;

    // Use this for initialization
    void Start()
    {
        //为音频设置属性;
        impactAudioSource = this.gameObject.AddComponent<AudioSource>();
        impactAudioSource.playOnAwake = false;
        impactAudioSource.spatialize = true;
        impactAudioSource.spatialBlend = 1.0f;
        impactAudioSource.loop = true;
        impactAudioSource.dopplerLevel = 0.0f;
        impactAudioSource.rolloffMode = AudioRolloffMode.Logarithmic;
        impactAudioSource.maxDistance = 20f;

        rollingAudioSource = this.gameObject.AddComponent<AudioSource>();
        rollingAudioSource.playOnAwake = false;
        rollingAudioSource.spatialize = true;
        rollingAudioSource.spatialBlend = 1.0f;
        rollingAudioSource.loop = true;
        rollingAudioSource.dopplerLevel = 0.0f;
        rollingAudioSource.rolloffMode = AudioRolloffMode.Logarithmic;
        rollingAudioSource.maxDistance = 20f;

        为音频添加音频片段;
        impactAudioSource.clip = Resources.Load<AudioClip>("Impact");
        rollingAudioSource.clip = Resources.Load<AudioClip>("Rolling");
    }

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.relativeVelocity.magnitude >= 0.1f)
        {
            impactAudioSource.Play();
        }
    }

    private void OnCollisionExit(Collision collision)
    {
        Rigidbody rigidbody = gameObject.GetComponent<Rigidbody>();
        //rigidbody.velocity刚体的线性速度,可以理解为给物体一个初速度;
        //rigidbody.velocity.magnitude
        if (!rolling && rigidbody.velocity.magnitude >= 0.01f)
        {
            rolling = true;
            rollingAudioSource.Play();
        }
        else if (rolling && rigidbody.velocity.magnitude < 0.01f)
        {
            rolling = false;
            rollingAudioSource.Stop();
        }
    }

    private void OnCollisionStay(Collision collision)
    {
        if (rolling == false)
        {
            rolling = false;
            impactAudioSource.Stop();
            rollingAudioSource.Stop();
        }
    }
}

Chapter 1 —— Spatial mapping 空间映射

使用Spatial mapping实现将游戏板旋转到现实世界的真实物体上;

Objectives 目标

  • 将真实世界带入虚拟世界;
  • 将全息放置你任意想放置的地方;

Instructions 说明

  1. 将Holograms文件中的Spatial Mapping预制体拖放到层次结构视图根目录中;
  2. 修改Spatial Mapping的属性;
    • 选中Draw Visual Meshes;
    • Draw Material属性可以改变绘制的材质;比如当前设置的为Wireframe,那么当绘制当前世界的时候将使用Wireframe材质;

接下来,需要展示的是如何将游戏对象OrigamiCollection移到另一个新的位置上;

  1. 创建一个名为TapToPlaceParent的脚本,并把脚本分别挂到OrigamiCollection的子级Stage上;

注:在GazeGestureManager.cs脚本中有定阅手势按下之后的事件向OnSelect函数发送消息;

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TapToPlaceParent : MonoBehaviour
{
    bool placing = false;

    private void OnSelect()
    {
        placing = !placing;
        if(placing)
        {
            SpatialMapping.Instance.drawVisualMeshes = true;
        }
        else
        {
            SpatialMapping.Instance.drawVisualMeshes = false;
        }

    }

    void Update()
    {
        if(placing)
        {
            var headPosition = Camera.main.transform.position;
            var gazeDirection = Camera.main.transform.forward;

            RaycastHit hitInfo;
            //这条射线只SpatialMapping.PhysicsRaycastMask层上会返回焦点
            if (Physics.Raycast(headPosition,gazeDirection,out hitInfo,30.0f, SpatialMapping.PhysicsRaycastMask))
            {
                this.transform.parent.position = hitInfo.point;
                Quaternion toQuat = Camera.main.transform.localRotation;
                toQuat.x = 0;
                toQuat.z = 0;
                this.transform.parent.rotation = toQuat;
            }
        }
    }
}

Chapter 1 —— Holographic fun

Objectives 目标

将Stage放置在地板上,然后使用手势将球体落下,撞击到Target的时候,会发生爆炸,Target会隐藏,并会出现一个洞,可以看见另一个世界;

Instructions 说明

  1. 将Holograms文件中的Underwrold预制体拖放到层次结构视图中作为OrigaminCollection的子级;
  2. 创建名为HitTarget的脚本,挂载到OrigamiCollection - Stage - Target中;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class HitTarget : MonoBehaviour
{
    public GameObject underworld;
    public GameObject objectToHide;

    private void OnCollisionEnter(Collision collision)
    {
        objectToHide.SetActive(false);
        underworld.SetActive(true);

        SpatialMapping.Instance.MappingEnabled = false;
    }
}
  1. 并将Stage拖入到HotTarget的ObjectToHide属性中,将Underworld拖入到HotTarget的Underworld属性中;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值