• unity3D 游戏物体同时绑定单击、双击事件


      前言

      在unity中我们常用的获取鼠标点击的方法有:

      1、在3D场景中,一般用在Update方法中,每一帧调用

    void Update(){
        if(Input.GetMouseButtonDown(0)){
            Debug.log("鼠标左键点击");  
        }
    }

      2、在画布场景中,一般用在OnGUI方法中,这个也是一个循环调用的方法,这个方法在3D场景也可以触发点击事件

        void OnGUI()
        {
            Event e = Event.current;
            if (e.isMouse && (e.clickCount == 2))
            {
                Debug.Log("用户双击了鼠标");
            }
        }

      当我们想同时给一个游戏对象绑定单击、双击事件是就会一个问题,双击事件必然会触发至少一次单击事件,不管你点的有多快,因此我们需要引入定时器来解决这个问题

      定时器介绍

      MonoBehaviour简单介绍

    using System;
    using System.Collections;
    using System.Runtime.CompilerServices;
    using UnityEngine.Bindings;
    using UnityEngine.Internal;
    using UnityEngine.Scripting;
    
    namespace UnityEngine
    {
      //MonoBehaviour是每个Unity脚本派生的基类
      public class MonoBehaviour : Behaviour
      {
        //检查当前是否有定时器
        public bool IsInvoking()
    
        //取消所有定时器调用
        public void CancelInvoke()
    
        //在time秒后,调用方法名为methodName的方法
        public void Invoke(string methodName, float time)
    
        //在time秒后,调用方法名为methodName的方法,然后每repeatRate秒重复一次
        public void InvokeRepeating(string methodName, float time, float repeatRate)
    
        //取消方法名为methodName的定时器调用
        public void CancelInvoke(string methodName)
    
        //检查在方法名为methodName上是否有定时器调用 
        public bool IsInvoking(string methodName)
        
        //其他的介绍省略...
      }
    }

      MonoBehaviour是每个Unity脚本派生的基类,只要脚本引入了UnityEngine可以直接使用

      前面也有用System.Timers.Timer来实现,定时器也能正常触发,但有一个问题,在定时函数中,我无法访问gameObject,但是可以访问到我们的两个标识,很奇怪,如果有在函数中调用到gameObject等其他属性,程序也不打印报错信息,脚本直接终止,再点击对象已经没有反应,后面通过打断点调试发现,访问这些属性将会产生一个异常:Exception of type System.NotSupportedException,因此放弃使用这个定时器

      思路

      1、当触发点击,且点击对象为当前绑定脚本的对象才继续往下执行

      2、将单、双击标识设置取反,当前为false

      3、判断是否为新一轮

      4、触发定时器,在300毫秒后执行定时调用函数,同时锁定本次判断,再本次判断没结束之前不会触发定时器

      5、在函数里进行单、双击的判断(false单击、true双击),同时重置标识,开启下一轮

      那么在这300毫秒的时间里,如果我们再次点击将会执行到第二步,单、双击标识将会被设置成true,则定时调用函数的if分支就会走双击

      隐藏bug

      那么问题来了,如果有人手速非常快,他在300毫秒内点了好几下那岂不是会有问题?如果他点了两下,那定时调用函数的if分支又会走单击....

      这种情况下只能设置一个合适的触发时间来解决了

      最终脚本、效果

      C#脚本

    using UnityEngine;
    
    /**
     * 鼠标点击事件绑定
     */
    public class Click : MonoBehaviour
    {
        private Ray _ray;//物理射线相关
    
        public RaycastHit _hit;//物理射线相关
    
        private bool _first = true;//新一轮标识(或者也可以叫是否结束的标识)
        
        private bool _flag = true;//单击或双击的标识(默认单击)
    
        private void Update()
        {
            monitor();
        }
    
        /**
         * 鼠标单、双击监听
         */
        private void monitor()
        {
            //触发鼠标左键点击
            if (!Input.GetMouseButtonDown(0)) return;
            
            //射线检测到的对象是当前对象
            if (Camera.main != null) _ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            if (!Physics.Raycast(_ray, out _hit) || _hit.collider.gameObject != gameObject) return;
            
            _flag = !_flag;
    
            //上一次的事件是否已经执行完毕,也就是判断是否为新一轮
            if (!_first) return;
            
            _first = false;
                      
            //初始化定时器,300毫秒后执行预定方法
            Invoke("Timer", 0.3f);
        }
        
        /**
         * 定时调用函数
         */
        private void Timer()
        {
            //进行判断
            if (_flag)
            {
                OnDblclick();
            }
            else
            {
                OnClick();
            }
            
            //定时调用结束,重置标识
            _first = true;
            _flag = true;
        }
    
        /**
         * 单击事件
         */
        private void OnClick()
        {
            Debug.Log(gameObject.name + "单击事件被触发");
        }
    
        /**
         * 双击事件
         */
        private void OnDblclick()
        {
            Debug.Log(gameObject.name + "双击事件被触发");
        }
    }

      把脚本绑定在具体的游戏对象即可,要注意的是,用物理射线检测是否点击的是当前对象,这个需要对象本身有Collider碰撞体组件,因为射线是与对象的碰撞体发生碰撞

       效果演示

      上图的鼠标操作流程:单击,双击,单击,双击,双击,单击;(具体打印情况看控制台右边的打印次数)

      更新脚本

      2020-05-15更新

      更新一下脚本,之前是一个脚本只能绑定一个对像,因为事件处理时直接写在脚本里的,现在改一下,改成事件处理需要传进来UnityEvent,这样一来绑定事件就更加灵活了

        using UnityEngine.Events;
        using UnityEngine;
    
        /**
         * 鼠标点击事件绑定,利用射线检测碰撞,需要对象本身有Collider碰撞体组件
         */
        public class Click : MonoBehaviour
        {
            private Ray _ray;//物理射线相关
    
            private RaycastHit _hit;//物理射线相关
    
            private bool _first = true;//新一轮标识(或者也可以叫是否结束的标识)
        
            private bool _flag = true;//单击或双击的标识(默认单击)
            
            public UnityEvent OnClickListener; //单击事件监听
            
            public UnityEvent OnDblclickListener; //双击事件监听
    
    
            private void Update()
            {
                monitor();
            }
    
            /**
             * 鼠标单、双击监听
             */
            private void monitor()
            {
                //触发鼠标左键点击
                if (!Input.GetMouseButtonDown(0)) return;
            
                //射线检测到的对象是当前对象
                if (Camera.main != null) _ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                if (!Physics.Raycast(_ray, out _hit) || _hit.collider.gameObject != gameObject) return;
            
                _flag = !_flag;
    
                //上一次的事件是否已经执行完毕,也就是判断是否为新一轮
                if (!_first) return;
            
                _first = false;
                      
                //初始化定时器,300毫秒后执行预定方法
                Invoke("Timer", 0.3f);
            }
        
            /**
             * 定时调用函数
             */
            private void Timer()
            {
                //进行判断
                if (_flag)
                {
                    OnDblclickListener.Invoke();
                }
                else
                {
                    OnClickListener.Invoke();
                }
            
                //定时调用结束,重置标识
                _first = true;
                _flag = true;
            }
        }
    View Code

      如何使用

            //添加Click组件
            Click gameObjectClick = gameObject.AddComponent<Click>();
            
            //绑定单击事件
            gameObjectClick.OnClickListener = new UnityEvent();
            gameObjectClick.OnClickListener.AddListener(() =>
            {
                Debug.Log("单击获取对象名称:"+gameObject.name);
            });
    
           //绑定双击事件
            gameObjectClick.OnDblclickListener= new UnityEvent();
            gameObjectClick.OnDblclickListener.AddListener(() =>
            {
                Debug.Log("双击获取对象名称:"+gameObject.name);
            });

      后记

      unity3D 游戏物体同时绑定单击、双击事件暂时记录到这,后续还可以进一步封装,使游戏对象绑定单、双击更加简单

  • 相关阅读:
    C++ Node template typename T
    C++ BST Insert Print,compile via g++ ,the *.cpp asterisk can represent all the files end with cpp
    springmvc 请求数据 全局过滤器
    spring mvc页面跳转和回写
    Conda 换源
    d2l 安装,提示 "error:Microsoft Visual C++ 14.0 or greater is required.Get it with "Microsoft C++ Build Tools""
    Conda 安装配置
    UI卡顿假死问题
    System.NotSupportedException:“该类型的 CollectionView 不支持从调度程序线程以外的线程对其 SourceCollection 进行的更改。”
    List<T>和ObservableCollection<T>的相互转换
  • 原文地址:https://www.cnblogs.com/huanzi-qch/p/12012721.html
Copyright © 2020-2023  润新知