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分支又会走单击....
这种情况下只能设置一个合适的触发时间来解决了
最终脚本、效果
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();
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();
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 游戏物体同时绑定单击、双击事件暂时记录到这,后续还可以进一步封装,使游戏对象绑定单、双击更加简单
代码已经开源、托管到我的GitHub、码云:
GitHub:https://github.com/huanzi-qch/unity-demo
码云:https://gitee.com/huanzi-qch/unity-demo