Hotspot.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. using System;
  2. using UnityEngine;
  3. using UnityEngine.Events;
  4. using Logger = ARLocation.Utils.Logger;
  5. namespace ARLocation
  6. {
  7. [AddComponentMenu("AR+GPS/Hotspot")]
  8. [HelpURL("https://http://docs.unity-ar-gps-location.com/guide/#hotspot")]
  9. [DisallowMultipleComponent]
  10. public class Hotspot : MonoBehaviour
  11. {
  12. [Serializable]
  13. public class OnHotspotActivatedUnityEvent : UnityEvent<GameObject> { }
  14. [Serializable]
  15. public class OnHotspotActivatedWithIdUnityEvent : UnityEvent<GameObject, int> { }
  16. [Serializable]
  17. public class OnHotspotLeaveUnityEvent : UnityEvent<GameObject> { }
  18. [Serializable]
  19. public class OnHotspotLeaveWithIdUnityEvent : UnityEvent<GameObject, int> { }
  20. [Serializable]
  21. public enum PositionModes
  22. {
  23. HotspotCenter,
  24. CameraPosition
  25. };
  26. [Serializable]
  27. public class HotspotSettingsData
  28. {
  29. [Tooltip("The prefab/GameObject that will be instantiated by the Hotspot.")]
  30. public GameObject Prefab;
  31. [Tooltip("The positioning mode. 'HotspotCenter' means the object will be instantiated at the Hotpot's center geo-location. 'CameraPosition' means it will be positioned at the front of the camera.")]
  32. public PositionModes PositionMode;
  33. [Tooltip("The distance from the center that the user must be located to activate the Hotspot.")]
  34. public float ActivationRadius = 4.0f;
  35. [Tooltip("If true, align the instantiated object to face the camera (horizontally).")]
  36. public bool AlignToCamera = true;
  37. [Tooltip("If 'PositionMode' is set to 'CameraPosition', how far from the camera the instantiated object should be placed.")]
  38. public float DistanceFromCamera = 3.0f;
  39. [Tooltip("If true, use raw GPS data, instead of the filtered GPS data.")]
  40. public bool UseRawLocation = true;
  41. [Tooltip("If true, deactivate the hotspot when leaving the radius after entering it. After deactivattion, the hotspot will be available for activation againg when re-entering the radius.")]
  42. public bool DeactivateOnLeave = false;
  43. [Tooltip("An optional integer ID which is passed to the \"On Hotspot Activated With Id\" and \"On Hotspot Leave With Id\" events.")]
  44. [HideInInspector] public int Id = 0;
  45. }
  46. [Serializable]
  47. public class StateData
  48. {
  49. public bool Activated;
  50. public GameObject Instance;
  51. public Location Location;
  52. public bool EmittedLeaveHotspotEvent;
  53. }
  54. public PlaceAtLocation.LocationSettingsData LocationSettings = new PlaceAtLocation.LocationSettingsData();
  55. public HotspotSettingsData HotspotSettings = new HotspotSettingsData();
  56. [Space(4.0f)]
  57. [Header("Debug")]
  58. [Tooltip("When debug mode is enabled, this component will print relevant messages to the console. Filter by 'Hotspot' in the log output to see the messages.")]
  59. public bool DebugMode;
  60. [Space(4.0f)]
  61. [Header("Events")]
  62. [Tooltip("If true, monitor distance to emit the 'OnLeaveHotspot' event. If you don't need it, keep this disabled for better performance.")]
  63. public bool UseOnLeaveHotspotEvent;
  64. [Tooltip("Event for the Hotspot is activated.")]
  65. public OnHotspotActivatedUnityEvent OnHotspotActivated = new OnHotspotActivatedUnityEvent();
  66. [Tooltip("This event will be emited only once, when the user leaves the hotspot area after it is activated.")]
  67. public OnHotspotLeaveUnityEvent OnHotspotLeave = new OnHotspotLeaveUnityEvent();
  68. [Tooltip("This event will be emited only once, when the user leaves the hotspot area after it is activated.")]
  69. [HideInInspector] public OnHotspotLeaveWithIdUnityEvent OnHotspotLeaveWithId = new OnHotspotLeaveWithIdUnityEvent();
  70. [Tooltip("Event for the Hotspot is activated.")]
  71. [HideInInspector] public OnHotspotActivatedWithIdUnityEvent OnHotspotActivatedWithId = new OnHotspotActivatedWithIdUnityEvent();
  72. private readonly StateData state = new StateData();
  73. private Transform root;
  74. private Camera arCamera;
  75. private double currentDistance;
  76. // ReSharper disable once UnusedMember.Global
  77. public GameObject Instance => state.Instance;
  78. // ReSharper disable once MemberCanBePrivate.Global
  79. public Location Location
  80. {
  81. // ReSharper disable once UnusedMember.Global
  82. get => state.Location;
  83. set => state.Location = value;
  84. }
  85. /// <summary>
  86. ///
  87. /// Returns the Hotspot's current position, both before and after
  88. /// activation. Before activation it will return a Horizontal position,
  89. /// i.e., without altiture (that is, with y = 0).
  90. ///
  91. /// </summary>
  92. public Vector3 Position
  93. {
  94. get
  95. {
  96. if (state.Activated)
  97. {
  98. return state.Instance.transform.position;
  99. }
  100. else
  101. {
  102. var arLocMngr = ARLocationManager.Instance;
  103. var arLocPrvdr = ARLocationProvider.Instance;
  104. var pos = Location.GetGameObjectPositionForLocation(
  105. arLocMngr.gameObject.transform,
  106. arLocMngr.Camera.transform.position,
  107. arLocPrvdr.CurrentLocation.ToLocation(),
  108. state.Location,
  109. true
  110. );
  111. pos.y = 0;
  112. return pos;
  113. }
  114. }
  115. }
  116. /// <summary>
  117. /// Returns the current user distance to the Hotspot center.
  118. /// </summary>
  119. public float CurrentDistance => (float)currentDistance;
  120. void Start()
  121. {
  122. var arLocationProvider = ARLocationProvider.Instance;
  123. if (HotspotSettings.UseRawLocation)
  124. {
  125. arLocationProvider.Provider.LocationUpdatedRaw += Provider_LocationUpdated;
  126. }
  127. else
  128. {
  129. arLocationProvider.Provider.LocationUpdated += Provider_LocationUpdated;
  130. }
  131. root = ARLocationManager.Instance.gameObject.transform;
  132. if (state.Location == null)
  133. {
  134. state.Location = LocationSettings.GetLocation();
  135. }
  136. arCamera = ARLocationManager.Instance.MainCamera;
  137. if (arLocationProvider.IsEnabled)
  138. {
  139. Provider_LocationUpdated(arLocationProvider.CurrentLocation, arLocationProvider.LastLocation);
  140. }
  141. }
  142. public void OnDisable()
  143. {
  144. var arLocationProvider = ARLocationProvider.Instance;
  145. if (!arLocationProvider) return;
  146. if (HotspotSettings.UseRawLocation)
  147. {
  148. arLocationProvider.Provider.LocationUpdatedRaw -= Provider_LocationUpdated;
  149. }
  150. else
  151. {
  152. arLocationProvider.Provider.LocationUpdated -= Provider_LocationUpdated;
  153. }
  154. }
  155. public void Restart()
  156. {
  157. Destroy(state.Instance);
  158. state.Instance = null;
  159. state.Activated = false;
  160. state.EmittedLeaveHotspotEvent = false;
  161. }
  162. private void Provider_LocationUpdated(LocationReading currentLocation, LocationReading lastLocation)
  163. {
  164. if (state.Activated) return;
  165. Logger.LogFromMethod("Hotspot", "LocationUpdatedRaw", $"({gameObject.name}): New device location {currentLocation}", DebugMode);
  166. currentDistance = Location.HorizontalDistance(currentLocation.ToLocation(), state.Location);
  167. var delta = Location.HorizontalVectorFromTo(currentLocation.ToLocation(), state.Location);
  168. if (currentDistance <= HotspotSettings.ActivationRadius)
  169. {
  170. ActivateHotspot(new Vector3((float)delta.x, 0, (float)delta.y));
  171. }
  172. else
  173. {
  174. Logger.LogFromMethod("Hotspot", "LocationUpdatedRaw", $"({gameObject.name}): No activation - distance = {currentDistance}", DebugMode);
  175. }
  176. }
  177. private void ActivateHotspot(Vector3 delta)
  178. {
  179. Logger.LogFromMethod("Hotspot", "ActivateHotspot", $"({gameObject.name}): Activating hotspot...", DebugMode);
  180. if (HotspotSettings.Prefab == null) return;
  181. state.Instance = Instantiate(HotspotSettings.Prefab, HotspotSettings.AlignToCamera ? gameObject.transform : root);
  182. switch (HotspotSettings.PositionMode)
  183. {
  184. case PositionModes.HotspotCenter:
  185. state.Instance.transform.position = arCamera.transform.position + delta;
  186. break;
  187. case PositionModes.CameraPosition:
  188. var transform1 = arCamera.transform;
  189. var forward = transform1.forward;
  190. forward.y = 0;
  191. state.Instance.transform.position = transform1.position + forward * HotspotSettings.DistanceFromCamera;
  192. break;
  193. }
  194. if (HotspotSettings.AlignToCamera)
  195. {
  196. state.Instance.transform.LookAt(arCamera.transform);
  197. }
  198. state.Instance.AddComponent<GroundHeight>();
  199. state.Instance.name = name + " (Hotspot)";
  200. state.Activated = true;
  201. Logger.LogFromMethod("Hotspot", "ActivateHotspot", $"({name}): Hotspot activated", DebugMode);
  202. OnHotspotActivated?.Invoke(state.Instance);
  203. OnHotspotActivatedWithId?.Invoke(state.Instance, HotspotSettings.Id);
  204. }
  205. public static Hotspot AddHotspotComponent(GameObject go, Location location, HotspotSettingsData settings)
  206. {
  207. var hotspot = go.AddComponent<Hotspot>();
  208. hotspot.Location = location.Clone();
  209. hotspot.HotspotSettings = settings;
  210. return hotspot;
  211. }
  212. public static Hotspot AddHotspotComponent(GameObject go, Location location, HotspotSettingsData settings, int id = 0)
  213. {
  214. settings.Id = id;
  215. return AddHotspotComponent(go, location, settings);
  216. }
  217. public static GameObject CreateHotspotGameObject(Location location, HotspotSettingsData settings,
  218. string name = "GPS Hotspot")
  219. {
  220. var go = new GameObject(name);
  221. AddHotspotComponent(go, location, settings);
  222. return go;
  223. }
  224. public static GameObject CreateHotspotGameObject(Location location, HotspotSettingsData settings,
  225. string name = "GPS Hotspot", int id = 0)
  226. {
  227. settings.Id = id;
  228. return CreateHotspotGameObject(location, settings, name);
  229. }
  230. void Update()
  231. {
  232. if (UseOnLeaveHotspotEvent && state.Activated && !state.EmittedLeaveHotspotEvent)
  233. {
  234. var distance = Vector3.Distance(arCamera.transform.position, state.Instance.transform.position);
  235. if (distance >= HotspotSettings.ActivationRadius)
  236. {
  237. OnHotspotLeave?.Invoke(state.Instance);
  238. OnHotspotLeaveWithId?.Invoke(state.Instance, HotspotSettings.Id);
  239. state.EmittedLeaveHotspotEvent = true;
  240. if (HotspotSettings.DeactivateOnLeave)
  241. {
  242. Restart();
  243. }
  244. }
  245. }
  246. }
  247. }
  248. }