Unity.VR.01简单交互

来源:互联网 发布:阿里云服务器的优势 编辑:程序博客网 时间:2024/06/08 03:13
【学习项目为Unity官方的VR Smaples,可以在Asset Store下载】
【参考官方学习教程Unity.learn.Virtual Reality】

VRSmaples中,有一个简单且可扩展的轻量级用户交互框架,包括3个主要脚本,分别是:
VRInput    
定义点击、双击、滑动等事件,判定用户从设备的输入,来执行相应的事件。
using System;using UnityEngine;namespace VRStandardAssets.Utils{  // 在其它类中可以注册这些事件,能够满足大部分VR Games的输入需求  // 所有场景中必须使用这个类, 可以挂在摄像机上  public class VRInput : MonoBehaviour  {  //枚举类型:滑动方向  public enum SwipeDirection  {  NONE,  UP,  DOWN,  LEFT,  RIGHT  };  public event Action OnSwipe; // 滑动  public event Action OnClick; // 单击  public event Action OnDown; // 按下键  public event Action OnUp; // 松开键  public event Action OnDoubleClick; // 双击  public event Action OnCancel; // 取消键  [SerializeField] private float m_DoubleClickTime = 0.3f; //双击的最大时间间隔  [SerializeField] private float m_SwipeWidth = 0.3f; //用来判定是垂直滑动还是水平滑动  private Vector2 m_MouseDownPosition; // Fire1(鼠标左键)点击时的屏幕坐标点  private Vector2 m_MouseUpPosition; //  Fire1(鼠标左键)松开时的屏幕坐标点  private float m_LastMouseUpTime; // 上一帧鼠标键松开的时间点  private float m_LastHorizontalValue; // 上一次键盘输入的水平方向移动的值,用来检测是否为有效滑动值  private float m_LastVerticalValue; //  上一次键盘输入的垂直方向移动的值,用来检测是否为有效滑动值  //鼠标双击的时间  public float DoubleClickTime{ get { return m_DoubleClickTime; } }  //在Update中检测输入  private void Update()  {  CheckInput();  }  private void CheckInput()  {  // 滑动枚举类型默认值设为NONE(没有滑动)  SwipeDirection swipe = SwipeDirection.NONE;  if (Input.GetButtonDown("Fire1"))  {  // 按下Fire1键时,记下屏幕坐标值(x,y)  m_MouseDownPosition = new Vector2(Input.mousePosition.x, Input.mousePosition.y);  // 如果OnDown被订阅了,执行OnDown   if (OnDown != null)  OnDown();  }  //这个if语句@是用来在松开Fire1键后,获取点击的坐标值  if (Input.GetButtonUp ("Fire1"))  {  // 松开Fire1键时,记下屏幕坐标值(x,y)  m_MouseUpPosition = new Vector2 (Input.mousePosition.x, Input.mousePosition.y);  // 通过DetectSwipe方法进行判定,确定滑动的方向  swipe = DetectSwipe ();  }  // 如果鼠标没有输入,则启动另一方法,进行键盘输入的滑动方向判定  if (swipe == SwipeDirection.NONE)  swipe = DetectKeyboardEmulatedSwipe();  // 如果OnSwipe被订阅了,执行OnSwipe  if (OnSwipe != null)  OnSwipe(swipe);  //这个if语句@是用来在松开Fire1键后,基于之前收集的信息来触发事件  if(Input.GetButtonUp ("Fire1"))  {  // 如果OnUp被订阅了,执行OnUp  if (OnUp != null)  OnUp();  //通过两次松开鼠标的时间差来判定双击是否有效  if (Time.time - m_LastMouseUpTime < m_DoubleClickTime)  {  // 如果OnDoubleClick被订阅了,执行OnDoubleClick  if (OnDoubleClick != null)  OnDoubleClick();  }  else  {  //双击时间超过0.3秒,则判定为单击  //如果OnClick被订阅了,执行OnClick  if (OnClick != null)  OnClick();   }  //用m_LastMouseUpTime记下这一次松开鼠标键的时间,用来在下一次再松开鼠标时计算时间间隔  m_LastMouseUpTime = Time.time;  }  //按下Cancel键,如果OnCancel被订阅了,执行OnCancel  if (Input.GetButtonDown("Cancel"))  {  if (OnCancel != null)  OnCancel();  }  }  //通过鼠标的输入判定滑动的方向  private SwipeDirection DetectSwipe ()  {  // 获取鼠标松开时坐标点指向按下时坐标点的向量,进行归一化,得到一个单位向量  Vector2 swipeData = (m_MouseUpPosition - m_MouseDownPosition).normalized;  // 这个单位向量的x坐标小于0.3,则进行垂直方向的滑动  bool swipeIsVertical = Mathf.Abs (swipeData.x) < m_SwipeWidth;  // 这个单位向量的y坐标小于0.3,则进行水平方向的滑动  bool swipeIsHorizontal = Mathf.Abs(swipeData.y) < m_SwipeWidth;  // y大于0,向上滑动  if (swipeData.y > 0f && swipeIsVertical)  return SwipeDirection.UP;  // y小于0,向下滑动  if (swipeData.y < 0f && swipeIsVertical)  return SwipeDirection.DOWN;  // x大于0,向右滑动  if (swipeData.x > 0f && swipeIsHorizontal)  return SwipeDirection.RIGHT;  // x小于0,向左滑动  if (swipeData.x < 0f && swipeIsHorizontal)  return SwipeDirection.LEFT;  // 以上条件都不满足时,则无滑动  return SwipeDirection.NONE;  }  //通过键盘的输入判定滑动的方向  private SwipeDirection DetectKeyboardEmulatedSwipe ()  {  // 记下键盘输入的水平轴、垂直轴的移动值  float horizontal = Input.GetAxis ("Horizontal");  float vertical = Input.GetAxis ("Vertical");  // 输入值太小,则不是有效输入  bool noHorizontalInputPreviously = Mathf.Abs (m_LastHorizontalValue) < float.Epsilon;  bool noVerticalInputPreviously = Mathf.Abs(m_LastVerticalValue) < float.Epsilon;  // 记下这次的输入值,用于在下一次判定上一次的键盘是否进行了有效输入  m_LastHorizontalValue = horizontal;  m_LastVerticalValue = vertical;  // 垂直输入大于0且上一次没有输入,则向上滑动  if (vertical > 0f && noVerticalInputPreviously)  return SwipeDirection.UP;  // 垂直输入小于0且上一次没有输入,则向下滑动  if (vertical < 0f && noVerticalInputPreviously)  return SwipeDirection.DOWN;  // 水平输入大于0且上一次没有输入,则向右滑动  if (horizontal > 0f && noHorizontalInputPreviously)  return SwipeDirection.RIGHT;  // 水平输入大于0且上一次没有输入,则向左滑动  if (horizontal < 0f && noHorizontalInputPreviously)  return SwipeDirection.LEFT;  // 没有输入,则不滑动  return SwipeDirection.NONE;  }  private void OnDestroy()  {  // 这个对象被摧毁时,务必置空所有事件  OnSwipe = null;  OnClick = null;  OnDoubleClick = null;  OnDown = null;  OnUp = null;  }  }}
VREyeRaycaster
发出一条射线,通过射线获取到一个可交互物体;
编写方法订阅VRInput中的相应事件,对这个可交互物体(InteractiveItem)进行操作,执行该物体脚本(VRInteractiveItem)中的方法。
using System;using UnityEngine;namespace VRStandardAssets.Utils{    //投射一条射线来尝试获取一个可交互的对象并与之进行交互    //这个脚本一般放在摄像机上    public class VREyeRaycaster : MonoBehaviour    {        public event Action OnRaycasthit;                   // 凝视到一个带有碰撞体的对象时进行调用        [SerializeField] private Transform m_Camera;        [SerializeField] private LayerMask m_ExclusionLayers;           // 排除射线的蒙版层        [SerializeField] private Reticle m_Reticle;                     // 准心        [SerializeField] private VRInput m_VrInput;                     // 脚本VRInput        [SerializeField] private bool m_ShowDebugRay;                   // 是否需要调试线        [SerializeField] private float m_DebugRayLength = 5f;           // 调试光线长度        [SerializeField] private float m_DebugRayDuration = 1f;         // 调试光线显示时间        [SerializeField] private float m_RayLength = 500f;              // 光线投射的长度                private VRInteractiveItem m_CurrentInteractible;                //当前交互物体        private VRInteractiveItem m_LastInteractible;                   //上个交互物体        // 使其它类获取当前的交互对象        public VRInteractiveItem CurrentInteractible        {            get { return m_CurrentInteractible; }        }                private void OnEnable()        {            m_VrInput.OnClick += HandleClick;            m_VrInput.OnDoubleClick += HandleDoubleClick;            m_VrInput.OnUp += HandleUp;            m_VrInput.OnDown += HandleDown;        }        private void OnDisable ()        {            m_VrInput.OnClick -= HandleClick;            m_VrInput.OnDoubleClick -= HandleDoubleClick;            m_VrInput.OnUp -= HandleUp;            m_VrInput.OnDown -= HandleDown;        }        //不断地投射射线来尝试获取交互对象        private void Update()        {            EyeRaycast();        }              private void EyeRaycast()        {            // 绘制一条蓝色的调试线            if (m_ShowDebugRay)            {                Debug.DrawRay(m_Camera.position, m_Camera.forward * m_DebugRayLength, Color.blue, m_DebugRayDuration);            }            // 生成一条光线,从相机坐标点,射向前方            Ray ray = new Ray(m_Camera.position, m_Camera.forward);            RaycastHit hit;                        // 射线射到任何碰撞器时,判定为真            if (Physics.Raycast(ray, out hit, m_RayLength, ~m_ExclusionLayers))            {                VRInteractiveItem interactible = hit.collider.GetComponent(); //从投射点的碰撞器获取到该交互对象                m_CurrentInteractible = interactible;                // 射到一个交互物体,且不是上一个射到的交互物体,则调用这个交互物体的Over方法                if (interactible && interactible != m_LastInteractible)                    interactible.Over();                 // 如果射到的交互物体不是上一个射到的交互物体,则调用方法停用上一个交互物体                if (interactible != m_LastInteractible)                    DeactiveLastInteractible();                m_LastInteractible = interactible;                // 有物体被射中,通过Reticle中的SetPosition方法来设定准心坐标                if (m_Reticle)                    m_Reticle.SetPosition(hit);                if (OnRaycasthit != null)                    OnRaycasthit(hit);            }            else            {                // 没有物体被射中时,停用上一个交互物体                DeactiveLastInteractible();                m_CurrentInteractible = null;                // 设置准心位置                if (m_Reticle)                    m_Reticle.SetPosition();            }        }        private void DeactiveLastInteractible()        {            if (m_LastInteractible == null)                return;            m_LastInteractible.Out();            m_LastInteractible = null;        }        private void HandleUp()        {            if (m_CurrentInteractible != null)                m_CurrentInteractible.Up();        }        private void HandleDown()        {            if (m_CurrentInteractible != null)                m_CurrentInteractible.Down();        }        private void HandleClick()        {            if (m_CurrentInteractible != null)                m_CurrentInteractible.Click();        }        private void HandleDoubleClick()        {            if (m_CurrentInteractible != null)                m_CurrentInteractible.DoubleClick();        }    }}
VRInteractiveItem
定义凝视、停止凝视、单击、双击等事件,放入相应的各种方法中等待触发后执行。
using System;using UnityEngine;namespace VRStandardAssets.Utils{    // 这个脚本应该放在需要交互的游戏对象上,用户通过凝视可以激活这个对象    // 定义了各种输入行为的事件,在其它类中可以进行注册,来实现具体的交互行为    public class VRInteractiveItem : MonoBehaviour    {        public event Action OnOver;             // 凝视        public event Action OnOut;              // 停止凝视        public event Action OnClick;            // 单击        public event Action OnDoubleClick;      // 双击        public event Action OnUp;               // 松开键        public event Action OnDown;             // 按下键        protected bool m_IsOver;        public bool IsOver        {            get { return m_IsOver; }              // 当前物体是否被凝视        }        //当检测到适当的设备输入时,下面这些方法会被VREyeRaycaster调用        //这些事件只有被订阅了时,才会被调用        public void Over()        {            m_IsOver = true;            if (OnOver != null)                OnOver();        }        public void Out()        {            m_IsOver = false;            if (OnOut != null)                OnOut();        }        public void Click()        {            if (OnClick != null)                OnClick();        }        public void DoubleClick()        {            if (OnDoubleClick != null)                OnDoubleClick();        }        public void Up()        {            if (OnUp != null)                OnUp();        }        public void Down()        {            if (OnDown != null)                OnDown();        }    }}

总结:
VRInput主要用来检测设备输入并且定义各种按键事件,在检测到适当的输入时执行相应的事件;
VREyeRaycaster用来通过凝视寻找可以交互的对象(VRInteractiveItem),用适当的方法注册VRInput中的按键事件,这些方法用来调用可交互对象中的相应方法;
VRInteractiveItem中各种方法中,都是由该类重新定义的各种相应的事件,这些事件再被其它的类中具体的方法注册,来实现具体的交互方法。
1 0
原创粉丝点击