FloatingOriginUpdater.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.Events;
  5. namespace Google.Maps.Examples.Shared {
  6. /// <summary>
  7. /// Component to update the <see cref="Google.Maps.MapsService"/>'s Floating Origin whenever the
  8. /// <see cref="Camera.main"/> moves far enough.
  9. /// <para>
  10. /// The Floating Origin is used to periodically recenter the world, moving the world until the
  11. /// player is back at the origin (0f, 0f, 0f). The prevents geometry being created with
  12. /// increasingly large floating point coordinates, ultimately resulting in floating point rounding
  13. /// errors.
  14. /// </para></summary>
  15. public sealed class FloatingOriginUpdater : MonoBehaviour {
  16. /// <summary>The last set floating origin.</summary>
  17. public Vector3 FloatingOrigin { get; private set; }
  18. /// <summary>
  19. /// Optional <see cref="UnityEvent"/> called whenever this script updates the world's Floating
  20. /// Origin.
  21. /// </summary>
  22. /// <remarks>Passes in the amount the Floating Origin was moved by.</remarks>
  23. public OriginEvent OnFloatingOriginUpdate = new OriginEvent();
  24. [Tooltip("Script for controlling Camera movement. Used to detect when the Camera has moved.")]
  25. public CameraController CameraController;
  26. [Tooltip(
  27. "Distance in meters the Camera should move before the world's Floating Origin is " +
  28. "reset. This value must be positive.")]
  29. public float FloatingOriginRange = 200f;
  30. [Tooltip("Should a debug message be shown whenever the Floating Origin is re-centered?")]
  31. public bool DebugFloatingOrigin = true;
  32. /// <summary>
  33. /// The <see cref="MapsService"/> to update the floating origin of.
  34. /// </summary>
  35. public MapsService MapsService;
  36. /// <summary>
  37. /// All <see cref="GameObject"/>s to be moved when the world's Floating Origin is moved.
  38. /// </summary>
  39. /// <remarks>
  40. /// If this array is not set by calling <see cref="SetAdditionalGameObjects"/>, then this array
  41. /// is initialized with <see cref="Camera.main"/> during <see cref="Awake"/>. This is so, by
  42. /// default, the scene's <see cref="Camera"/> is moved when the Floating Origin is recentered,
  43. /// resulting in a seamless recentering of the world that should be invisible to the user.
  44. /// </remarks>
  45. private GameObject[] AdditionalGameObjects;
  46. /// <summary>
  47. /// Use <see cref="CameraController"/>'s OnMove event to detect when the <see cref="Camera"/>
  48. /// has moved far enough that the Floating Origin needs to be recentered.
  49. /// </summary>
  50. private void Awake() {
  51. if (MapsService == null) {
  52. Debug.LogError(ExampleErrors.MissingParameter(
  53. this, MapsService, "Maps Service", "is required for this script to work."));
  54. return;
  55. }
  56. // Verify a Camera Controller has been given.
  57. if (CameraController == null) {
  58. Debug.LogError(ExampleErrors.MissingParameter(
  59. this, CameraController, "Camera Controller", "to tell when the Camera has moved"));
  60. return;
  61. }
  62. // Verify that a valid Floating Origin range was given, i.e. that given distance was not
  63. // negative nor zero. Comparison is made to float.Epsilon instead of zero to account for float
  64. // rounding errors.
  65. if (FloatingOriginRange <= float.Epsilon) {
  66. Debug.LogError(ExampleErrors.NotGreaterThanZero(
  67. this,
  68. FloatingOriginRange,
  69. "Floating Origin Range",
  70. "to tell how far the Camera should move before the Floating " + "Origin is reset"));
  71. return;
  72. }
  73. // Store the initial position of the Camera on the ground plane.
  74. FloatingOrigin = GetCameraPositionOnGroundPlane();
  75. // If no additional GameObjects have been set (to be moved when the world's Floating Origin is
  76. // recentered), set this array to be just Camera.main's GameObject. This is so that, by
  77. // default, the scene's Camera is moved when the world is recentered, resulting in a seamless
  78. // recentering of the world that should be invisible to the user.
  79. if (AdditionalGameObjects == null) {
  80. AdditionalGameObjects = new[] { Camera.main.gameObject };
  81. }
  82. }
  83. private Vector3 GetCameraPositionOnGroundPlane() {
  84. Vector3 result = Camera.main.transform.position;
  85. // Ignore the Y value since the floating origin only really makes sense on the ground plane.
  86. result.y = 0;
  87. return result;
  88. }
  89. /// <summary>
  90. /// See if <see cref="Camera.main"/> has moved far enough that the world's Floating Origin needs
  91. /// to be recentered.
  92. /// </summary>
  93. /// <param name="moveAmount">
  94. /// Amount <see cref="Camera.main"/> has moved (not used as this value is recalculated here with
  95. /// height ignored).
  96. /// </param>
  97. private void TryMoveFloatingOrigin(Vector3 moveAmount) {
  98. if (MapsService == null) {
  99. return;
  100. }
  101. Vector3 newFloatingOrigin = GetCameraPositionOnGroundPlane();
  102. float distance = Vector3.Distance(FloatingOrigin, newFloatingOrigin);
  103. // Reset the world's Floating Origin if (and only if) the Camera has moved far enough.
  104. if (distance < FloatingOriginRange) {
  105. return;
  106. }
  107. // The Camera's current position is given to MapsService's MoveFloatingOrigin function,
  108. // along with any GameObjects to move along with the world (which will at least be the the
  109. // Camera itself). This is so that the world, the Camera, and any extra GameObjects can all be
  110. // moved together, until the Camera is over the origin again. Note that the MoveFloatingOrigin
  111. // function automatically moves all geometry loaded by the Maps Service.
  112. Vector3 originOffset =
  113. MapsService.MoveFloatingOrigin(newFloatingOrigin, AdditionalGameObjects);
  114. // Use event to inform other classes of change in origin. Note that because this is a Unity
  115. // Event a null reference exception will not be triggered if no listeners have been added.
  116. OnFloatingOriginUpdate.Invoke(originOffset);
  117. // Set the new Camera origin. This ensures that we can accurately tell when the Camera has
  118. // moved away from this new origin, and the world needs to be recentered again.
  119. FloatingOrigin = newFloatingOrigin;
  120. // Optionally print a debug message, saying how much the Floating Origin was moved by.
  121. if (DebugFloatingOrigin && !Mathf.Approximately(distance, 0)) {
  122. Debug.LogFormat("Floating Origin moved: world moved by {0}", originOffset);
  123. }
  124. }
  125. /// <summary>
  126. /// Set an array of <see cref="GameObject"/>s to be moved whenever the world's Floating Origin
  127. /// is recentered.
  128. /// </summary>
  129. /// <remarks>
  130. /// <see cref="Camera.main"/>'s <see cref="GameObject"/> is automatically added to the given
  131. /// array of <see cref="GameObject"/>s (if it is not already present), so that by default the
  132. /// scene's <see cref="Camera"/> is moved when the Floating Origin is recentered, resulting in a
  133. /// seamless recentering of the world that should be invisible to the user.
  134. /// </remarks>
  135. /// <param name="objects">
  136. /// Array of <see cref="GameObject"/>s to move with the world's Floating Origin.
  137. /// </param>
  138. public void SetAdditionalGameObjects(ICollection<GameObject> objects) {
  139. // Check to see if the main Camera's GameObject is already a part of this given set of
  140. // GameObjects, adding it if not and storing as the array of GameObjects to move when the
  141. // world's Floating Origin is recentered.
  142. GameObject cameraGameObject = Camera.main.gameObject;
  143. List<GameObject> objectList = new List<GameObject>(objects);
  144. if (!objects.Contains(cameraGameObject)) {
  145. objectList.Add(cameraGameObject);
  146. }
  147. AdditionalGameObjects = objectList.ToArray();
  148. }
  149. void OnEnable() {
  150. // Whenever the Camera moves, check to see if it has moved far enough that the world's
  151. // Floating Origin needs to be re-centered.
  152. CameraController.OnMove.AddListener(TryMoveFloatingOrigin);
  153. }
  154. void OnDisable() {
  155. CameraController.OnMove.RemoveListener(TryMoveFloatingOrigin);
  156. }
  157. /// <summary>
  158. /// Optional <see cref="UnityEvent"/> called every frame this script updates the world's
  159. /// floating origin.
  160. /// </summary>
  161. /// <remarks>Passes in the world's new floating origin.</remarks>
  162. [Serializable]
  163. public class OriginEvent : UnityEvent<Vector3> {}
  164. }
  165. }