DefaultOnScreenTargetIndicator.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. using UnityEngine;
  2. using UnityEngine.UI;
  3. namespace ARLocation.MapboxRoutes
  4. {
  5. public class DefaultOnScreenTargetIndicator : AbstractOnScreenTargetIndicator
  6. {
  7. public enum TargetVisibilityState
  8. {
  9. None,
  10. Visible,
  11. OffUp,
  12. OffDown,
  13. OffLeft,
  14. OffRight
  15. }
  16. public enum ArrowDir
  17. {
  18. Left,
  19. Right
  20. }
  21. public Sprite ArrowSprite;
  22. public ArrowDir NeutralArrowDirection;
  23. public float Margin = 20;
  24. private RectTransform indicator;
  25. private Canvas canvas;
  26. private Camera cam;
  27. private Transform camTransform;
  28. private Renderer targetRenderer;
  29. private TargetVisibilityState targetVisibility;
  30. private bool initialized;
  31. public TargetVisibilityState TargetVisibility => targetVisibility;
  32. public override void Init(MapboxRoute route)
  33. {
  34. if (!initialized)
  35. {
  36. var canvasGo = new GameObject("[OnScreenTargetIndicatorCanvas]");
  37. var canvas = canvasGo.AddComponent<Canvas>();
  38. canvas.renderMode = RenderMode.ScreenSpaceOverlay;
  39. var indicatorGo = new GameObject("[OnScreenTargetIndicatorImage]");
  40. indicatorGo.transform.parent = canvasGo.transform;
  41. var indicatorImage = indicatorGo.AddComponent<Image>();
  42. indicatorImage.sprite = ArrowSprite;
  43. indicator = indicatorGo.GetComponent<RectTransform>();
  44. cam = Camera.main;
  45. camTransform = cam.transform;
  46. initialized = true;
  47. }
  48. }
  49. bool isLeftOfCamera(Vector3 targetPos)
  50. {
  51. var camForward = camTransform.forward;
  52. var camPos = camTransform.position;
  53. return Vector3.Dot(Vector3.Cross(camForward, targetPos - camPos), (new Vector3(0, 1, 0))) < 0;
  54. }
  55. bool isBehindCamera(Vector3 targetPos)
  56. {
  57. var camForward = camTransform.forward;
  58. var relative = targetPos - camTransform.position;
  59. return Vector3.Dot(camForward, relative) < 0;
  60. }
  61. bool isVisible(Vector3 targetPos)
  62. {
  63. if (isBehindCamera(targetPos))
  64. {
  65. return false;
  66. }
  67. if (targetRenderer != null)
  68. {
  69. return GeometryUtility.TestPlanesAABB(GeometryUtility.CalculateFrustumPlanes(Camera.main), targetRenderer.bounds);
  70. }
  71. else
  72. {
  73. var p = cam.WorldToScreenPoint(targetPos);
  74. return (p.x >= 0 && p.x <= Screen.width) && (p.y >= 0 && p.y <= Screen.height);
  75. }
  76. }
  77. public override void OnRouteUpdate(SignPostEventArgs args)
  78. {
  79. bool isLeft = isLeftOfCamera(args.TargetPos);
  80. bool isBehind = isBehindCamera(args.TargetPos);
  81. bool isFront = !isBehind;
  82. bool isRight = !isLeft;
  83. if (isVisible(args.TargetPos))
  84. {
  85. this.indicator.gameObject.SetActive(false);
  86. targetVisibility = TargetVisibilityState.Visible;
  87. return;
  88. }
  89. else
  90. {
  91. this.indicator.gameObject.SetActive(true);
  92. }
  93. var p = cam.WorldToScreenPoint(args.TargetPos);
  94. if (p.x < 0)
  95. {
  96. targetVisibility = TargetVisibilityState.OffLeft;
  97. }
  98. else if (p.x >= Screen.width)
  99. {
  100. targetVisibility = TargetVisibilityState.OffRight;
  101. }
  102. else if (p.y < 0)
  103. {
  104. targetVisibility = isBehind ? TargetVisibilityState.OffUp : TargetVisibilityState.OffDown;
  105. }
  106. else
  107. {
  108. targetVisibility = isBehind ? TargetVisibilityState.OffDown : TargetVisibilityState.OffUp;
  109. }
  110. p.x = Mathf.Clamp(p.x, Margin, Screen.width - Margin);
  111. p.y = Mathf.Clamp(p.y, Margin, Screen.height - Margin);
  112. if (isBehind)
  113. {
  114. p.y = Screen.height - p.y;
  115. if (isLeft)
  116. {
  117. p.x = Margin;
  118. targetVisibility = TargetVisibilityState.OffLeft;
  119. }
  120. else
  121. {
  122. p.x = Screen.width - Margin;
  123. targetVisibility = TargetVisibilityState.OffRight;
  124. }
  125. }
  126. indicator.position = p;
  127. switch (targetVisibility)
  128. {
  129. case TargetVisibilityState.OffLeft:
  130. if (NeutralArrowDirection == ArrowDir.Right)
  131. {
  132. indicator.rotation = Quaternion.AngleAxis(180, Vector3.forward);
  133. }
  134. else
  135. {
  136. indicator.rotation = Quaternion.identity;
  137. }
  138. break;
  139. case TargetVisibilityState.OffRight:
  140. if (NeutralArrowDirection == ArrowDir.Right)
  141. {
  142. indicator.rotation = Quaternion.identity;
  143. }
  144. else
  145. {
  146. indicator.rotation = Quaternion.AngleAxis(180, Vector3.forward);
  147. }
  148. break;
  149. case TargetVisibilityState.OffUp:
  150. if (NeutralArrowDirection == ArrowDir.Right)
  151. {
  152. indicator.rotation = Quaternion.AngleAxis(90, Vector3.forward);
  153. }
  154. else
  155. {
  156. indicator.rotation = Quaternion.AngleAxis(-90, Vector3.forward);
  157. }
  158. break;
  159. case TargetVisibilityState.OffDown:
  160. if (NeutralArrowDirection == ArrowDir.Right)
  161. {
  162. indicator.rotation = Quaternion.AngleAxis(-90, Vector3.forward);
  163. }
  164. else
  165. {
  166. indicator.rotation = Quaternion.AngleAxis(90, Vector3.forward);
  167. }
  168. break;
  169. default:
  170. indicator.rotation = Quaternion.identity;
  171. break;
  172. }
  173. }
  174. }
  175. }