using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems; using System.Collections.Generic; using System; using static UnityEngine.EventSystems.PointerEventData; using Rokid.UXR.Utility; namespace Rokid.UXR.Interaction { public abstract class BaseRayCaster : MonoBehaviour { /// /// Threshold for determining whether to start dragging /// public float m_DragThreshold = 0.01f; /// /// Cache the current hovering object /// [SerializeField] protected GameObject m_HoveringObj; /// /// Cache selected object /// [SerializeField] protected GameObject m_SelectedObj; [SerializeField] protected GameObject m_FirstSelectedObj; [Tooltip("This attribute only affects UI graphics, detecting the front and back sides of the UI")] [SerializeField] private bool ignoreReversedGraphics = true; [Tooltip("Is detect UI graphics")] [SerializeField] private bool raycastGraphic = true; [Tooltip("Is detect physical collisions")] [SerializeField] private bool raycastPhysical = true; [SerializeField] protected Transform rayOrigin; protected Ray ray; protected Vector3 oriHitPoint; /// The hits. private static readonly RaycastHit[] hits = new RaycastHit[64]; /// /// 点击的阈值 /// protected float clickTime = 0.5f; protected float pressTime = 0; /// /// 是否正在在拖拽中 /// [SerializeField] protected bool dragging = false; /// /// 缓存开始拖拽的时候射线原点到触碰点的距离 /// protected float oriHitPointDis = 0; [SerializeField] protected BaseInput m_InputOverride; private BaseInput m_DefaultInput; [SerializeField] protected RaycastResult result; [SerializeField] protected PointerEventData pointerEventData; [SerializeField] protected RayInteractor rayInteractor; [SerializeField] protected Camera eventCamera; [SerializeField] protected bool sendGlobalEvent = true; protected Vector2 screenCenterPoint = new Vector2(Screen.width * 0.5f, Screen.height * 0.5f); public LayerMask raycastMask = (1 << 0 | 1 << 1 | 1 << 2 | 1 << 3 | 1 << 4 | 1 << 5 | 1 << 6 | 1 << 7); private static readonly Comparison s_RaycastComparer = RaycastComparer; protected readonly List sortedRaycastResults = new List(); #region Event names can be overwritten protected string pointerEnter = "OnRayPointerEnter"; protected string pointerExit = "OnRayPointerExit"; protected string pointerHover = "OnRayPointerHover"; protected string pointerClick = "OnRayPointerClick"; protected string dragBegin = "OnRayBeginDrag"; protected string drag = "OnRayDrag"; protected string dragEnd = "OnRayEndDrag"; #endregion public BaseInput input { get { if (m_InputOverride != null) { return m_InputOverride; } if (m_DefaultInput == null) { BaseInput[] components = GetComponents(); foreach (BaseInput baseInput in components) { if (baseInput != null && baseInput.GetType() == typeof(BaseInput)) { m_DefaultInput = baseInput; break; } } if (m_DefaultInput == null) { m_DefaultInput = base.gameObject.AddComponent(); } } return m_DefaultInput; } } public BaseInput inputOverride { get { return m_InputOverride; } set { m_InputOverride = value; } } protected virtual void Init() { pointerEventData = new PointerEventData(EventSystem.current); pointerEventData.button = InputButton.Left; if (rayInteractor == null) { rayInteractor = GetComponent(); } } private int GetRayIdentifier() { if (rayInteractor == null) { return 0; } else { return rayInteractor.realId; } } protected virtual void Start() { Init(); if (eventCamera == null) { eventCamera = GetEventCamera(); } } protected virtual Camera GetEventCamera() { return MainCameraCache.mainCamera; } protected virtual void OnDestroy() { } protected virtual void OnDisable() { if (dragging) { RKLog.Debug("====BaseRayCaster====: dragEnd"); if (!ProcessEndDrag(ray)) m_SelectedObj?.SendMessageUpwards(dragEnd, SendMessageOptions.DontRequireReceiver); } else { if (pressTime < clickTime && m_SelectedObj == result.gameObject && result.gameObject != null) //发送点击事件 m_SelectedObj?.SendMessageUpwards(pointerClick, SendMessageOptions.DontRequireReceiver); } dragging = false; m_SelectedObj = null; m_FirstSelectedObj = null; } protected void UpdatePointerEventData() { pointerEventData.pointerCurrentRaycast = result; pointerEventData.pointerId = GetRayIdentifier(); } private void Update() { if (rayOrigin != null) { ray.origin = rayOrigin.position; ray.direction = rayOrigin.forward; } else { ray.origin = transform.position; ray.direction = transform.forward; } #if UNITY_EDITOR Debug.DrawRay(ray.origin, ray.direction, Color.red); #endif if (input.GetMouseButtonDown(0)) { if (!TriggerPointerDown()) { pressTime = 0; Raycast(ray, Mathf.Infinity, sortedRaycastResults); result = FirstRaycastResult(); UpdatePointerEventData(); if (result.gameObject != null) { m_FirstSelectedObj = result.gameObject; OnFirstSelect(); } else { m_FirstSelectedObj = null; ProcessNothingDownEvent(pointerEventData); } } } else if (!dragging) { Raycast(ray, Mathf.Infinity, sortedRaycastResults); result = FirstRaycastResult(); UpdatePointerEventData(); if (input.GetMouseButtonUp(0) && result.gameObject == null) { ProcessNothingUpEvent(pointerEventData); } if (result.gameObject != null) { if (m_HoveringObj == null) { m_HoveringObj = result.gameObject; RKLog.Debug($"====BaseRayCaster====: pointerEnter:{m_HoveringObj.name}"); m_HoveringObj.SendMessageUpwards(pointerEnter, SendMessageOptions.DontRequireReceiver); if (sendGlobalEvent) { RKPointerLisener.OnPointerEnter?.Invoke(pointerEventData); } } else { if (m_HoveringObj != result.gameObject) { RKLog.Debug($"====BaseRayCaster====: pointerExit: {m_HoveringObj.name}"); m_HoveringObj.SendMessageUpwards(pointerExit, SendMessageOptions.DontRequireReceiver); if (sendGlobalEvent) { RKPointerLisener.OnPointerExit?.Invoke(pointerEventData, m_HoveringObj); } m_HoveringObj = result.gameObject; RKLog.Debug($"====BaseRayCaster====: pointerEnter {m_HoveringObj.name}"); m_HoveringObj.SendMessageUpwards(pointerEnter, SendMessageOptions.DontRequireReceiver); if (sendGlobalEvent) { RKPointerLisener.OnPointerEnter?.Invoke(pointerEventData); } } m_HoveringObj.SendMessageUpwards(pointerHover, result, SendMessageOptions.DontRequireReceiver); if (sendGlobalEvent) RKPointerLisener.OnPointerHover?.Invoke(pointerEventData); if (input.GetMouseButton(0) && !dragging) { pressTime += Time.deltaTime; if (m_FirstSelectedObj == m_HoveringObj) { m_SelectedObj = m_HoveringObj; Vector3 delta = CalDragDelta();//拖拽移动的向量 RKLog.Debug($"====BaseRayCaster====: JudgeDrag {delta},{m_DragThreshold}"); if (CanDrag(delta)) { OnBeginDrag(); RKLog.Debug("====BaseRayCaster====: dragBegin"); if (!ProcessBeginDrag(ray)) { m_SelectedObj.SendMessageUpwards(dragBegin, pointerEventData, SendMessageOptions.DontRequireReceiver); if (sendGlobalEvent) RKPointerLisener.OnPointerDragBegin?.Invoke(pointerEventData); } dragging = true; } } } } } else if (m_HoveringObj != null) { RKLog.Debug($"====BaseRayCaster====: pointerExit: {m_HoveringObj.name}"); m_HoveringObj.SendMessageUpwards(pointerExit, SendMessageOptions.DontRequireReceiver); RKPointerLisener.OnPointerExit?.Invoke(pointerEventData, m_HoveringObj); result.gameObject = null; m_HoveringObj = null; } } if (dragging) { ProcessDrag(ray); if (sendGlobalEvent) RKPointerLisener.OnPointerDrag?.Invoke(pointerEventData); } //处理点击释放逻辑 if (input.GetMouseButtonUp(0)) { if (dragging == false) { if (pressTime < clickTime && m_SelectedObj == result.gameObject && result.gameObject != null) { //发送点击事件 m_SelectedObj.SendMessageUpwards(pointerClick, SendMessageOptions.DontRequireReceiver); } m_SelectedObj = null; } } //处理拖拽释放逻辑 if (dragging && DragRelease()) { if (m_SelectedObj != null) { RKLog.Debug("====BaseRayCaster====: dragEnd 2"); if (!ProcessEndDrag(ray)) { m_SelectedObj.SendMessageUpwards(dragEnd, SendMessageOptions.DontRequireReceiver); if (sendGlobalEvent) RKPointerLisener.OnPointerDragEnd?.Invoke(pointerEventData); } m_SelectedObj = null; } dragging = false; } } protected virtual bool CanDrag(Vector3 delta) { return !dragging && Vector3.SqrMagnitude(delta) >= m_DragThreshold * m_DragThreshold; } protected virtual bool DragRelease() { return !input.GetMouseButton(0); } protected virtual bool TriggerPointerDown() { return false; } protected virtual void OnFirstSelect() { oriHitPoint = result.worldPosition; } protected virtual Vector3 CalDragDelta() { RKLog.Debug($"====BaseRayCaster==== dragDelta:{result.worldPosition - oriHitPoint}"); return result.worldPosition - oriHitPoint;//拖拽移动的向量 } protected virtual void OnBeginDrag() { oriHitPointDis = Vector3.Distance(result.worldPosition, ray.origin); oriHitPoint = result.worldPosition; } protected virtual bool ProcessBeginDrag(Ray ray) { return false; } protected virtual bool ProcessEndDrag(Ray ray) { return false; } protected virtual bool ProcessDrag(Ray ray) { return false; } protected virtual void ProcessNothingUpEvent(PointerEventData eventData) { if (sendGlobalEvent) RKPointerLisener.OnPointerNothingUp?.Invoke(eventData); } protected virtual void ProcessNothingDownEvent(PointerEventData eventData) { if (sendGlobalEvent) RKPointerLisener.OnPointerNothingDown?.Invoke(eventData); } private static int RaycastComparer(RaycastResult lhs, RaycastResult rhs) { if (lhs.module != rhs.module) { var lhsEventCamera = lhs.module.eventCamera; var rhsEventCamera = rhs.module.eventCamera; if (lhsEventCamera != null && rhsEventCamera != null && lhsEventCamera.depth != rhsEventCamera.depth) { // need to reverse the standard compareTo if (lhsEventCamera.depth < rhsEventCamera.depth) return 1; if (lhsEventCamera.depth == rhsEventCamera.depth) return 0; return -1; } if (lhs.module.sortOrderPriority != rhs.module.sortOrderPriority) return rhs.module.sortOrderPriority.CompareTo(lhs.module.sortOrderPriority); if (lhs.module.renderOrderPriority != rhs.module.renderOrderPriority) return rhs.module.renderOrderPriority.CompareTo(lhs.module.renderOrderPriority); } if (lhs.sortingLayer != rhs.sortingLayer) { // Uses the layer value to properly compare the relative order of the layers. var rid = SortingLayer.GetLayerValueFromID(rhs.sortingLayer); var lid = SortingLayer.GetLayerValueFromID(lhs.sortingLayer); return rid.CompareTo(lid); } if (lhs.sortingOrder != rhs.sortingOrder) return rhs.sortingOrder.CompareTo(lhs.sortingOrder); // comparing depth only makes sense if the two raycast results have the same root canvas (case 912396) if (lhs.module != null && rhs.module != null) { if (lhs.depth != rhs.depth && lhs.module.rootRaycaster == rhs.module.rootRaycaster) return rhs.depth.CompareTo(lhs.depth); } if (lhs.distance != rhs.distance) return lhs.distance.CompareTo(rhs.distance); return lhs.index.CompareTo(rhs.index); } /// Raycasts. /// The ray. /// The distance. /// The raycast results. public void Raycast(Ray ray, float distance, List raycastResults) { raycastResults.Clear(); if (raycastGraphic) GraphicRaycast(ignoreReversedGraphics, ray, distance, raycastResults); if (raycastPhysical) PhysicsRaycast(ray, distance, raycastResults); raycastResults.Sort(s_RaycastComparer); } /// Physics raycast. /// The ray. /// The distance. /// The raycast results. public virtual void PhysicsRaycast(Ray ray, float distance, List raycastResults) { var hitCount = Physics.RaycastNonAlloc(ray, hits, distance, raycastMask); for (int i = 0; i < hitCount; ++i) { RKLog.Debug("=====BaseRayCast==== Physical Raycast:" + hits[i].collider.gameObject.name); if (!hits[i].collider.GetComponent() && !hits[i].collider.GetComponentInParent()) continue; raycastResults.Add(new RaycastResult { gameObject = hits[i].collider.gameObject, distance = hits[i].distance, worldPosition = hits[i].point, worldNormal = hits[i].normal, screenPosition = screenCenterPoint, index = raycastResults.Count, sortingLayer = 0, sortingOrder = 0 }); } } public RaycastResult FirstRaycastResult() { for (int i = 0, imax = sortedRaycastResults.Count; i < imax; ++i) { RKLog.Debug("=====BaseRayCast==== FirstRaycastResult:" + sortedRaycastResults[i].gameObject.name); return sortedRaycastResults[i]; } return default(RaycastResult); } public RaycastResult FirstRaycastResult(List sortedRaycastResults) { for (int i = 0, imax = sortedRaycastResults.Count; i < imax; ++i) { // if (!sortedRaycastResults[i].isValid) // continue; return sortedRaycastResults[i]; } return default(RaycastResult); } /// /// 获取事件位置 /// protected virtual Vector2 GetEventPosition(Ray ray, Camera eventCamera, Graphic graphic) { if (Utils.GetWorldPointInRectangle(graphic, ray, out Vector3 worldPoint)) { return eventCamera.WorldToScreenPoint(worldPoint); } return screenCenterPoint; } /// Graphic raycast. /// The canvas. /// True to ignore reversed graphics. /// The ray. /// The distance. /// The raycaster. /// The raycast results. public virtual void GraphicRaycast(bool ignoreReversedGraphics, Ray ray, float distance, List raycastResults) { foreach (Canvas canvas in CanvasRegister.canvasList) { var graphics = GraphicRegistry.GetGraphicsForCanvas(canvas); for (int i = 0; i < graphics.Count; ++i) { var graphic = graphics[i]; // -1 means it hasn't been processed by the canvas, which means it isn't actually drawn if (graphic.depth == -1 || !graphic.raycastTarget || graphic.canvasRenderer.cull) continue; Vector2 screenPoint = GetEventPosition(ray, eventCamera, graphic); if (!RectTransformUtility.RectangleContainsScreenPoint(graphic.rectTransform, screenPoint, eventCamera)) continue; if (eventCamera != null && eventCamera.WorldToScreenPoint(graphic.rectTransform.position).z > eventCamera.farClipPlane) continue; if (eventCamera != null && ignoreReversedGraphics && Vector3.Dot(eventCamera.transform.forward, graphic.transform.forward) < 0) continue; float dist; //平面和射线的交点 new Plane(graphic.transform.forward, graphic.transform.position).Raycast(ray, out dist); if (float.IsNaN(dist) || dist > distance) continue; if (graphic.Raycast(screenPoint, eventCamera)) { raycastResults.Add(new RaycastResult { gameObject = graphic.gameObject, distance = dist, worldPosition = ray.GetPoint(dist), worldNormal = -graphic.transform.forward, screenPosition = screenCenterPoint, index = raycastResults.Count, depth = graphic.depth, sortingLayer = canvas.sortingLayerID, sortingOrder = canvas.sortingOrder }); } } } } } }