Unity 单击、双击、长按事件处理

这篇具有很好参考价值的文章主要介绍了Unity 单击、双击、长按事件处理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

需求

想要一个工具,能够同时集成单击、双击、长按的功能

思路

通过IPointerDownHandler, IPointerUpHandler, IPointerClickHandler这三个接口就可以监听点击状态,然后再通过不同的点击状态来处理相应的事件

遇到的问题

  • 由于可能同时存在多个事件,实际开发过程中会出现多余事件通知,如下:
    • 同时拥有单击和双击事件,点击双击的同时会响应两次单击
    • 同时拥有单击事件和长按事件时,当长按事件响应的时候会同时响应单击事件
    • 同时拥有单击双击和长按事件时点击双击时会同时响应两次单击,长按事件响应时会同时响应单击事件

解决问题

  • 由于我们无法预测用户的具体行为,所以当需要响应单击事件时,检测其他事件有没有在安全时效内的检测体,例如,单击响应的时候如果有其他事件正在检测过程中我们忽略这次的事件响应,并把事件存起来,等待所有事件的安全时效结束时响应这次单击事件。或者是等待过程中有其他事件成功响应那么我们就舍弃这次的单击事件而响应对应的双击或者长按事件。那么这样就完美解决了上述所有问题。

具体代码如下:文章来源地址https://www.toymoban.com/news/detail-512392.html

ClickEvent

public class ClickEvent : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerClickHandler
    {
        private List<IClickListener> listenerList = new List<IClickListener>();
        private Dictionary<IClickListener, Action> callBackDict = new Dictionary<IClickListener, Action>();

        public void OnPointerDown(PointerEventData eventData)
        {
            for (int i = listenerList.Count - 1; i >= 0; i--)
            {
                listenerList[i]?.OnPointerDown(eventData);
            }
        }

        public void OnPointerUp(PointerEventData eventData)
        {
            for (int i = listenerList.Count - 1; i >= 0; i--)
            {
                listenerList[i]?.OnPointerUp(eventData);
            }
        }

        public void OnPointerClick(PointerEventData eventData)
        {
            for (int i = listenerList.Count - 1; i >= 0; i--)
            {
                listenerList[i]?.OnPointerClick(eventData);
            }
        }

        private void Update()
        {
            for (int i = listenerList.Count - 1; i >= 0; i--)
            {
                listenerList[i]?.Update();
            }
        }

        public T Add<T>() where T : ClickListener, new()
        {
            T t = Get<T>();
            if (t == null)
            {
                t = new T();
                (t as IClickListener).OnCallBack((success) => OnReceive(success, t));
                if (listenerList.Count == 0 || t is ClickButton) listenerList.Add(t);
                else listenerList.Insert(listenerList.Count - 1, t);
            }

            return t;
        }

        public void Remove<T>() where T : ClickListener
        {
            if (!Contains<T>()) return;
            T t = Get<T>();
            if (callBackDict.ContainsKey(t)) callBackDict.Remove(t);
            listenerList.Remove(t);
        }

        public void RemoveAll()
        {
            listenerList.Clear();
            callBackDict.Clear();
        }

        public T Get<T>() where T : ClickListener
        {
            if (listenerList.Count == 0) return default;
            IClickListener listener = default;
            for (int i = listenerList.Count - 1; i >= 0; i--)
            {
                listener = listenerList[i];
                if (listener is T) return (T) listener;
            }

            return default;
        }

        public bool Contains<T>() where T : ClickListener
        {
            if (listenerList.Count == 0) return false;
            IClickListener listener = default;
            for (int i = listenerList.Count - 1; i >= 0; i--)
            {
                listener = listenerList[i];
                if (listener is T) return true;
            }

            return false;
        }

        public bool ContainsCallBack<T>() where T : ClickListener
        {
            T t = Get<T>();
            if (t == null) return false;
            if (callBackDict.ContainsKey(t))
            {
                if (callBackDict[t] == null)
                {
                    callBackDict.Remove(t);
                    return false;
                }

                return true;
            }
            else return false;
        }

        public Action GetCallBack<T>() where T : ClickListener
        {
            T t = Get<T>();
            if (t == null) return null;
            if (callBackDict.ContainsKey(t))
            {
                Action callBack = callBackDict[t];
                if (callBack == null)
                {
                    callBackDict.Remove(t);
                    return null;
                }

                return callBack;
            }
            else return null;
        }

        public bool HasRuning()
        {
            bool isRuning = false;
            foreach (var lis in listenerList)
            {
                if (lis.isRuning)
                {
                    isRuning = true;
                    break;
                }
            }

            return isRuning;
        }

        public T OnRegister<T>(Action callBack) where T : ClickListener, new()
        {
            T t = Add<T>();
            if (callBackDict.ContainsKey(t)) callBackDict[t] = callBack;
            else callBackDict.Add(t, callBack);
            return t;
        }

        private Action clickCallBack = null;

        void OnReceive(bool success, IClickListener listener)
        {
            if (success)
            {
                if (listener is ClickButton) //单击事件
                {
                    Action cbk = GetCallBack<ClickButton>();
                    if (cbk == null) return;
                    if (HasRuning()) //有正在运行的事件检测单击事件滞后分发
                    {
                        clickCallBack = cbk;
                    }
                    else
                    {
                        clickCallBack = null;
                        ResetOther();
                        cbk.Invoke();
                    }
                }
                else //其他事件
                {
                    clickCallBack = null;
                    ResetOther();
                    if (callBackDict.ContainsKey(listener)) callBackDict[listener]?.Invoke();
                }
            }
            else
            {
                if (!HasRuning())
                {
                    clickCallBack?.Invoke();
                    clickCallBack = null;
                }
            }

            void ResetOther()
            {
                foreach (var btn in listenerList)
                {
                    if (btn != listener) btn?.Reset();
                }
            }
        }

#if UNITY_EDITOR

        [UnityEditor.CustomEditor(typeof(ClickEvent))]
        class ClickEventInspector : UnityEditor.Editor
        {
            private ClickEvent clickEvent;

            private void OnEnable()
            {
                clickEvent = target as ClickEvent;
            }

            public override void OnInspectorGUI()
            {
                base.OnInspectorGUI();
                GUILayout.Label("ClickListeners: ");
                IClickListener listener;
                for (int i = clickEvent.listenerList.Count - 1; i >= 0; i--)
                {
                    listener = clickEvent.listenerList[i];
                    GUILayout.Label("    " + listener);
                }
            }
        }

#endif

        private interface IClickListener
        {
            bool isRuning { get; set; }
            void OnPointerDown(PointerEventData eventData);
            void OnPointerUp(PointerEventData eventData);

            void OnPointerClick(PointerEventData eventData);

            void Update();

            void OnCallBack(Action<bool> callBack);

            void Reset();

            void Dispose();
        }

        public abstract class ClickListener : IClickListener
        {
            bool IClickListener.isRuning
            {
                get => isRuning;
                set { isRuning = value; }
            }

            protected bool isRuning = false;

            protected Action<bool> callBack { get; private set; }
            void IClickListener.OnPointerDown(PointerEventData eventData) => OnPointerDown(eventData);
            void IClickListener.OnPointerUp(PointerEventData eventData) => OnPointerUp(eventData);
            void IClickListener.OnPointerClick(PointerEventData eventData) => OnPointerClick(eventData);
            void IClickListener.Update() => Update();
            void IClickListener.Reset() => Reset();
            void IClickListener.Dispose() => Dispose();

            void IClickListener.OnCallBack(Action<bool> callBack)
            {
                this.callBack = callBack;
            }

            protected virtual void OnPointerDown(PointerEventData eventData)
            {
            }

            protected virtual void OnPointerUp(PointerEventData eventData)
            {
            }

            protected virtual void OnPointerClick(PointerEventData eventData)
            {
            }

            protected virtual void Update()
            {
            }

            protected abstract void Reset();

            protected virtual void Dispose()
            {
                callBack = null;
            }
        }
    }

Buttons

 //单击按钮
    public class ClickButton : ClickEvent.ClickListener
    {
        protected override void OnPointerDown(PointerEventData eventData)
        {
            isRuning = true;
        }

        protected override void OnPointerUp(PointerEventData eventData)
        {
            if (isRuning)
            {
                Reset();
                callBack?.Invoke(true);
            }
        }

        protected override void Reset()
        {
            isRuning = false;
        }
    }

    //双击按钮
    public class DoubleClickButton : ClickEvent.ClickListener
    {
        public int maxSpaceTime = 250;
        private int clickCount = 0;
        private Stopwatch stopWatch;
        private float lastPointTime = 0;

        public DoubleClickButton()
        {
            stopWatch = new Stopwatch();
            Reset();
        }

        protected override void OnPointerDown(PointerEventData eventData)
        {
            isRuning = true;
        }

        protected override void OnPointerClick(PointerEventData eventData)
        {
            stopWatch.Start();
            clickCount++;
            if (clickCount == 2)
            {
                Reset();
                callBack?.Invoke(true);
            }
        }

        protected override void Update()
        {
            if (!stopWatch.IsRunning) return;
            if (stopWatch.ElapsedMilliseconds > maxSpaceTime)
            {
                Reset();
                callBack?.Invoke(false);
            }
        }

        protected override void Reset()
        {
            isRuning = false;
            if (stopWatch.IsRunning) stopWatch.Stop();
            stopWatch.Reset();
            clickCount = 0;
        }
    }

    //长按按钮
    public class PressButton : ClickEvent.ClickListener
    {
        public int pressMinTime = 2000;
        private Stopwatch stopWatch;

        public PressButton()
        {
            stopWatch = new Stopwatch();
        }

        protected override void OnPointerDown(PointerEventData eventData)
        {
            isRuning = true;
            stopWatch.Start();
        }

        protected override void OnPointerUp(PointerEventData eventData)
        {
            Reset();
            callBack?.Invoke(false);
        }


        protected override void Update()
        {
            if (!stopWatch.IsRunning) return;
            if (stopWatch.ElapsedMilliseconds > pressMinTime)
            {
                Reset();
                callBack?.Invoke(true);
            }
        }

        protected override void Reset()
        {
            isRuning = false;
            if (stopWatch.IsRunning) stopWatch.Stop();
            stopWatch.Reset();
        }
    }

GameObjectExpand

public static class GameObjectExpand
    {
        public static ClickButton OnClick(this GameObject obj, Action callBack)
        {
            if (obj == null) return null;
            ClickEvent clickEvent = GetClickEvent(obj);
            ClickButton button = clickEvent.OnRegister<ClickButton>(callBack);
            return button;
        }

        public static DoubleClickButton OnDoubleClick(this GameObject obj, Action callBack)
        {
            if (obj == null) return null;
            ClickEvent clickEvent = GetClickEvent(obj);
            DoubleClickButton button = clickEvent.OnRegister<DoubleClickButton>(callBack);
            return button;
        }

        public static PressButton OnPress(this GameObject obj, Action callBack)
        {
            if (obj == null) return null;
            ClickEvent clickEvent = GetClickEvent(obj);
            PressButton button = clickEvent.OnRegister<PressButton>(callBack);
            return button;
        }

        static ClickEvent GetClickEvent(GameObject obj)
        {
            ClickEvent clickEvent = obj.GetComponent<ClickEvent>();
            if (clickEvent == null) clickEvent = obj.AddComponent<ClickEvent>();
            return clickEvent;
        }
    }

使用

public GameObject uiObj;
//点击事件注册
uiObj.OnClick(() =>
{
    Debug.Log("OnClick");
});
//双击事件注册
uiObj.OnDoubleClick(() =>
{
    Debug.Log("OnDoubleClick");
});
//长按事件注册
uiObj.OnPress(() =>
{
    Debug.Log("OnPress");
});

到了这里,关于Unity 单击、双击、长按事件处理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 按键状态机(实现单击,长按,双击)的模块分享

    目录 一、相关说明 二、分析 三、模块代码 三、代码讲解 四、作者的话         1.需要的资源:一个定时器,一个按键。         2.相关设置:利用定时器计时中断,10ms进行一次按键扫描。         3.使用说明:定时器中断的优先级要设置高一点,相关的宏定义可以

    2024年02月07日
    浏览(47)
  • STM32独立按键实现单击双击长按功能

    目录 前言 一、按键功能定义 二、使用步骤 1.按键初始化 2.按键扫描函数(重点) 总结 在使用STM32或其他单片机开发项目时,经常需要用到独立按键进行控制。 通常一个独立按键需要使用一个IO口,如果项目需要按键实现多个功能,往往需要使用到多个按键,需要使用到多个

    2023年04月17日
    浏览(26)
  • 「Python|Selenium|场景案例」如何模拟鼠标单击、双击、右击、长按和拖拽等操作?

    本文主要介绍如何在使用selenium进行自动化测试的时候模拟各种鼠标操作。 在进行自动化测试的时候,我们可能会需要需要进行鼠标操作的场景,比如: 测试右击,查看是否网页是否屏蔽了右键菜单 面对滑块式登录验证方式,模拟拖拽 模拟前进或后退等鼠标扩展操作 ……

    2023年04月08日
    浏览(37)
  • 【蓝桥杯嵌入式】定时器实现按键单击,双击,消抖以及长按的代码实现

    🎊【蓝桥杯嵌入式】专题正在持续更新中,原理图解析✨,各模块分析✨以及历年真题讲解✨都在这儿哦,欢迎大家前往订阅本专题,获取更多详细信息哦🎏🎏🎏 🪔本系列专栏 -  蓝桥杯嵌入式_勾栏听曲_0的博客 🍻欢迎大家  🏹  点赞👍  评论📨  收藏⭐️ 📌个人主

    2024年01月17日
    浏览(35)
  • antd table的单击事件和双击事件冲突

    问题:当table上同时有onClick和onDoubleClick时,双击会一直触发单击事件。 解决方法:使用延迟定时器-setTimeout                   单击事件延迟执行,如果检测到连续点击,则是双击事件,不再执行单击事件。 代码实现:

    2024年01月25日
    浏览(37)
  • 微信小程序长按与单击事件触发

    方式一 wxml 片段 js 片段 解析 tap 触摸事件采用 catch 阻止事件冒泡 1.5.0之后支持 longpress 事件,手指触摸后,超过350ms再离开,如果指定了事件回调函数并触发了这个事件,tap事件将不被触发 方式二(不推荐) longtap 事件,但在触发时会同时触发单击事件,需配合 touchstart 和

    2024年02月12日
    浏览(77)
  • 小程序更多的手势事件(左右滑动、放大缩小、双击、长按)

    微信小程序提供的原生事件有:触摸开始(bindtouchstart)、移动触摸点(bindtouchmove)、触摸结束(bindtouchend)以及长按事件(bindlongtap)和单击事件(bindtap)。通过以上原生事件可设计制作衍生出更多的手势事件; 文章记录小程序中组件 “左右滑动、放大缩小、双击” 事件

    2024年02月09日
    浏览(45)
  • 【RuoYi移动端】uni-app中的单击和双击事件

     @click=\\\"enterpriseSelect\\\"  @touchend=\\\"userinfo\\\"

    2024年02月08日
    浏览(35)
  • [Unity案例]Button的双击和长按

    实现内容: 扩展Unity自带的Button组件,在原有的单击事件的基础上实现双击和长按效果; 替换Button组件,通过单选框选择当前执行哪一种事件 代码详解: 单击事件处理: 通过 singleClickEnabled 开关控制是否启用单击事件。 在 OnPointerClick 方法中,根据按钮的激活状态和可交互

    2024年04月26日
    浏览(25)
  • Unity3D实现UI的单击、双击、拖动状态判断

    这篇文章就来实现UI的单击、双击、按压、拖动的不同状态判断。不定时更新Unity开发技巧,觉得有用记得一键三连哦。 示例、 判断单击和双击,主要是判断点击的次数。 UI的点击事件,需要继承UI的点击事件接口,重写点击事件即可。 UI点击事件接口: 3-1-1 所引用的命名空

    2024年01月20日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包