Unity 基础 之 简单实现鼠标点击(手机触屏)非 UI 处监听双击事件/长按事件的功能(内附判断点击在UI上还是非UI上的方法)
目录
Unity 基础 之 简单实现鼠标点击(手机触屏)非 UI 处监听双击事件/长按事件的功能(内附判断点击在UI上还是非UI上的方法)
一、简单介绍
Unity中的一些基础知识点。
本节介绍,在 Unity 中,简单实现当鼠标或者手机手机触屏在不是 UI 元素上面的双击事件和长按事件的功能,便于后期使用,有不对,欢迎指正。
二、实现原理
1、使用封装的 IsPointerOverUIObject() 判断是否点击在非 UI 处
2、监听 Event.current.isMouse && Event.current.type == EventType.MouseDown 是鼠标,且是鼠标按下事件(对应手机上的手指触屏)
3、监听 Event.current.clickCount == 2 双击事件触发
4、Update 通过时间差,监听长按事件触发
三、注意事项
1、因为使用到 EventSystem,所以场景中一定要有对应的 EventSystem 组件(既可以是UI的必备组件EventSystem )
2、移动端Touch Input Module输入模式下不正常,弃用了监听 EventSystem.current.IsPointerOverGameObject() == false (PC )或者 EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId) == false ( 移动端 ) 非UI处 这个方式
四、效果预览
五、实现步骤
1、打开Unity,新建空工程
2、在场景中,搭建UI,用来区分有UI处和 非 UI处
3、在工程中新建脚本,DoubleClickLongPressNonUIWrapper 实现点击非UI处双击/长按的功能,MonoSingleton 单例,TestDoubleClickLongPressNonUIWrapper 测试 DoubleClickLongPressNonUIWrapper 功能
4、把 TestDoubleClickLongPressNonUIWrapper 脚本挂载到场景中
5、运行场景(或打包到移动设备上),效果如上
六、关键代码
1、DoubleClickLongPressNonUIWrapper
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace XANTools
{
/// <summary>
/// 非 UI处的双击和长按事件封装
/// 注意,场景中一定要有 EventSystem 组件
/// </summary>
public class DoubleClickLongPressNonUIWrapper : MonoSingleton<DoubleClickLongPressNonUIWrapper>
{
/// <summary>
/// 初始化监听非UI处长按和双击事件
/// </summary>
public void Init(Action doubleClickListener, Action longPressListener) {
doubleClickAction = doubleClickListener;
longPressAction = longPressListener;
}
// Update is called once per frame
void Update()
{
// 长按监听
LongPress();
}
/// <summary>
/// 监听点击非UI处的,双击和长按触发条件监听
/// </summary>
void OnGUI()
{
// 鼠标按下才监控
if (Input.GetMouseButtonDown(0))
{
// 點擊非UI,是鼠标按下
if (IsPointerOverUIObject() == false && Event.current.isMouse && Event.current.type == EventType.MouseDown)
{
// 监听长按事件
isMouseDown = true;
longPressTime = Time.time;
// 双击事件
if (Event.current.clickCount == 2)
{
if (doubleClickAction != null)
{
doubleClickAction();
}
//Debug.Log("双击NonUI");
}
}
}
else if (Event.current.type == EventType.MouseUp)
{
//Debug.Log("MouseUp");
isMouseDown = false;
}
}
/// <summary>
/// 长按监听
/// 1、触发一次,需要松开再次长按才能触发下一次
/// </summary>
void LongPress() {
if (isMouseDown == true)
{
if (Time.time - longPressTime > longPressIntervalTime)
{
// 触发长按
if (longPressAction != null) {
longPressAction();
}
longPressTime = Time.time;
isMouseDown = false;
//Debug.Log("长按NonUI");
}
}
}
#region 判断是否点击在UI上
/// <summary>
/// Cast a ray to test if Input.mousePosition is over any UI object in EventSystem.current. This is a replacement
/// for IsPointerOverGameObject() which does not work on Android in 4.6.0f3
/// </summary>
private bool IsPointerOverUIObject()
{
if (EventSystem.current == null)
return false;
// Referencing this code for GraphicRaycaster https://gist.github.com/stramit/ead7ca1f432f3c0f181f
// the ray cast appears to require only eventData.position.
PointerEventData eventDataCurrentPosition = new PointerEventData(EventSystem.current);
eventDataCurrentPosition.position = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
List<RaycastResult> results = new List<RaycastResult>();
EventSystem.current.RaycastAll(eventDataCurrentPosition, results);
return results.Count > 0;
}
/// <summary>
/// Cast a ray to test if screenPosition is over any UI object in canvas. This is a replacement
/// for IsPointerOverGameObject() which does not work on Android in 4.6.0f3
/// </summary>
private bool IsPointerOverUIObject(Canvas canvas, Vector2 screenPosition)
{
if (EventSystem.current == null)
return false;
// Referencing this code for GraphicRaycaster https://gist.github.com/stramit/ead7ca1f432f3c0f181f
// the ray cast appears to require only eventData.position.
PointerEventData eventDataCurrentPosition = new PointerEventData(EventSystem.current);
eventDataCurrentPosition.position = screenPosition;
GraphicRaycaster uiRaycaster = canvas.gameObject.GetComponent<GraphicRaycaster>();
List<RaycastResult> results = new List<RaycastResult>();
uiRaycaster.Raycast(eventDataCurrentPosition, results);
return results.Count > 0;
}
#endregion 判断是否点击在UI上
// 双击事件
Action doubleClickAction;
// 长按事件
Action longPressAction;
// 是否按下
private bool isMouseDown = false;
// 长按触发时长
private float longPressIntervalTime = 0.6f;
// 长按计时器
private float longPressTime = 0f;
}
}
2、TestDoubleClickLongPressNonUIWrapper
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XANTools;
public class TestDoubleClickLongPressNonUIWrapper : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
DoubleClickLongPressNonUIWrapper.Instance.Init(()=> { Debug.Log("双击NonUI"); },
() => { Debug.Log("长按NonUI"); });
}
}
3、MonoSingleton
using UnityEngine;
public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour
{
private static T instance = null;
private static readonly object locker = new object();
private static bool bAppQuitting;
public static T Instance
{
get
{
if (bAppQuitting)
{
instance = null;
return instance;
}
lock (locker)
{
if (instance == null)
{
// 保证场景中只有一个 单例
T[] managers = Object.FindObjectsOfType(typeof(T)) as T[];
if (managers.Length != 0)
{
if (managers.Length == 1)
{
instance = managers[0];
instance.gameObject.name = typeof(T).Name;
return instance;
}
else
{
Debug.LogError("Class " + typeof(T).Name + " exists multiple times in violation of singleton pattern. Destroying all copies");
foreach (T manager in managers)
{
Destroy(manager.gameObject);
}
}
}
var singleton = new GameObject();
instance = singleton.AddComponent<T>();
singleton.name = "(singleton)" + typeof(T);
singleton.hideFlags = HideFlags.None;
DontDestroyOnLoad(singleton);
}
instance.hideFlags = HideFlags.None;
return instance;
}
}
}
protected virtual void Awake()
{
bAppQuitting = false;
}
protected virtual void OnDestroy()
{
bAppQuitting = true;
}
}
附录(备注):
1、DoubleClickLongPressNonUIWrapper(这个脚本在移动端Touch Input Module输入模式下不正常,弃用了)
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
namespace XANTools
{
/// <summary>
/// 非 UI处的双击和长按事件封装
/// 注意,场景中一定要有 EventSystem 组件
/// </summary>
public class DoubleClickLongPressNonUIWrapper : MonoSingleton<DoubleClickLongPressNonUIWrapper>
{
/// <summary>
/// 初始化监听非UI处长按和双击事件
/// </summary>
public void Init(Action doubleClickListener, Action longPressListener) {
doubleClickAction = doubleClickListener;
longPressAction = longPressListener;
}
// Update is called once per frame
void Update()
{
// 长按监听
LongPress();
}
/// <summary>
/// 监听点击非UI处的,双击和长按触发条件监听
/// </summary>
void OnGUI()
{
// 鼠标按下才监控
if (Input.GetMouseButtonDown(0))
{
#if UNITY_EDITOR
// 點擊非UI,是鼠标按下
if (EventSystem.current.IsPointerOverGameObject() == false && Event.current.isMouse && Event.current.type == EventType.MouseDown)
#elif ((UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR)
// 移动端
if (EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId) == false && Event.current.isMouse && Event.current.type == EventType.MouseDown)
#endif
{
#if UNITY_EDITOR
Debug.Log("EventSystem.current.IsPointerOverGameObject() ="+ EventSystem.current.IsPointerOverGameObject());
#elif ((UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR)
Debug.Log("EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId) = " + EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId));
#endif
// 监听长按事件
isMouseDown = true;
longPressTime = Time.time;
// 双击事件
if (Event.current.clickCount == 2)
{
if (doubleClickAction != null)
{
doubleClickAction();
}
//Debug.Log("双击NonUI");
}
}
}
else if (Event.current.type == EventType.MouseUp)
{
Debug.Log("MouseUp");
isMouseDown = false;
}
}
/// <summary>
/// 长按监听
/// 1、触发一次,需要松开再次长按才能触发下一次
/// </summary>
void LongPress() {
if (isMouseDown == true)
{
if (Time.time - longPressTime > longPressIntervalTime)
{
// 触发长按
if (longPressAction != null) {
longPressAction();
}
longPressTime = Time.time;
isMouseDown = false;
//Debug.Log("长按NonUI");
}
}
}
// 双击事件
Action doubleClickAction;
// 长按事件
Action longPressAction;
// 是否按下
private bool isMouseDown = false;
// 长按触发时长
private float longPressIntervalTime = 0.6f;
// 长按计时器
private float longPressTime = 0f;
}
}