Hololens开发(9) Holograms 101:基本交互(凝视、手势、语音、空间声音)

先学习一下HoloToolkit的原理,但是直接看里面的脚本,相互直接有关系,稍微有点复杂,先从官方教程开始学习吧。使用最原始的脚本来进行交互。

官方提供了系列教程:

https://developer.microsoft.com/en-us/windows/mixed-reality/academy

交互部分:

https://developer.microsoft.com/en-us/windows/mixed-reality/holograms_101#chapter_7_-_holographic_fun

与Hololens的交互有:Gaze(凝视),Gesture(手势),Voice(语音)。

1.Gaze(凝视)

WorldCursor.cs

using UnityEngine;

public class WorldCursor : MonoBehaviour {

    private MeshRenderer meshRenderer;

	void Start () {
        meshRenderer = this.gameObject.GetComponentInChildren<MeshRenderer>();
    }
	
	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;
        }
	}
}
创建一个游戏物体(简单的圆)作为光标,添加脚本,移动摄像头。

当摄像头前有物体时光标显示在物体上,没有时则隐藏。

注意:作为光标的物体不能有Collider,不然会发生光标从碰撞处移动到摄像头,不断循环,因为光标显示后就会碰上自己了。

相当于头部(摄像头)就是鼠标本身,通过移动头部来控制光标的位置。


2.Gesture(手势)

GazeGestureManager.cs

using UnityEngine;
using UnityEngine.VR.WSA.Input;

public class GazeGestureManager : MonoBehaviour {

	public static GazeGestureManager Instance { get; set; }
    public GameObject FocusedObject { get; private set; }
    GestureRecognizer recognizer;
	void Awake () {
        Instance = this;
        recognizer = new GestureRecognizer();
        recognizer.TappedEvent += Recognizer_TappedEvent;
        recognizer.StartCapturingGestures();
    }

    private void Recognizer_TappedEvent(InteractionSourceKind source, int tapCount, Ray headRay)
    {
        SendSelect();
    }

    [ContextMenu("SendSelect")]
    void SendSelect()
    {
        if (FocusedObject != null)
        {
            FocusedObject.SendMessageUpwards("OnSelect", SendMessageOptions.DontRequireReceiver);
        }
    }

    void Update () {
        GameObject oldFolusObject = FocusedObject;
        var headPosition = Camera.main.transform.position;
        var gazeDirection = Camera.main.transform.forward;
        RaycastHit hitInfo;
        if (Physics.Raycast(headPosition, gazeDirection, out hitInfo))
        {
            FocusedObject = hitInfo.collider.gameObject;
        }
        else
        {
            FocusedObject = null;
        }
        if (FocusedObject != oldFolusObject)
        {
            recognizer.CancelGestures();
            recognizer.StartCapturingGestures();
        }
    }
}
通过GestureRecognizer识别点击事件(Tap),发送OnSelect消息给当前对焦的游戏物体。

添加到场景中的任何物体上都可以。

using UnityEngine;

public class Commands : MonoBehaviour {

	void OnSelect()
    {
        if (!this.GetComponent<Rigidbody>())
        {
            var rigidbody = this.gameObject.AddComponent<Rigidbody>();
            rigidbody.collisionDetectionMode = CollisionDetectionMode.Continuous;
        }
    }
}

接收GestureManager发送过来的OnSelect消息,在OnSelect中给游戏物体添加刚体,让物体下落。

[ContextMenu("SendSelect")]是为了在Unity中测试

3.Voice(声音)

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", SendReset);
        keywords.Add("Drop Sphere", SendDrop);
        keywordRecognizer = new KeywordRecognizer(keywords.Keys.ToArray());
        keywordRecognizer.OnPhraseRecognized += KeywordRecognizer_OnPhraseRecognized;
        keywordRecognizer.Start();
    }

    [ContextMenu("SendReset")]
    void SendReset()
    {
        this.BroadcastMessage("OnReset");
    }

    [ContextMenu("SendDrop")]
    void SendDrop()
    {
        var focosObject = GazeGestureManager.Instance.FocusedObject;
        if (focosObject != null)
        {
            focosObject.SendMessage("OnDrop", SendMessageOptions.DontRequireReceiver);
        }
    }

    private void KeywordRecognizer_OnPhraseRecognized(PhraseRecognizedEventArgs args)
    {
        System.Action keywordActoin;
        if(keywords.TryGetValue(args.text,out keywordActoin))
        {
            keywordActoin.Invoke();
        }
    }

    // Update is called once per frame
    void Update () {
		
	}
}

定义关键字,通过KeywordRecognizer识别语音,根据识别到的关键字发送相应的消息。

添加到场景中的任何物体上都可以。

修改Commands,添加相应的消息响应函数。

using UnityEngine;

public class Commands : MonoBehaviour {

	void OnSelect()
    {
        if (!this.GetComponent<Rigidbody>())
        {
            var rigidbody = this.gameObject.AddComponent<Rigidbody>();
            rigidbody.collisionDetectionMode = CollisionDetectionMode.Continuous;
        }
    }

    Vector3 originalPosition;
    void Start()
    {
        originalPosition = this.transform.localPosition;
    }

    void OnReset()
    {
        var rigidbody = this.GetComponent<Rigidbody>();
        if (rigidbody != null)
        {
            rigidbody.isKinematic = true;
            Destroy(rigidbody);
        }
        this.transform.localPosition = originalPosition;
    }

    void Ondrop()
    {
        OnSelect();
    }
}
因为用的是BroadcastMessage发送消息,要把游戏物体放到SpeechManager物体的子物体中,Reset才有反应。

后面还有,稍微看一下就好

4.空间声音

 按提示修改场景

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

public class SpatialSounds : MonoBehaviour {

    AudioSource impactAudoSource = null;
    AudioSource rollingAudioSource = null;
    bool rolling = false;

	// Use this for initialization
	void Start () {
        impactAudoSource = gameObject.AddComponent<AudioSource>();
        impactAudoSource.playOnAwake = false;
        impactAudoSource.spatialize = true;
        impactAudoSource.spatialBlend = 1.0f;
        impactAudoSource.dopplerLevel = 0.0f;
        impactAudoSource.rolloffMode = AudioRolloffMode.Logarithmic;
        impactAudoSource.maxDistance = 20f;

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

        impactAudoSource.clip = Resources.Load<AudioClip>("Impact");
        rollingAudioSource.clip = Resources.Load<AudioClip>("Rolling");
    }

    void OnCollisionEnter(Collision collision)
    {
        print("OnCollisionEnter:"+ collision);
        if (collision.relativeVelocity.magnitude >= 0.1f)
        {
            impactAudoSource.Play();
        }
    }

    void OnCollisionStay(Collision collision)
    {
        Rigidbody rigid = gameObject.GetComponent<Rigidbody>();
        if(!rolling && rigid.velocity.magnitude >= 0.01f)
        {
            rolling = true;
            rollingAudioSource.Play();
        }
        else if(rolling && rigid.velocity.magnitude < 0.01f)
        {
            rolling = false;
            rollingAudioSource.Stop();
        }
    }

    void OnCollisionExit(Collision collision)
    {
        print("OnCollisionExit:" + collision);
        if (rolling)
        {
            rolling = false;
            impactAudoSource.Stop();
            rollingAudioSource.Stop();
        }
    }
}
运行后的效果是点击,两个小球掉下来,滚动,响应的发出滚动的声音。停止滚动则停止声音。

5.空间地图

 按提示修改场景,添加 Spatial Mapping预设,修改场景中的脚本设置。

添加脚本 TapToPlaceParent.cs

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

public class TapToPlaceParent : MonoBehaviour {

    bool placing = false;
	// Use this for initialization
	void OnSelect(){
        placing = !placing;
        if (placing)
        {
            SpatialMapping.Instance.DrawVisualMeshes = true;
        }
        else
        {
            SpatialMapping.Instance.DrawVisualMeshes = false;
        }
	}
	
	// Update is called once per frame
	void Update () {
        if (placing)
        {
            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))
            {
                this.transform.parent.position = hitInfo.point;
                Quaternion toQuat = Camera.main.transform.localRotation;
                toQuat.x = 0;
                toQuat.z = 0;
                this.transform.parent.rotation = toQuat;
            }
        }
	}
}
没有实际测试过,总之应该是点击物体,地图显示。物体在地图上移动,再点击一次物体,固定物体。隐藏地图。

地图还不是很精细,应该有地方调整的。以后再说。


到这,基本就过一遍了。后面的教程是对这几方面的更加详细的展开说明。

按需要再学习吧。

先用当前的做一下测试。





  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值