前言
在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; } }
如何使用
//添加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 游戲物體同時綁定單擊、雙擊事件暫時記錄到這,后續還可以進一步封裝,使游戲對象綁定單、雙擊更加簡單