using System;
using UnityEngine;
using UnityEngine.Events;
namespace Google.Maps.Examples.Shared {
///
/// A simple camera controller, to allow for user-controlled movement in example scenes.
///
///
/// Intended to be attached to the being controlled.
///
/// Movement is performed via WASD keys (transverse), QE (up/down) and arrow keys (rotation).
///
[RequireComponent(typeof(Camera))]
public class CameraController : MonoBehaviour {
///
/// Event triggered when the moves, passing back the amount moved.
///
[Serializable]
public class MoveEvent : UnityEvent {}
[Tooltip("Movement speed when pressing movement keys (WASD for panning, QE for up/down).")]
public float MovementSpeed = 200f;
[Tooltip("Rotation speed when pressing arrow keys.")]
public float RotationSpeed = 100f;
[Tooltip("Minimum height off the ground.")]
public float MinHeight = 2f;
[Tooltip("Maximum height off the ground.")]
public float MaxHeight = 600f;
[Tooltip("Minimum angle above ground.")]
public float MinXRotation = 0;
[Tooltip("Maximum angle above ground.")]
public float MaxXRotation = 90;
///
/// Optional called whenever the is moved in any way.
///
///
/// Passes in the amount moved so the type/direction of movement can be queried.
///
public MoveEvent OnMove = new MoveEvent();
///
/// Optional called whenever the is rotated.
///
public UnityEvent OnRotate = new UnityEvent();
///
/// Optional called whenever the is moved or rotated
/// in any way.
///
public UnityEvent OnTransform = new UnityEvent();
///
/// The current desired rotation of the Camera around the Y-Axis. Applied in world space after
/// Inclination is applied.
///
private float Azimuth;
///
/// The current desired rotation of the Camera around the X-Axis. Applied in world space before
/// Azimuth is applied.
///
private float Inclination;
public void InitializeAzimuthAndInclination() {
// Initialize Azimuth and Inclination from the current rotation Euler angles. Reading Euler
// angles is generally not a good idea but should be safe to do once at initialization if the
// Camera starts in a non-extreme orientation.
Azimuth = transform.eulerAngles.y;
Inclination = transform.eulerAngles.x;
}
private void Awake() {
InitializeAzimuthAndInclination();
}
private void Update() {
// Determine which keys are currently being pressed.
bool pressingW = Input.GetKey(KeyCode.W);
bool pressingS = Input.GetKey(KeyCode.S);
bool pressingA = Input.GetKey(KeyCode.A);
bool pressingD = Input.GetKey(KeyCode.D);
bool pressingQ = Input.GetKey(KeyCode.Q);
bool pressingE = Input.GetKey(KeyCode.E);
bool pressingUp = Input.GetKey(KeyCode.UpArrow);
bool pressingDown = Input.GetKey(KeyCode.DownArrow);
bool pressingLeft = Input.GetKey(KeyCode.LeftArrow);
bool pressingRight = Input.GetKey(KeyCode.RightArrow);
// Convert to simple summaries of whether movement and/or rotation is required this frame.
bool isMoving = pressingW || pressingS || pressingA || pressingD || pressingQ || pressingE;
bool isRotating = pressingUp || pressingDown || pressingLeft || pressingRight;
// If no change is to be applied this frame, we skip any further processing.
if (!isMoving && !isRotating) {
return;
}
// Convert key presses to directions of movement and rotation.
float xInput = pressingD ? 1 : pressingA ? -1 : 0;
float yInput = pressingE ? 1 : pressingQ ? -1 : 0;
float zInput = pressingW ? 1 : pressingS ? -1 : 0;
float rotX = pressingDown ? 1 : pressingUp ? -1 : 0;
float rotY = pressingRight ? 1 : pressingLeft ? -1 : 0;
// Apply movement. We skip this if there is no movement this frame.
Vector3 positionBefore = transform.position;
if (isMoving) {
// Move the camera at a speed that is linearly dependent on the height of the camera above
// the ground plane to make camera manual camera movement practicable. The movement speed
// is clamped between 1% and 100% of the configured MovementSpeed.
float speed = Mathf.Clamp(transform.position.y, MovementSpeed * 0.01f, MovementSpeed);
Vector3 forward = Quaternion.Euler(0, Azimuth, 0) * Vector3.forward;
Vector3 right = Quaternion.Euler(0, Azimuth, 0) * Vector3.right;
Vector3 motion =
(right * xInput + forward * zInput + yInput * Vector3.up) * speed * Time.deltaTime;
Vector3 position = transform.position + motion;
// Enforce min/max height.
position.y = Mathf.Clamp(position.y, MinHeight, MaxHeight);
transform.position = position;
}
// Rotate, adding change in rotation to current rotation (recorded before overriden for
// movement). We skip this if there is no rotation this frame.
if (isRotating) {
Azimuth += rotY * RotationSpeed * Time.deltaTime;
Inclination = Mathf.Clamp(
Inclination + rotX * RotationSpeed * Time.deltaTime, MinXRotation, MaxXRotation);
// Quaternion.Euler is documented as applying X-rotation before Y-rotation, in world space.
transform.localRotation = Quaternion.Euler(Inclination, Azimuth, 0);
}
// Invoke any defined Actions to inform other classes of any change in Camera's movement or
// rotation this frame.
if (isMoving) {
// Pass in the amount moved this frame (current position minus position last frame).
OnMove.Invoke(transform.position - positionBefore);
}
if (isRotating) {
OnRotate.Invoke();
}
OnTransform.Invoke();
}
}
}