NxrUIGraphicRaycaster.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.EventSystems;
  5. using UnityEngine.UI;
  6. namespace Nxr.Internal
  7. {
  8. public class NxrUIGraphicRaycaster : GraphicRaycaster
  9. {
  10. protected Canvas currentCanvas;
  11. protected Vector2 lastKnownPosition;
  12. protected const float UI_CONTROL_OFFSET = 0.00001f;
  13. [NonSerialized]
  14. // Use a static to prevent list reallocation. We only need one of these globally (single main thread), and only to hold temporary data
  15. private static List<RaycastResult> s_RaycastResults = new List<RaycastResult>();
  16. public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
  17. {
  18. if (canvas == null)
  19. {
  20. return;
  21. }
  22. var ray = new Ray(eventData.pointerCurrentRaycast.worldPosition, eventData.pointerCurrentRaycast.worldNormal);
  23. Raycast(canvas, eventCamera, ray, ref s_RaycastResults);
  24. SetNearestRaycast(ref eventData, ref resultAppendList, ref s_RaycastResults);
  25. s_RaycastResults.Clear();
  26. }
  27. protected virtual void SetNearestRaycast(ref PointerEventData eventData, ref List<RaycastResult> resultAppendList, ref List<RaycastResult> raycastResults)
  28. {
  29. RaycastResult? nearestRaycast = null;
  30. for (var index = 0; index < raycastResults.Count; index++)
  31. {
  32. RaycastResult castResult = raycastResults[index];
  33. castResult.index = resultAppendList.Count;
  34. if (!nearestRaycast.HasValue || castResult.distance < nearestRaycast.Value.distance)
  35. {
  36. nearestRaycast = castResult;
  37. }
  38. resultAppendList.Add(castResult);
  39. }
  40. if (nearestRaycast.HasValue)
  41. {
  42. eventData.position = nearestRaycast.Value.screenPosition;
  43. eventData.delta = eventData.position - lastKnownPosition;
  44. lastKnownPosition = eventData.position;
  45. eventData.pointerCurrentRaycast = nearestRaycast.Value;
  46. }
  47. }
  48. protected virtual float GetHitDistance(Ray ray)
  49. {
  50. var hitDistance = float.MaxValue;
  51. if (canvas.renderMode != RenderMode.ScreenSpaceOverlay && blockingObjects != BlockingObjects.None)
  52. {
  53. var maxDistance = Vector3.Distance(ray.origin, canvas.transform.position);
  54. if (blockingObjects == BlockingObjects.ThreeD || blockingObjects == BlockingObjects.All)
  55. {
  56. RaycastHit hit;
  57. Physics.Raycast(ray, out hit, maxDistance);
  58. if (hit.collider)
  59. {
  60. hitDistance = hit.distance;
  61. }
  62. }
  63. if (blockingObjects == BlockingObjects.TwoD || blockingObjects == BlockingObjects.All)
  64. {
  65. RaycastHit2D hit = Physics2D.Raycast(ray.origin, ray.direction, maxDistance);
  66. if (hit.collider != null)
  67. {
  68. hitDistance = hit.fraction * maxDistance;
  69. }
  70. }
  71. }
  72. return hitDistance;
  73. }
  74. protected virtual void Raycast(Canvas canvas, Camera eventCamera, Ray ray, ref List<RaycastResult> results)
  75. {
  76. var hitDistance = GetHitDistance(ray);
  77. var canvasGraphics = GraphicRegistry.GetGraphicsForCanvas(canvas);
  78. for (int i = 0; i < canvasGraphics.Count; ++i)
  79. {
  80. var graphic = canvasGraphics[i];
  81. if (graphic.depth == -1 || !graphic.raycastTarget)
  82. {
  83. continue;
  84. }
  85. var graphicTransform = graphic.transform;
  86. Vector3 graphicForward = graphicTransform.forward;
  87. float distance = Vector3.Dot(graphicForward, graphicTransform.position - ray.origin) / Vector3.Dot(graphicForward, ray.direction);
  88. if (distance < 0)
  89. {
  90. continue;
  91. }
  92. //Prevents "flickering hover" on items near canvas center.
  93. if ((distance - UI_CONTROL_OFFSET) > hitDistance)
  94. {
  95. continue;
  96. }
  97. Vector3 position = ray.GetPoint(distance);
  98. Vector2 pointerPosition = eventCamera.WorldToScreenPoint(position);
  99. if (!RectTransformUtility.RectangleContainsScreenPoint(graphic.rectTransform, pointerPosition, eventCamera))
  100. {
  101. continue;
  102. }
  103. if (graphic.Raycast(pointerPosition, eventCamera))
  104. {
  105. var result = new RaycastResult()
  106. {
  107. gameObject = graphic.gameObject,
  108. module = this,
  109. distance = distance,
  110. screenPosition = pointerPosition,
  111. worldPosition = position,
  112. depth = graphic.depth,
  113. sortingLayer = canvas.sortingLayerID,
  114. sortingOrder = canvas.sortingOrder,
  115. };
  116. results.Add(result);
  117. }
  118. }
  119. results.Sort((g1, g2) => g2.depth.CompareTo(g1.depth));
  120. }
  121. protected virtual Canvas canvas
  122. {
  123. get
  124. {
  125. if (currentCanvas != null)
  126. {
  127. return currentCanvas;
  128. }
  129. currentCanvas = gameObject.GetComponent<Canvas>();
  130. return currentCanvas;
  131. }
  132. }
  133. }
  134. }