CameraController.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. using System;
  2. using UnityEngine;
  3. using UnityEngine.Events;
  4. namespace Google.Maps.Examples.Shared {
  5. /// <summary>
  6. /// A simple camera controller, to allow for user-controlled movement in example scenes.
  7. /// </summary>
  8. /// <remarks>
  9. /// Intended to be attached to the <see cref="Camera"/> <see cref="GameObject"/> being controlled.
  10. /// <para>
  11. /// Movement is performed via WASD keys (transverse), QE (up/down) and arrow keys (rotation).
  12. /// </para></remarks>
  13. [RequireComponent(typeof(Camera))]
  14. public class CameraController : MonoBehaviour {
  15. /// <summary>
  16. /// Event triggered when the <see cref="Camera"/> moves, passing back the amount moved.
  17. /// </summary>
  18. [Serializable]
  19. public class MoveEvent : UnityEvent<Vector3> {}
  20. [Tooltip("Movement speed when pressing movement keys (WASD for panning, QE for up/down).")]
  21. public float MovementSpeed = 200f;
  22. [Tooltip("Rotation speed when pressing arrow keys.")]
  23. public float RotationSpeed = 100f;
  24. [Tooltip("Minimum height off the ground.")]
  25. public float MinHeight = 2f;
  26. [Tooltip("Maximum height off the ground.")]
  27. public float MaxHeight = 600f;
  28. [Tooltip("Minimum angle above ground.")]
  29. public float MinXRotation = 0;
  30. [Tooltip("Maximum angle above ground.")]
  31. public float MaxXRotation = 90;
  32. /// <summary>
  33. /// Optional <see cref="Action"/> called whenever the <see cref="Camera"/> is moved in any way.
  34. /// </summary>
  35. /// <remarks>
  36. /// Passes in the amount moved so the type/direction of movement can be queried.
  37. /// </remarks>
  38. public MoveEvent OnMove = new MoveEvent();
  39. /// <summary>
  40. /// Optional <see cref="Action"/> called whenever the <see cref="Camera"/> is rotated.
  41. /// </summary>
  42. public UnityEvent OnRotate = new UnityEvent();
  43. /// <summary>
  44. /// Optional <see cref="Action"/> called whenever the <see cref="Camera"/> is moved or rotated
  45. /// in any way.
  46. /// </summary>
  47. public UnityEvent OnTransform = new UnityEvent();
  48. /// <summary>
  49. /// The current desired rotation of the Camera around the Y-Axis. Applied in world space after
  50. /// Inclination is applied.
  51. /// </summary>
  52. private float Azimuth;
  53. /// <summary>
  54. /// The current desired rotation of the Camera around the X-Axis. Applied in world space before
  55. /// Azimuth is applied.
  56. /// </summary>
  57. private float Inclination;
  58. public void InitializeAzimuthAndInclination() {
  59. // Initialize Azimuth and Inclination from the current rotation Euler angles. Reading Euler
  60. // angles is generally not a good idea but should be safe to do once at initialization if the
  61. // Camera starts in a non-extreme orientation.
  62. Azimuth = transform.eulerAngles.y;
  63. Inclination = transform.eulerAngles.x;
  64. }
  65. private void Awake() {
  66. InitializeAzimuthAndInclination();
  67. }
  68. private void Update() {
  69. // Determine which keys are currently being pressed.
  70. bool pressingW = Input.GetKey(KeyCode.W);
  71. bool pressingS = Input.GetKey(KeyCode.S);
  72. bool pressingA = Input.GetKey(KeyCode.A);
  73. bool pressingD = Input.GetKey(KeyCode.D);
  74. bool pressingQ = Input.GetKey(KeyCode.Q);
  75. bool pressingE = Input.GetKey(KeyCode.E);
  76. bool pressingUp = Input.GetKey(KeyCode.UpArrow);
  77. bool pressingDown = Input.GetKey(KeyCode.DownArrow);
  78. bool pressingLeft = Input.GetKey(KeyCode.LeftArrow);
  79. bool pressingRight = Input.GetKey(KeyCode.RightArrow);
  80. // Convert to simple summaries of whether movement and/or rotation is required this frame.
  81. bool isMoving = pressingW || pressingS || pressingA || pressingD || pressingQ || pressingE;
  82. bool isRotating = pressingUp || pressingDown || pressingLeft || pressingRight;
  83. // If no change is to be applied this frame, we skip any further processing.
  84. if (!isMoving && !isRotating) {
  85. return;
  86. }
  87. // Convert key presses to directions of movement and rotation.
  88. float xInput = pressingD ? 1 : pressingA ? -1 : 0;
  89. float yInput = pressingE ? 1 : pressingQ ? -1 : 0;
  90. float zInput = pressingW ? 1 : pressingS ? -1 : 0;
  91. float rotX = pressingDown ? 1 : pressingUp ? -1 : 0;
  92. float rotY = pressingRight ? 1 : pressingLeft ? -1 : 0;
  93. // Apply movement. We skip this if there is no movement this frame.
  94. Vector3 positionBefore = transform.position;
  95. if (isMoving) {
  96. // Move the camera at a speed that is linearly dependent on the height of the camera above
  97. // the ground plane to make camera manual camera movement practicable. The movement speed
  98. // is clamped between 1% and 100% of the configured MovementSpeed.
  99. float speed = Mathf.Clamp(transform.position.y, MovementSpeed * 0.01f, MovementSpeed);
  100. Vector3 forward = Quaternion.Euler(0, Azimuth, 0) * Vector3.forward;
  101. Vector3 right = Quaternion.Euler(0, Azimuth, 0) * Vector3.right;
  102. Vector3 motion =
  103. (right * xInput + forward * zInput + yInput * Vector3.up) * speed * Time.deltaTime;
  104. Vector3 position = transform.position + motion;
  105. // Enforce min/max height.
  106. position.y = Mathf.Clamp(position.y, MinHeight, MaxHeight);
  107. transform.position = position;
  108. }
  109. // Rotate, adding change in rotation to current rotation (recorded before overriden for
  110. // movement). We skip this if there is no rotation this frame.
  111. if (isRotating) {
  112. Azimuth += rotY * RotationSpeed * Time.deltaTime;
  113. Inclination = Mathf.Clamp(
  114. Inclination + rotX * RotationSpeed * Time.deltaTime, MinXRotation, MaxXRotation);
  115. // Quaternion.Euler is documented as applying X-rotation before Y-rotation, in world space.
  116. transform.localRotation = Quaternion.Euler(Inclination, Azimuth, 0);
  117. }
  118. // Invoke any defined Actions to inform other classes of any change in Camera's movement or
  119. // rotation this frame.
  120. if (isMoving) {
  121. // Pass in the amount moved this frame (current position minus position last frame).
  122. OnMove.Invoke(transform.position - positionBefore);
  123. }
  124. if (isRotating) {
  125. OnRotate.Invoke();
  126. }
  127. OnTransform.Invoke();
  128. }
  129. }
  130. }