Bootstrap

Unity 基础 之 简单实现鼠标点击(手机触屏)非 UI 处监听双击事件/长按事件的功能(内附判断点击在UI上还是非UI上的方法)

 

 

Unity 基础 之 简单实现鼠标点击(手机触屏)非 UI 处监听双击事件/长按事件的功能(内附判断点击在UI上还是非UI上的方法)

 

目录

Unity 基础 之 简单实现鼠标点击(手机触屏)非 UI 处监听双击事件/长按事件的功能(内附判断点击在UI上还是非UI上的方法)

一、简单介绍

二、实现原理

三、注意事项

四、效果预览

五、实现步骤

六、关键代码

附录(备注):


 

一、简单介绍

Unity中的一些基础知识点。

本节介绍,在 Unity 中,简单实现当鼠标或者手机手机触屏在不是 UI 元素上面的双击事件和长按事件的功能,便于后期使用,有不对,欢迎指正。

 

二、实现原理

1、使用封装的 IsPointerOverUIObject() 判断是否点击在非 UI 处

2、监听 Event.current.isMouse && Event.current.type == EventType.MouseDown 是鼠标,且是鼠标按下事件(对应手机上的手指触屏)

3、监听 Event.current.clickCount == 2 双击事件触发

4、Update 通过时间差,监听长按事件触发

 

三、注意事项

1、因为使用到 EventSystem,所以场景中一定要有对应的 EventSystem 组件(既可以是UI的必备组件EventSystem )

2、移动端Touch Input Module输入模式下不正常,弃用了监听 EventSystem.current.IsPointerOverGameObject() == false (PC )或者 EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId) == false ( 移动端 ) 非UI处 这个方式

 

四、效果预览

 

五、实现步骤

1、打开Unity,新建空工程

 

2、在场景中,搭建UI,用来区分有UI处和 非 UI处

 

3、在工程中新建脚本,DoubleClickLongPressNonUIWrapper 实现点击非UI处双击/长按的功能,MonoSingleton 单例,TestDoubleClickLongPressNonUIWrapper 测试 DoubleClickLongPressNonUIWrapper   功能

 

4、把 TestDoubleClickLongPressNonUIWrapper 脚本挂载到场景中

 

5、运行场景(或打包到移动设备上),效果如上

 

六、关键代码

1、DoubleClickLongPressNonUIWrapper

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

namespace XANTools
{
    /// <summary>
    /// 非 UI处的双击和长按事件封装
    /// 注意,场景中一定要有 EventSystem 组件
    /// </summary>
    public class DoubleClickLongPressNonUIWrapper : MonoSingleton<DoubleClickLongPressNonUIWrapper>
    {


        /// <summary>
        /// 初始化监听非UI处长按和双击事件
        /// </summary>
        public void Init(Action doubleClickListener, Action longPressListener) {
            doubleClickAction = doubleClickListener;
            longPressAction = longPressListener;
        }


        // Update is called once per frame
        void Update()
        {
            // 长按监听
            LongPress();
        }


        /// <summary>
        /// 监听点击非UI处的,双击和长按触发条件监听
        /// </summary>
        void OnGUI()
        {
            // 鼠标按下才监控
            if (Input.GetMouseButtonDown(0))
            {



                // 點擊非UI,是鼠标按下
                if (IsPointerOverUIObject() == false && Event.current.isMouse && Event.current.type == EventType.MouseDown)
                {


                    // 监听长按事件
                    isMouseDown = true;
                    longPressTime = Time.time;

                    // 双击事件
                    if (Event.current.clickCount == 2)
                    {
                        if (doubleClickAction != null)
                        {
                            doubleClickAction();
                        }

                        //Debug.Log("双击NonUI");
                    }

                }
                
            }
            else if (Event.current.type == EventType.MouseUp)
            {
                //Debug.Log("MouseUp");

                isMouseDown = false;
            }



        }


        /// <summary>
        /// 长按监听
        /// 1、触发一次,需要松开再次长按才能触发下一次
        /// </summary>
        void LongPress() {

            if (isMouseDown == true)
            {
                if (Time.time - longPressTime > longPressIntervalTime)
                {
                    // 触发长按
                    if (longPressAction != null) {
                        longPressAction();
                    }
                    
                    longPressTime = Time.time;
                    isMouseDown = false;
                    
                    //Debug.Log("长按NonUI");
                }
            }
        }

        #region 判断是否点击在UI上

        /// <summary>
        /// Cast a ray to test if Input.mousePosition is over any UI object in EventSystem.current. This is a replacement
        /// for IsPointerOverGameObject() which does not work on Android in 4.6.0f3
        /// </summary>
        private bool IsPointerOverUIObject()
        {
            if (EventSystem.current == null)
                return false;

            // Referencing this code for GraphicRaycaster https://gist.github.com/stramit/ead7ca1f432f3c0f181f
            // the ray cast appears to require only eventData.position.
            PointerEventData eventDataCurrentPosition = new PointerEventData(EventSystem.current);
            eventDataCurrentPosition.position = new Vector2(Input.mousePosition.x, Input.mousePosition.y);

            List<RaycastResult> results = new List<RaycastResult>();
            EventSystem.current.RaycastAll(eventDataCurrentPosition, results);

            return results.Count > 0;
        }

        /// <summary>
        /// Cast a ray to test if screenPosition is over any UI object in canvas. This is a replacement
        /// for IsPointerOverGameObject() which does not work on Android in 4.6.0f3
        /// </summary>
        private bool IsPointerOverUIObject(Canvas canvas, Vector2 screenPosition)
        {
            if (EventSystem.current == null)
                return false;

            // Referencing this code for GraphicRaycaster https://gist.github.com/stramit/ead7ca1f432f3c0f181f
            // the ray cast appears to require only eventData.position.
            PointerEventData eventDataCurrentPosition = new PointerEventData(EventSystem.current);
            eventDataCurrentPosition.position = screenPosition;

            GraphicRaycaster uiRaycaster = canvas.gameObject.GetComponent<GraphicRaycaster>();
            List<RaycastResult> results = new List<RaycastResult>();
            uiRaycaster.Raycast(eventDataCurrentPosition, results);
            return results.Count > 0;
        }
        #endregion 判断是否点击在UI上

        // 双击事件
        Action doubleClickAction;
        // 长按事件
        Action longPressAction;

        // 是否按下
        private bool isMouseDown = false;

        // 长按触发时长
        private float longPressIntervalTime = 0.6f;
        // 长按计时器
        private float longPressTime = 0f;

    }
}

 

2、TestDoubleClickLongPressNonUIWrapper

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

public class TestDoubleClickLongPressNonUIWrapper : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        DoubleClickLongPressNonUIWrapper.Instance.Init(()=> { Debug.Log("双击NonUI"); },
                                                            () => { Debug.Log("长按NonUI"); });

    }

   
}

 

3、MonoSingleton

using UnityEngine;

public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T instance = null;

    private static readonly object locker = new object();

    private static bool bAppQuitting;

    public static T Instance
    {
        get
        {
            if (bAppQuitting)
            {
                instance = null;
                return instance;
            }

            lock (locker)
            {
                if (instance == null)
                {
                    // 保证场景中只有一个 单例
                    T[] managers = Object.FindObjectsOfType(typeof(T)) as T[];
                    if (managers.Length != 0)
                    {
                        if (managers.Length == 1)
                        {
                            instance = managers[0];
                            instance.gameObject.name = typeof(T).Name;
                            return instance;
                        }
                        else
                        {
                            Debug.LogError("Class " + typeof(T).Name + " exists multiple times in violation of singleton pattern. Destroying all copies");
                            foreach (T manager in managers)
                            {
                                Destroy(manager.gameObject);
                            }
                        }
                    }


                    var singleton = new GameObject();
                    instance = singleton.AddComponent<T>();
                    singleton.name = "(singleton)" + typeof(T);
                    singleton.hideFlags = HideFlags.None;
                    DontDestroyOnLoad(singleton);

                }
                instance.hideFlags = HideFlags.None;
                return instance;
            }
        }
    }

    protected virtual void Awake()
    {
        bAppQuitting = false;
    }

    protected virtual void OnDestroy()
    {
        bAppQuitting = true;
    }
}

 

附录(备注):

1、DoubleClickLongPressNonUIWrapper(这个脚本在移动端Touch Input Module输入模式下不正常,弃用了)

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

namespace XANTools
{
    /// <summary>
    /// 非 UI处的双击和长按事件封装
    /// 注意,场景中一定要有 EventSystem 组件
    /// </summary>
    public class DoubleClickLongPressNonUIWrapper : MonoSingleton<DoubleClickLongPressNonUIWrapper>
    {


        /// <summary>
        /// 初始化监听非UI处长按和双击事件
        /// </summary>
        public void Init(Action doubleClickListener, Action longPressListener) {
            doubleClickAction = doubleClickListener;
            longPressAction = longPressListener;
        }


        // Update is called once per frame
        void Update()
        {
            // 长按监听
            LongPress();
        }


        /// <summary>
        /// 监听点击非UI处的,双击和长按触发条件监听
        /// </summary>
        void OnGUI()
        {
            // 鼠标按下才监控
            if (Input.GetMouseButtonDown(0))
            {


#if UNITY_EDITOR
                // 點擊非UI,是鼠标按下
                if (EventSystem.current.IsPointerOverGameObject() == false && Event.current.isMouse && Event.current.type == EventType.MouseDown)
                    
#elif ((UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR)
                 // 移动端
            if (EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId) == false && Event.current.isMouse && Event.current.type == EventType.MouseDown)
                
#endif
                {

#if UNITY_EDITOR
                    Debug.Log("EventSystem.current.IsPointerOverGameObject() ="+ EventSystem.current.IsPointerOverGameObject());
#elif ((UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR)

                    Debug.Log("EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId) = " + EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId));
#endif
                    // 监听长按事件
                    isMouseDown = true;
                    longPressTime = Time.time;

                    // 双击事件
                    if (Event.current.clickCount == 2)
                    {
                        if (doubleClickAction != null)
                        {
                            doubleClickAction();
                        }

                        //Debug.Log("双击NonUI");
                    }

                }
                
            }
            else if (Event.current.type == EventType.MouseUp)
            {
                Debug.Log("MouseUp");

                isMouseDown = false;
            }



        }


        /// <summary>
        /// 长按监听
        /// 1、触发一次,需要松开再次长按才能触发下一次
        /// </summary>
        void LongPress() {

            if (isMouseDown == true)
            {
                if (Time.time - longPressTime > longPressIntervalTime)
                {
                    // 触发长按
                    if (longPressAction != null) {
                        longPressAction();
                    }
                    
                    longPressTime = Time.time;
                    isMouseDown = false;
                    
                    //Debug.Log("长按NonUI");
                }
            }
        }



        // 双击事件
        Action doubleClickAction;
        // 长按事件
        Action longPressAction;

        // 是否按下
        private bool isMouseDown = false;

        // 长按触发时长
        private float longPressIntervalTime = 0.6f;
        // 长按计时器
        private float longPressTime = 0f;

    }
}

 

;