Unity 工具类 之 AR/VR 分屏 Gaze 凝视 和 Click 点击 UI 交互并存

Unity 工具类 之 AR/VR 分屏 Gaze 凝视 和 Click 点击 UI 交互并存

目录

Unity 工具类 之 AR/VR 分屏 Gaze 凝视 和 Click 点击 UI 交互并存

一、引出问题

二、解决方法

三、注意事项

四、效果预览

五、实现步骤

六、附加

鼠标右键交互功能

取消掉UGUI系统默认的鼠标点击交互功能


 

一、引出问题

Unity在使用中,AR和VR开发中,会遇到以下问题

  • 1、首先需要分屏,一般左右两屏幕;

  • 2、交互上一般的点击,由于屏幕的原因(不像手机一样的触击屏幕交互),就不适合点击交互了;

  • 3、Gaze 凝视 交互就应运而生;

  • 4、但是在电脑上开发中,Gaze 交互操作又不是很方便,所以用需要用到 点击交互;

故综上所述,Gaze 凝视交互和点击交互需要并存。

二、解决方法

1、左右两个camera,分别渲染,实现分屏;

2、Canvas 设置为 worldSpace,然后交互camera 设置为 RightCamera(当然根据需要切换也可以);

3、GazeEyeRaycaster 实现 Gaze 交互,且不干扰点击 UI 事件;

三、注意事项

1、由于分屏,所以凝视点点不能像之前单屏一样为屏幕的中心,而是左右屏中某个屏的中心( ScreenWidth * 3/4,ScreenHeight * 1/2(右屏幕中心) 或者 ScreenWidth * 1/4,ScreenHeight * 1/2 (左屏幕中心))

2、这里点击交互以右屏camera交互为主(也可以根据需要动态修改);

四、效果预览

五、实现步骤

1、打开Unity,新建场景,布局如下

2、主要脚本

using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using System.Collections.Generic;
#if UNITY_2017_2_OR_NEWER
using UnityEngine.XR;
#else
using XRSettings = UnityEngine.VR.VRSettings;
#endif

[System.Serializable]
public class FloatUnityEvent : UnityEvent<float> { }

public class GazeEyeRaycaster : MonoBehaviour
{
    [SerializeField, Tooltip("In seconds")]
    float m_loadingTime;
    [SerializeField]
    FloatUnityEvent m_onLoad;

    float m_elapsedTime = 0;

    // Prevents loop over the same selectable
    Selectable m_excluded; 
    Selectable m_currentSelectable;
    RaycastResult m_currentRaycastResult;

    IPointerClickHandler m_clickHandler;
    IDragHandler m_dragHandler;

    EventSystem m_eventSystem;
    PointerEventData m_pointerEvent;

    private void Start()
    {
        m_eventSystem = EventSystem.current;
        m_pointerEvent = new PointerEventData(m_eventSystem);
        m_pointerEvent.button = PointerEventData.InputButton.Left;
    }

    void Update()
    {
        // 因为要分屏所以让 m_pointerEvent.position 在右边屏幕中心(即为 width * 3/4,height * 1/2)
        // Set pointer position
        m_pointerEvent.position =
#if UNITY_EDITOR
            new Vector2(Screen.width * 3 / 4, Screen.height / 2);
#else
            new Vector2(XRSettings.eyeTextureWidth * 3 / 4, XRSettings.eyeTextureHeight / 2);
#endif


        List<RaycastResult> raycastResults = new List<RaycastResult>();
        m_eventSystem.RaycastAll(m_pointerEvent, raycastResults);

        // Detect selectable
        if (raycastResults.Count > 0)
        {
            foreach(var result in raycastResults)
            {
                var newSelectable = result.gameObject.GetComponentInParent<Selectable>();

                if (newSelectable)
                {
                    if(newSelectable != m_excluded && newSelectable != m_currentSelectable)
                    {
                        Select(newSelectable);
                        m_currentRaycastResult = result;
                    }
                    break;
                }
            }
        }
        else
        {
            if(m_currentSelectable || m_excluded)
            {
                Select(null, null);
            }
        }

        // Target is being activating
        if (m_currentSelectable)
        {
            m_elapsedTime += Time.deltaTime;
            m_onLoad.Invoke(m_elapsedTime / m_loadingTime);

            if (m_elapsedTime > m_loadingTime)
            {
                if (m_clickHandler != null)
                {
                    m_clickHandler.OnPointerClick(m_pointerEvent);
                    Select(null, m_currentSelectable);
                }
                else if (m_dragHandler != null)
                {
                    m_pointerEvent.pointerPressRaycast = m_currentRaycastResult;
                    m_dragHandler.OnDrag(m_pointerEvent);
                }
            }
        }
    }

    void Select(Selectable s, Selectable exclude = null)
    {
        m_excluded = exclude;

        if (m_currentSelectable)
            m_currentSelectable.OnPointerExit(m_pointerEvent);

        m_currentSelectable = s;

        if (m_currentSelectable)
        {
            m_currentSelectable.OnPointerEnter(m_pointerEvent);
            m_clickHandler = m_currentSelectable.GetComponent<IPointerClickHandler>();
            m_dragHandler = m_currentSelectable.GetComponent<IDragHandler>();
        }

        m_elapsedTime = 0;
        m_onLoad.Invoke(m_elapsedTime / m_loadingTime);
    }
}

3、运行场景效果

4、工程场景下载

UnityGazeAndClickUI AR/VR 分屏 Gaze 凝视 和 Click 点击 UI 交互并存 点击下载

六、附加

鼠标右键交互功能

1、该脚本保留之前的凝视计时确认操作,同事添加凝视点击交互,具体见脚本


	public class GazeEyeRaycasterAndInteraactiveType : MonoBehaviour
	{
        public enum InterActiveType { 
            None,   //   无交互
            TimeCounter,    // 计时交互
            LeftClick,      // 右键点击
        }

        [SerializeField, Tooltip("In seconds")]
        float m_loadingTime;
        [SerializeField]
        FloatUnityEvent m_onLoad;

        [SerializeField]
        InterActiveType mCurInterActiveType = InterActiveType.LeftClick;

        float m_elapsedTime = 0;

        // Prevents loop over the same selectable
        Selectable m_excluded;
        Selectable m_currentSelectable;
        RaycastResult m_currentRaycastResult;

        IPointerClickHandler m_clickHandler;
        IDragHandler m_dragHandler;

        EventSystem m_eventSystem;
        PointerEventData m_pointerEvent;

        private void Start()
        {
            m_eventSystem = EventSystem.current;
            m_pointerEvent = new PointerEventData(m_eventSystem);
            m_pointerEvent.button = PointerEventData.InputButton.Left;
        }

        void Update()
        {
            // 因为要分屏所以让 m_pointerEvent.position 在右边屏幕中心(即为 width * 3/4,height * 1/2)
            // Set pointer position
            m_pointerEvent.position =
#if UNITY_EDITOR
            new Vector2(Screen.width * 3 / 4, Screen.height / 2);
#else
            new Vector2(XRSettings.eyeTextureWidth * 3 / 4, XRSettings.eyeTextureHeight / 2);
#endif


            List<RaycastResult> raycastResults = new List<RaycastResult>();
            m_eventSystem.RaycastAll(m_pointerEvent, raycastResults);

            // Detect selectable
            if (raycastResults.Count > 0)
            {
                foreach (var result in raycastResults)
                {
                    var newSelectable = result.gameObject.GetComponentInParent<Selectable>();

                    if (newSelectable)
                    {
                        if (newSelectable != m_excluded && newSelectable != m_currentSelectable)
                        {
                            Select(newSelectable);
                            m_currentRaycastResult = result;
                        }
                        break;
                    }
                }
            }
            else
            {
                if (m_currentSelectable || m_excluded)
                {
                    Select(null, null);
                }
            }

            // Target is being activating
            if (m_currentSelectable)
            {
                InteractiveHandle(mCurInterActiveType);
            }
        }

        void InteractiveHandle(InterActiveType interActiveType) {
            if (interActiveType == InterActiveType.TimeCounter)
            {
                m_elapsedTime += Time.deltaTime;
                m_onLoad.Invoke(m_elapsedTime / m_loadingTime);

                if (m_elapsedTime > m_loadingTime)
                {
                    ClickAndDragUIHandle();
                }
            }

            else if (interActiveType == InterActiveType.LeftClick)
            {
                if (Input.GetMouseButton(0))
                {
                    ClickAndDragUIHandle();
                }
            }
        }

        void ClickAndDragUIHandle() {
            if (m_clickHandler != null)
            {
                m_clickHandler.OnPointerClick(m_pointerEvent);
                Select(null, m_currentSelectable);
            }
            else if (m_dragHandler != null)
            {
                m_pointerEvent.pointerPressRaycast = m_currentRaycastResult;
                m_dragHandler.OnDrag(m_pointerEvent);
            }
        }

        void Select(Selectable s, Selectable exclude = null)
        {
            m_excluded = exclude;

            if (m_currentSelectable)
                m_currentSelectable.OnPointerExit(m_pointerEvent);

            m_currentSelectable = s;

            if (m_currentSelectable)
            {
                m_currentSelectable.OnPointerEnter(m_pointerEvent);
                m_clickHandler = m_currentSelectable.GetComponent<IPointerClickHandler>();
                m_dragHandler = m_currentSelectable.GetComponent<IDragHandler>();
            }

            m_elapsedTime = 0;
            m_onLoad.Invoke(m_elapsedTime / m_loadingTime);
        }
    }

取消掉UGUI系统默认的鼠标点击交互功能

猜你喜欢

转载自blog.csdn.net/u014361280/article/details/103588392#comments_26720952
今日推荐