using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; namespace JC.Unity { /**模拟鼠标-场景中需要存在EventSystem */ public class SimulateMouse : MonoBehaviour { [SerializeField] CanvasScaler canvasScaler; [SerializeField] RectTransform mousePointer; [NonSerialized] public Action OnPointerEnter; [NonSerialized] public Action OnPointerClick; ScreenRayRaycasterUIWrapper mScreenRayRaycasterUIWrapper = new ScreenRayRaycasterUIWrapper(); void Awake() { mousePointer.anchorMin = Vector2.zero; mousePointer.anchorMax = Vector2.zero; if (mScreenRayRaycasterUIWrapper != null) { // 把鼠标的位置传 Ray 发射位置 mScreenRayRaycasterUIWrapper.Init(this, () => { Vector2 pos = mousePointer.anchoredPosition; pos.x *= (float) Screen.width / GetScaleScreenWidth(); pos.y *= (float) Screen.height / GetScaleScreenHeight(); return pos; }); } } void OnDisable() { mScreenRayRaycasterUIWrapper.PressUp(); } void OnEnable() { MakeMouseToScreenCenter(); } void Update() { mScreenRayRaycasterUIWrapper.Update(); } Vector2 ClampValue(Vector2 val) { val.x = val.x < 0 ? 0 : val.x; val.y = val.y < 0 ? 0 : val.y; float maxWidth = GetScaleScreenWidth(); float maxHeight = GetScaleScreenHeight(); val.x = val.x > maxWidth ? maxWidth : val.x; val.y = val.y > maxHeight ? maxHeight : val.y; return val; } #region 对外提供的接口 public float GetScaleScreenWidth() { float width = Screen.width; if (canvasScaler.uiScaleMode == CanvasScaler.ScaleMode.ScaleWithScreenSize) { if (canvasScaler.screenMatchMode == CanvasScaler.ScreenMatchMode.MatchWidthOrHeight) { float height = Screen.height; if (canvasScaler.matchWidthOrHeight == 1) { width *= canvasScaler.referenceResolution.y / height; } else if (canvasScaler.matchWidthOrHeight == 0) { width = canvasScaler.referenceResolution.x; } } } return width; } public float GetScaleScreenHeight() { float height = Screen.height; if (canvasScaler.uiScaleMode == CanvasScaler.ScaleMode.ScaleWithScreenSize) { if (canvasScaler.screenMatchMode == CanvasScaler.ScreenMatchMode.MatchWidthOrHeight) { float width = Screen.width; if (canvasScaler.matchWidthOrHeight == 0) { height *= canvasScaler.referenceResolution.x / width; } else if (canvasScaler.matchWidthOrHeight == 1) { height = canvasScaler.referenceResolution.y; } } } return height; } public void MoveMousePointer(Vector2 deltaPos) { if (mousePointer == null) return; Vector2 val = mousePointer.anchoredPosition; val += deltaPos; mousePointer.anchoredPosition = ClampValue(val); } public void SetMousePointerPosition(Vector2 pos) { if (mousePointer == null) return; mousePointer.position = pos; } public void ClickMousePointer() { mScreenRayRaycasterUIWrapper.Click(); } public void MakeMouseToScreenCenter() { mousePointer.anchoredPosition = new Vector2(GetScaleScreenWidth()/2,GetScaleScreenHeight()/2); } public Selectable GetCurrentSelectable() { return mScreenRayRaycasterUIWrapper.m_currentSelectable; } public void onSetNullToSelectable(Selectable target) { //mScreenRayRaycasterUIWrapper.OnSelect(target); } #endregion 对外提供的接口 } public class ScreenRayRaycasterUIWrapper { public Selectable m_currentSelectable = null; RaycastResult m_currentRaycastResult; IPointerClickHandler m_clickHandler; IDragHandler m_dragHandler; EventSystem m_eventSystem; PointerEventData m_pointerEvent; SimulateMouse mSimulateMouse; Func mFunPointerPos; private bool mIsClick = false; private bool mIsPress = false; private bool mIsDrag = false; public void Init(SimulateMouse simulateMouse, Func funPointerPos) { mSimulateMouse = simulateMouse; m_eventSystem = EventSystem.current; m_pointerEvent = new PointerEventData(m_eventSystem); m_pointerEvent.button = PointerEventData.InputButton.Left; mFunPointerPos = funPointerPos; } public void Update() { m_pointerEvent.position = mFunPointerPos.Invoke(); List raycastResults = new List(); m_eventSystem.RaycastAll(m_pointerEvent, raycastResults); // Detect selectable bool hasRaycastSelectable = false; if (raycastResults.Count > 0) { var result = raycastResults[0]; //只检测第一个 var newSelectable = result.gameObject.GetComponentInParent(); if (newSelectable) { hasRaycastSelectable = true; if (newSelectable != m_currentSelectable) { Select(newSelectable); m_currentRaycastResult = result; } } } if (!hasRaycastSelectable) { if (m_currentSelectable) { Select(null); } } if (m_currentSelectable) { if (m_clickHandler != null && mIsClick) { try { mSimulateMouse.OnPointerClick?.Invoke(m_currentSelectable); } catch (System.Exception e) { Debug.LogError(e.Message); } m_clickHandler.OnPointerClick(m_pointerEvent); Select(null); } else if (m_dragHandler != null && mIsPress) { mIsDrag = true; m_pointerEvent.pointerPressRaycast = m_currentRaycastResult; m_dragHandler.OnDrag(m_pointerEvent); } } mIsClick = false; } public void OnSelect(Selectable s) { Select(s); } void Select(Selectable s) { if (mIsDrag == true) return; // if (m_currentSelectable) { //退出选中 // m_currentSelectable.OnPointerExit(m_pointerEvent); // } m_currentSelectable = s; if (m_currentSelectable) { try { mSimulateMouse.OnPointerEnter?.Invoke(m_currentSelectable); } catch (System.Exception e) { Debug.LogError(e.Message); Debug.LogError(e.StackTrace); } // m_currentSelectable.OnPointerEnter(m_pointerEvent); //选中 m_clickHandler = m_currentSelectable.GetComponent(); m_dragHandler = m_currentSelectable.GetComponent(); } else { m_clickHandler = null; m_dragHandler = null; } } //模拟点击 public void Click() { mIsClick = true; } //模拟按下 public void PressDown() { mIsClick = true; mIsPress = true; mIsDrag = false; } //模拟松开 public void PressUp() { mIsClick = false; mIsPress = false; mIsDrag = false; Select(null); } } }