暫無描述
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TrackedDeviceRaycaster.cs 9.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. #if PACKAGE_DOCS_GENERATION || UNITY_INPUT_SYSTEM_ENABLE_UI
  2. using System;
  3. using System.Collections.Generic;
  4. using UnityEngine.EventSystems;
  5. using UnityEngine.InputSystem.Utilities;
  6. using UnityEngine.Serialization;
  7. using UnityEngine.UI;
  8. namespace UnityEngine.InputSystem.UI
  9. {
  10. /// <summary>
  11. /// Raycasting implementation for use with <see cref="TrackedDevice"/>s.
  12. /// </summary>
  13. /// <remarks>
  14. /// This component needs to be added alongside the <c>Canvas</c> component. Usually, raycasting is
  15. /// performed by the <c>GraphicRaycaster</c> component found there but for 3D raycasting necessary for
  16. /// tracked devices, this component is required.
  17. /// </remarks>
  18. [AddComponentMenu("Event/Tracked Device Raycaster")]
  19. [RequireComponent(typeof(Canvas))]
  20. public class TrackedDeviceRaycaster : BaseRaycaster
  21. {
  22. private struct RaycastHitData
  23. {
  24. public RaycastHitData(Graphic graphic, Vector3 worldHitPosition, Vector2 screenPosition, float distance)
  25. {
  26. this.graphic = graphic;
  27. this.worldHitPosition = worldHitPosition;
  28. this.screenPosition = screenPosition;
  29. this.distance = distance;
  30. }
  31. public Graphic graphic { get; }
  32. public Vector3 worldHitPosition { get; }
  33. public Vector2 screenPosition { get; }
  34. public float distance { get; }
  35. }
  36. public override Camera eventCamera
  37. {
  38. get
  39. {
  40. var myCanvas = canvas;
  41. return myCanvas != null ? myCanvas.worldCamera : null;
  42. }
  43. }
  44. public LayerMask blockingMask
  45. {
  46. get => m_BlockingMask;
  47. set => m_BlockingMask = value;
  48. }
  49. public bool checkFor3DOcclusion
  50. {
  51. get => m_CheckFor3DOcclusion;
  52. set => m_CheckFor3DOcclusion = value;
  53. }
  54. public bool checkFor2DOcclusion
  55. {
  56. get => m_CheckFor2DOcclusion;
  57. set => m_CheckFor2DOcclusion = value;
  58. }
  59. public bool ignoreReversedGraphics
  60. {
  61. get => m_IgnoreReversedGraphics;
  62. set => m_IgnoreReversedGraphics = value;
  63. }
  64. public float maxDistance
  65. {
  66. get => m_MaxDistance;
  67. set => m_MaxDistance = value;
  68. }
  69. protected override void OnEnable()
  70. {
  71. base.OnEnable();
  72. s_Instances.AppendWithCapacity(this);
  73. }
  74. protected override void OnDisable()
  75. {
  76. var index = s_Instances.IndexOfReference(this);
  77. if (index != -1)
  78. s_Instances.RemoveAtByMovingTailWithCapacity(index);
  79. base.OnDisable();
  80. }
  81. public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
  82. {
  83. if (eventData is ExtendedPointerEventData trackedEventData && trackedEventData.pointerType == UIPointerType.Tracked)
  84. PerformRaycast(trackedEventData, resultAppendList);
  85. }
  86. // Cached instances for raycasts hits to minimize GC.
  87. [NonSerialized] private List<RaycastHitData> m_RaycastResultsCache = new List<RaycastHitData>();
  88. internal void PerformRaycast(ExtendedPointerEventData eventData, List<RaycastResult> resultAppendList)
  89. {
  90. if (canvas == null)
  91. return;
  92. if (eventCamera == null)
  93. return;
  94. var ray = new Ray(eventData.trackedDevicePosition, eventData.trackedDeviceOrientation * Vector3.forward);
  95. var hitDistance = m_MaxDistance;
  96. #if UNITY_INPUT_SYSTEM_ENABLE_PHYSICS
  97. if (m_CheckFor3DOcclusion)
  98. {
  99. if (Physics.Raycast(ray, out var hit, maxDistance: hitDistance, layerMask: m_BlockingMask))
  100. hitDistance = hit.distance;
  101. }
  102. #endif
  103. #if UNITY_INPUT_SYSTEM_ENABLE_PHYSICS2D
  104. if (m_CheckFor2DOcclusion)
  105. {
  106. var raycastDistance = hitDistance;
  107. var hits = Physics2D.GetRayIntersection(ray, raycastDistance, m_BlockingMask);
  108. if (hits.collider != null)
  109. hitDistance = hits.distance;
  110. }
  111. #endif
  112. m_RaycastResultsCache.Clear();
  113. SortedRaycastGraphics(canvas, ray, m_RaycastResultsCache);
  114. // Now that we have a list of sorted hits, process any extra settings and filters.
  115. for (var i = 0; i < m_RaycastResultsCache.Count; i++)
  116. {
  117. var validHit = true;
  118. var hitData = m_RaycastResultsCache[i];
  119. var go = hitData.graphic.gameObject;
  120. if (m_IgnoreReversedGraphics)
  121. {
  122. var forward = ray.direction;
  123. var goDirection = go.transform.rotation * Vector3.forward;
  124. validHit = Vector3.Dot(forward, goDirection) > 0;
  125. }
  126. validHit &= hitData.distance < hitDistance;
  127. if (validHit)
  128. {
  129. var castResult = new RaycastResult
  130. {
  131. gameObject = go,
  132. module = this,
  133. distance = hitData.distance,
  134. index = resultAppendList.Count,
  135. depth = hitData.graphic.depth,
  136. worldPosition = hitData.worldHitPosition,
  137. screenPosition = hitData.screenPosition,
  138. };
  139. resultAppendList.Add(castResult);
  140. }
  141. }
  142. }
  143. internal static InlinedArray<TrackedDeviceRaycaster> s_Instances;
  144. private static readonly List<RaycastHitData> s_SortedGraphics = new List<RaycastHitData>();
  145. private void SortedRaycastGraphics(Canvas canvas, Ray ray, List<RaycastHitData> results)
  146. {
  147. var graphics = GraphicRegistry.GetGraphicsForCanvas(canvas);
  148. s_SortedGraphics.Clear();
  149. for (var i = 0; i < graphics.Count; ++i)
  150. {
  151. var graphic = graphics[i];
  152. if (graphic.depth == -1)
  153. continue;
  154. Vector3 worldPos;
  155. float distance;
  156. if (RayIntersectsRectTransform(graphic.rectTransform, ray, out worldPos, out distance))
  157. {
  158. Vector2 screenPos = eventCamera.WorldToScreenPoint(worldPos);
  159. // mask/image intersection - See Unity docs on eventAlphaThreshold for when this does anything
  160. if (graphic.Raycast(screenPos, eventCamera))
  161. {
  162. s_SortedGraphics.Add(new RaycastHitData(graphic, worldPos, screenPos, distance));
  163. }
  164. }
  165. }
  166. s_SortedGraphics.Sort((g1, g2) => g2.graphic.depth.CompareTo(g1.graphic.depth));
  167. results.AddRange(s_SortedGraphics);
  168. }
  169. private static bool RayIntersectsRectTransform(RectTransform transform, Ray ray, out Vector3 worldPosition, out float distance)
  170. {
  171. var corners = new Vector3[4];
  172. transform.GetWorldCorners(corners);
  173. var plane = new Plane(corners[0], corners[1], corners[2]);
  174. float enter;
  175. if (plane.Raycast(ray, out enter))
  176. {
  177. var intersection = ray.GetPoint(enter);
  178. var bottomEdge = corners[3] - corners[0];
  179. var leftEdge = corners[1] - corners[0];
  180. var bottomDot = Vector3.Dot(intersection - corners[0], bottomEdge);
  181. var leftDot = Vector3.Dot(intersection - corners[0], leftEdge);
  182. // If the intersection is right of the left edge and above the bottom edge.
  183. if (leftDot >= 0 && bottomDot >= 0)
  184. {
  185. var topEdge = corners[1] - corners[2];
  186. var rightEdge = corners[3] - corners[2];
  187. var topDot = Vector3.Dot(intersection - corners[2], topEdge);
  188. var rightDot = Vector3.Dot(intersection - corners[2], rightEdge);
  189. //If the intersection is left of the right edge, and below the top edge
  190. if (topDot >= 0 && rightDot >= 0)
  191. {
  192. worldPosition = intersection;
  193. distance = enter;
  194. return true;
  195. }
  196. }
  197. }
  198. worldPosition = Vector3.zero;
  199. distance = 0;
  200. return false;
  201. }
  202. [FormerlySerializedAs("ignoreReversedGraphics")]
  203. [SerializeField]
  204. private bool m_IgnoreReversedGraphics;
  205. [FormerlySerializedAs("checkFor2DOcclusion")]
  206. [SerializeField]
  207. private bool m_CheckFor2DOcclusion;
  208. [FormerlySerializedAs("checkFor3DOcclusion")]
  209. [SerializeField]
  210. private bool m_CheckFor3DOcclusion;
  211. [Tooltip("Maximum distance (in 3D world space) that rays are traced to find a hit.")]
  212. [SerializeField] private float m_MaxDistance = 1000;
  213. [SerializeField]
  214. private LayerMask m_BlockingMask;
  215. [NonSerialized]
  216. private Canvas m_Canvas;
  217. private Canvas canvas
  218. {
  219. get
  220. {
  221. if (m_Canvas != null)
  222. return m_Canvas;
  223. m_Canvas = GetComponent<Canvas>();
  224. return m_Canvas;
  225. }
  226. }
  227. }
  228. }
  229. #endif