屏幕区域点击检测

此处直接代码:


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


namespace Asstes.UI
{
    /// <summary>
    /// Introduction: 屏幕区域点击判断
    /// Author: 
    /// Time: 
    /// </summary>
    public class AreaClick
    {
        private static AreaClick m_instance;

        public static AreaClick Instance
        {
            get
            {
                if (m_instance == null)
                    m_instance = new AreaClick();
                return m_instance;
            }
        }

        private int m_index;
        private List<AreaRect> m_rects;
        private List<AreaRect> m_removes;
        private List<int> m_deletes;

        public AreaClick()
        {
            m_index = 0;
            m_rects = new List<AreaRect>();
            m_removes = new List<AreaRect>();
            m_deletes = new List<int>();
            InputManager.Instance.AddListener(InputType.OnClick, OnClickScreen);
            InputManager.Instance.AddListener(InputType.OnEndLongPress, OnClickScreen);
        }

        private void OnClickScreen(Vector2 param)
        {
            m_removes.Clear();
            for (int i = 0; i < m_rects.Count; i++)//移除已删除的点
            {
                if(m_deletes.Contains(m_rects[i].id))
                    m_removes.Add(m_rects[i]);
            }
            for (int i = 0; i < m_removes.Count; i++)
            {
                m_rects.Remove(m_removes[i]);
            }
            m_deletes.Clear();
            for (int i = 0; i < m_rects.Count; i++)
            {
                m_rects[i].OnClick(param);
            }
        }

        /// <summary>
        /// 添加检测
        /// </summary>
        /// <param name="area"></param>
        /// <returns></returns>
        public int AddCheck(AreaRect area)
        {
            area.id = m_index++;
            m_rects.Add(area);
            return area.id;
        }

        /// <summary>
        /// 移除检测
        /// </summary>
        /// <param name="id"></param>
        public void CancelCheck(int id)
        {
            if(!m_deletes.Contains(id))
                m_deletes.Add(id);
        }

    }

    public class AreaRect
    {
        private Rect m_rect;
        private bool m_allNotice;
        private bool m_onlyOnce;
        private Callback_1<bool> m_callback;
        internal int id;
        /// <summary>
        /// 是否只使用一次
        /// </summary>
        public bool UseOnce { get { return m_onlyOnce; } set { m_onlyOnce = value; } }

        /// <summary>
        /// 普通构造函数
        /// </summary>
        /// <param name="rect">在屏幕上的区域</param>
        /// <param name="allNotice">通知点中和未点中</param>
        /// <param name="callback">回调</param>
        public AreaRect(Rect rect, bool allNotice, Callback_1<bool> callback)
        {
            m_rect = rect;
            m_allNotice = allNotice;
            m_callback = callback;
        }

        /// <summary>
        /// 可缩放区域
        /// </summary>
        /// <param name="rect">设计时所选区域,如UI设计时的rect</param>
        /// <param name="designSize">UI设计时屏幕分辨率</param>
        /// <param name="allNotice">通知点中和未点中</param>
        /// <param name="callback">回调</param>
        public AreaRect(Rect rect, Vector2 designSize, bool allNotice, Callback_1<bool> callback)
        {
            float sx = Screen.width/designSize.x;
            float sy = Screen.height/designSize.y;

            Rect newRect = new Rect()
            {
                x = rect.x*sx,
                y = rect.y*sy,
                width = rect.width*sx,
                height = rect.height*sy
            };

            m_rect = newRect;
            m_allNotice = allNotice;
            m_callback = callback;
        }

        /// <summary>
        /// UI区域
        /// </summary>
        /// <param name="transform">RectTransform</param>
        /// <param name="allNotice">通知点中和未点中</param>
        /// <param name="callback">回调</param>
        public AreaRect(Transform transform, bool allNotice, Callback_1<bool> callback)
        {
            RectTransform rectTransform = transform as RectTransform;

            /*m_rect = rectTransform.rect; //没有考虑到缩放问题,当缩放后rect的长宽并未改变
            m_rect.center = rectTransform.position;*/ //没有考虑到中心点影响,center并非world position

            float w = rectTransform.sizeDelta.x * rectTransform.lossyScale.x;//消除缩放影响
            float h = rectTransform.sizeDelta.y * rectTransform.localScale.y;

            float x = rectTransform.position.x - rectTransform.pivot.x * w; //消除中心点并非pivot非(0.5,0.5)影响
            float y = rectTransform.position.y - rectTransform.pivot.y * h;

            m_rect.center = new Vector2(x, y);
            m_rect.width = w;
            m_rect.height = h;

            m_allNotice = allNotice;
            m_callback = callback;
        }

        /// <summary>
        /// 是否在区域中
        /// </summary>
        /// <param name="pos"></param>
        /// <returns></returns>
        public bool IsContain(Vector3 pos)
        {
            return m_rect.Contains(pos);
        }

        public void OnClick(Vector2 pos)
        {
            if (IsContain(pos))
            {
                m_callback(true);
                if(m_onlyOnce)
                    AreaClick.Instance.CancelCheck(id);
            }
            else if(m_allNotice)
            {
                m_callback(false);
            }
        }

    }
}

其中InputManager是我自己实现的一个在多平台上判断点击事件的工具,

InputManager.Instance.AddListener(InputType.OnClick, OnClickScreen);  
InputManager.Instance.AddListener(InputType.OnEndLongPress, OnClickScreen);  
//可直接用
Input.GetMouseButtonDown(0) 和  
Input.GetTouch(0)***************代替判断

一个很平常的需求、点击空白处关闭界面,以前自己的做法是给它做个全透明的图片托底(当时自己也没有想其他方法),当判断到点击是图片后关闭。但最近时遇到一个不能用这个方法的,因为你不能遮住其他的UI点击事件,故写了个工具,并做了个小扩展,目前只支持矩形区域的硬检测。目前可用到所有的屏幕区域检测和RectTransform检测。

由于最近用到在拖动时,如果在某个区域则相应拖动事件,如果超过这个区域则停止移动,于是改了一下代码:

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


namespace Assets.UI
{
    /// <summary>
    /// Introduction: 屏幕区域点击移动判断
    /// Author:         Cheng
    /// Time: 
    /// </summary>
    public class AreaCheck
    {
        private static AreaCheck m_instance;
        public static AreaCheck Instance
        {
            get
            {
                if (m_instance == null)
                    m_instance = new AreaCheck();
                return m_instance;
            }
        }
        private int m_index;
        private List<AreaRect> m_rects;
        private List<AreaRect> m_removes;
        private List<int> m_deletes;

        public AreaCheck()
        {
            m_index = 0;
            m_rects = new List<AreaRect>();
            m_removes = new List<AreaRect>();
            m_deletes = new List<int>();
            InputManager.Instance.AddListener(InputType.OnTouchBegin, OnTouchBegin);
            InputManager.Instance.AddListener(InputType.OnMoveBegin, OnMoveBrgin);
            InputManager.Instance.AddListener(InputType.OnMove, OnMove);
            InputManager.Instance.AddListener(InputType.OnMoveEnd, OnMoveEnd);
            InputManager.Instance.AddListener(InputType.OnClick, OnClickScreen);
            InputManager.Instance.AddListener(InputType.OnEndLongPress, OnClickScreen);
        }
        private void OnTouchBegin(Vector2 param)
        {
            m_removes.Clear();
            for (int i = 0; i < m_rects.Count; i++)//移除已删除的点
            {
                if (m_deletes.Contains(m_rects[i].id))
                    m_removes.Add(m_rects[i]);
            }
            for (int i = 0; i < m_removes.Count; i++)
            {
                m_rects.Remove(m_removes[i]);
            }
            m_deletes.Clear();
        }
        private void OnClickScreen(Vector2 param)
        {
            for (int i = 0; i < m_rects.Count; i++)
            {
                m_rects[i].OnClick(param);
            }
        }
        private void OnMoveBrgin(Vector2 param)
        {
            for (int i = 0; i < m_rects.Count; i++)
            {
                m_rects[i].OnMoveBegin(param);
            }
        }
        private void OnMove(Vector2 param)
        {
            for (int i = 0; i < m_rects.Count; i++)
            {
                m_rects[i].OnMove(param);
            }
        }
        private void OnMoveEnd(Vector2 param)
        {
            for (int i = 0; i < m_rects.Count; i++)
            {
                m_rects[i].OnMoveEnd(param);
            }
        }

        /**********因lua重载的callback只检测luafunction,改重载为重命名  T_T  ************************/
        public int AddClickCheckRect(Rect rect, bool noticeAll, Callback_1<bool> callback)
        {
            AreaRect area = new AreaRect(rect)
            {
                CheckClick = true,
                ClickAllNotice = noticeAll,
                ClickCallback = callback
            };
            return AddCheck(area);
        }

        public int AddClickCheckResizeRect(Rect rect, Vector2 designSize, bool noticeAll, Callback_1<bool> callback)
        {
            AreaRect area = new AreaRect(rect, designSize)
            {
                CheckClick = true,
                ClickAllNotice = noticeAll,
                ClickCallback = callback
            };
            return AddCheck(area);
        }

        public int AddClickCheckTransform(Transform transform, bool noticeAll, Callback_1<bool> callback)
        {
            AreaRect area = new AreaRect(transform)
            {
                CheckClick = true,
                ClickAllNotice = noticeAll,
                ClickCallback = callback
            };
            return AddCheck(area);
        }

        public int AddMoveCheckRect(Rect rect, bool outOnEnd, Callback_1<Vector2> begin, Callback_1<Vector2> move, Callback_1<Vector2> end)
        {
            AreaRect area = new AreaRect(rect)
            {
                CheckMove = true,
                EndOnOut = outOnEnd,
                MoveBeginCallback = begin,
                MoveCallback = move,
                MoveEndCallback = end
            };
            return AddCheck(area);
        }

        public int AddMoveCheckResizeRect(Rect rect, Vector2 designSize, bool outOnEnd, Callback_1<Vector2> begin, Callback_1<Vector2> move, Callback_1<Vector2> end)
        {
            AreaRect area = new AreaRect(rect, designSize)
            {
                CheckMove = true,
                EndOnOut = outOnEnd,
                MoveBeginCallback = begin,
                MoveCallback = move,
                MoveEndCallback = end
            };
            return AddCheck(area);
        }

        public int AddMoveCheckTransform(Transform transform, bool outOnEnd, Callback_1<Vector2> begin, Callback_1<Vector2> move, Callback_1<Vector2> end)
        {
            AreaRect area = new AreaRect(transform)
            {
                CheckMove = true,
                EndOnOut = outOnEnd,
                MoveBeginCallback = begin,
                MoveCallback = move,
                MoveEndCallback = end
            };
            return AddCheck(area);
        }

        /// <summary>
        /// 添加检测
        /// </summary>
        /// <param name="area"></param>
        /// <returns></returns>
        [LuaInterface.NoToLua]
        public int AddCheck(AreaRect area)
        {
            area.id = m_index++;
            m_rects.Add(area);
            return area.id;
        }

        /// <summary>
        /// 移除检测
        /// </summary>
        /// <param name="id"></param>
        public void CancelCheck(int id)
        {
            if(!m_deletes.Contains(id))
                m_deletes.Add(id);
        }

    }

    public class AreaRect
    {
        private Rect m_rect;
        private bool moving;
        internal int id;
        /// <summary>
        /// 鼠标移动时移除即结束
        /// </summary>
        public bool EndOnOut{ get; set; }
        /// <summary>
        /// 通知点中和未点中
        /// </summary>
        public bool ClickAllNotice { get; set; }
        /// <summary>
        /// 检测点击
        /// </summary>
        public bool CheckClick { get; set; }
        /// <summary>
        /// 检测移动
        /// </summary>
        public bool CheckMove { get; set; }
        /// <summary>
        /// 点击回调
        /// </summary>
        public Callback_1<bool> ClickCallback;
        /// <summary>
        /// 移动结束回调
        /// </summary>
        public Callback_1<Vector2> MoveBeginCallback;
        /// <summary>
        /// 移动回调
        /// </summary>
        public Callback_1<Vector2> MoveCallback;
        /// <summary>
        /// 移动结束回调
        /// </summary>
        public Callback_1<Vector2> MoveEndCallback;

        /// <summary>
        /// 普通构造函数
        /// </summary>
        /// <param name="rect">在屏幕上的区域</param>
        public AreaRect(Rect rect)
        {
            m_rect = rect;
        }

        /// <summary>
        /// 可缩放区域
        /// </summary>
        /// <param name="rect">设计时所选区域,如UI设计时的rect</param>
        /// <param name="designSize">UI设计时屏幕分辨率</param>
        public AreaRect(Rect rect, Vector2 designSize)
        {
            float sx = Screen.width/designSize.x;
            float sy = Screen.height/designSize.y;

            Rect newRect = new Rect()
            {
                x = rect.x*sx,
                y = rect.y*sy,
                width = rect.width*sx,
                height = rect.height*sy
            };

            m_rect = newRect;
        }

        /// <summary>
        /// UI区域
        /// </summary>
        /// <param name="transform">RectTransform</param>
        public AreaRect(Transform transform)
        {
            RectTransform rectTransform = transform as RectTransform;

            /*m_rect = rectTransform.rect; //没有考虑到缩放问题,当缩放后rect的长宽并未改变
            m_rect.center = rectTransform.position;*/ //没有考虑到中心点影响,center并非world position

            float w = rectTransform.sizeDelta.x * rectTransform.lossyScale.x;//消除缩放影响
            float h = rectTransform.sizeDelta.y * rectTransform.localScale.y;

            float x = rectTransform.position.x - rectTransform.pivot.x * w; //消除中心点并非pivot非(0.5,0.5)影响
            float y = rectTransform.position.y - rectTransform.pivot.y * h;

            m_rect.center = new Vector2(x, y);
            m_rect.width = w;
            m_rect.height = h;
        }

        /// <summary>
        /// 是否在区域中
        /// </summary>
        /// <param name="pos"></param>
        /// <returns></returns>
        public bool IsContain(Vector3 pos)
        {
            return m_rect.Contains(pos);
        }

        /// <summary>
        /// 区域点击检测
        /// </summary>
        /// <param name="pos"></param>
        public void OnClick(Vector2 pos)
        {
            if(!CheckClick) return;
            if (IsContain(pos) && ClickCallback != null)
            {
                ClickCallback(true);
            }
            else if (ClickAllNotice && ClickCallback != null)
            {
                ClickCallback(false);
            }
        }

        /// <summary>
        /// 开始移动
        /// </summary>
        /// <param name="pos"></param>
        public void OnMoveBegin(Vector2 pos)
        {
            if (!CheckMove) return;
            if (IsContain(pos))
            {
                moving = true;
                if (MoveBeginCallback != null)
                    MoveBeginCallback(pos);
            }
        }

        /// <summary>
        /// 区域移动检测
        /// </summary>
        /// <param name="pos"></param>
        public void OnMove(Vector2 pos)
        {
            if(!moving || !CheckMove) return;

            if (IsContain(pos))
            {
                if (MoveCallback != null)
                    MoveCallback(pos);
            }
            else
            {
                if (EndOnOut)
                {
                    moving = false;
                    if (MoveEndCallback != null)
                        MoveEndCallback(pos);
                }
                else
                {
                    if (MoveCallback != null)
                        MoveCallback(pos);
                }
            }
        }

        /// <summary>
        /// 移动结束
        /// </summary>
        /// <param name="pos"></param>
        public void OnMoveEnd(Vector2 pos)
        {
            if (!moving || !CheckMove) return;
            if (MoveEndCallback != null)
                MoveEndCallback(pos);
        }

    }
}

使用方式现在直接调用AreaCheck的单例即可。