H_SafeExperienceDrivingSystem/U3D_DrivingSystem/Assets/EVP5/Scripts/VehicleCameraController.cs

743 lines
19 KiB
C#

//------------------------------------------------------------------------------------------------
// Edy's Vehicle Physics
// (c) Angel Garcia "Edy" - Oviedo, Spain
// http://www.edy.es
//------------------------------------------------------------------------------------------------
using UnityEngine;
using UnityEngine.Serialization;
using System;
namespace EVP
{
// TO-DO: raise to Serializable and add a common property "Mode Key" for each mode
public class CameraMode
{
public KeyCode hotKey = KeyCode.None;
// controller gets populated externally
public VehicleCameraController controller;
// Adjust the public values for the given configuration
public virtual void SetViewConfig (VehicleViewConfig viewConfig) { }
// Called one time when the host camera controller is enabled
public virtual void Initialize (Transform self) { }
// Called when the camera mode is enabled
public virtual void OnEnable (Transform self, Transform target, Vector3 targetOffset) { }
// Reset the values for the given target
public virtual void Reset (Transform self, Transform target, Vector3 targetOffset) { }
// Do the camera control stuff
public virtual void Update (Transform self, Transform target, Vector3 targetOffset, float deltaTime) { }
// Called when the camera mode is disabled
public virtual void OnDisable (Transform self, Transform target, Vector3 targetOffset) { }
// Utility method for getting the input for a given axis
public static float GetInputForAxis (string axisName)
{
return string.IsNullOrEmpty(axisName)? 0.0f : Input.GetAxis(axisName);
}
}
// Fixed camera mode
[Serializable]
public class CameraAttachTo : CameraMode
{
public Transform attachTarget;
public override void SetViewConfig (VehicleViewConfig viewConfig)
{
attachTarget = viewConfig.driverView;
}
public override void Update (Transform self, Transform target, Vector3 targetOffset, float deltaTime)
{
if (attachTarget != null) target = attachTarget;
if (target == null) return;
self.position = target.position;
self.rotation = target.rotation;
}
}
// Smooth follow camera mode
[Serializable]
public class CameraSmoothFollow : CameraMode
{
public float distance = 10.0f;
public float height = 5.0f;
public float viewHeightRatio = 0.5f; // Look above the target (height * this ratio)
public bool lookBehind = false;
[Space(5)]
public float heightDamping = 2.0f;
public float rotationDamping = 3.0f;
[Space(5)]
public bool followVelocity = true;
public float velocityDamping = 5.0f;
VehicleController m_vehicle;
Camera m_camera;
Vector3 m_smoothLastPos = Vector3.zero;
Vector3 m_smoothVelocity = Vector3.zero;
float m_smoothTargetAngle = 0.0f;
float m_selfRotationAngle;
float m_selfHeight;
public override void SetViewConfig (VehicleViewConfig viewConfig)
{
distance = viewConfig.viewDistance;
height = viewConfig.viewHeight;
rotationDamping = viewConfig.viewDamping;
}
public override void Initialize (Transform self)
{
m_camera = self.GetComponentInChildren<Camera>();
}
public override void Reset (Transform self, Transform target, Vector3 targetOffset)
{
if (target == null) return;
m_vehicle = target.GetComponent<VehicleController>();
m_smoothLastPos = target.position + targetOffset;
m_smoothVelocity = target.forward * 2.0f;
m_smoothTargetAngle = target.eulerAngles.y;
m_selfRotationAngle = self.eulerAngles.y;
m_selfHeight = self.position.y;
}
public override void Update (Transform self, Transform target, Vector3 targetOffset, float deltaTime)
{
if (target == null) return;
Vector3 updatedVelocity = (target.position + targetOffset - m_smoothLastPos) / deltaTime;
if (lookBehind) updatedVelocity = -updatedVelocity;
m_smoothLastPos = target.position + targetOffset;
updatedVelocity.y = 0.0f;
if (updatedVelocity.magnitude > 1.0f)
{
m_smoothVelocity = Vector3.Lerp(m_smoothVelocity, updatedVelocity, velocityDamping * deltaTime);
m_smoothTargetAngle = Mathf.Atan2(m_smoothVelocity.x, m_smoothVelocity.z) * Mathf.Rad2Deg;
}
if (!followVelocity)
m_smoothTargetAngle = target.eulerAngles.y;
float wantedHeight = target.position.y + targetOffset.y + height;
m_selfRotationAngle = Mathf.LerpAngle(m_selfRotationAngle, m_smoothTargetAngle, rotationDamping * deltaTime);
m_selfHeight = Mathf.Lerp(m_selfHeight, wantedHeight, heightDamping * deltaTime);
Quaternion currentRotation = Quaternion.Euler (0, m_selfRotationAngle, 0);
Vector3 selfPos = target.position + targetOffset;
selfPos -= currentRotation * Vector3.forward * distance;
selfPos.y = m_selfHeight;
Vector3 lookAtTarget = target.position + targetOffset + Vector3.up * height * viewHeightRatio;
if (m_vehicle != null && controller.cameraCollisions)
{
if (m_camera != null)
{
Vector3 origin = lookAtTarget;
Vector3 path = selfPos - lookAtTarget;
Vector3 direction = path.normalized;
float rayDistance = path.magnitude - m_camera.nearClipPlane;
float radius = m_camera.nearClipPlane * Mathf.Tan(m_camera.fieldOfView * Mathf.Deg2Rad * 0.5f) + 0.1f;
selfPos = origin + direction * m_vehicle.SphereRaycastOthers(origin, direction, radius, rayDistance, controller.collisionMask);
}
else
{
selfPos = m_vehicle.RaycastOthers(lookAtTarget, selfPos, controller.collisionMask);
}
}
self.position = selfPos;
self.LookAt(lookAtTarget);
}
}
// Mouse orbit camera mode
[Serializable]
public class CameraMouseOrbit : CameraMode
{
public float distance = 10.0f;
[Space(5)]
public float minVerticalAngle = -20.0f;
public float maxVerticalAngle = 80.0f;
public float horizontalSpeed = 5f;
public float verticalSpeed = 2.5f;
public float orbitDamping = 4.0f;
[Space(5)]
public float minDistance = 5.0f;
public float maxDistance = 50.0f;
public float distanceSpeed = 10.0f;
public float distanceDamping = 4.0f;
[Space(5)]
public string horizontalAxis = "Mouse X";
public string verticalAxis = "Mouse Y";
public string distanceAxis = "Mouse ScrollWheel";
VehicleController m_vehicle;
Camera m_camera;
float m_orbitX = 0.0f;
float m_orbitY = 0.0f;
float m_orbitDistance;
public override void SetViewConfig (VehicleViewConfig viewConfig)
{
distance = viewConfig.viewDistance;
minDistance = viewConfig.viewMinDistance;
minVerticalAngle = viewConfig.viewMinHeight;
}
public override void Initialize (Transform self)
{
m_camera = self.GetComponentInChildren<Camera>();
m_orbitDistance = distance;
Vector3 angles = self.eulerAngles;
m_orbitX = angles.y;
m_orbitY = angles.x;
}
public override void Reset (Transform self, Transform target, Vector3 targetOffset)
{
if (target == null) return;
m_vehicle = target.GetComponent<VehicleController>();
}
public override void Update (Transform self, Transform target, Vector3 targetOffset, float deltaTime)
{
if (target == null) return;
m_orbitX += GetInputForAxis(horizontalAxis) * horizontalSpeed;
m_orbitY -= GetInputForAxis(verticalAxis) * verticalSpeed;
distance -= GetInputForAxis(distanceAxis) * distanceSpeed;
m_orbitY = Mathf.Clamp(m_orbitY, minVerticalAngle, maxVerticalAngle);
distance = Mathf.Clamp(distance, minDistance, maxDistance);
m_orbitDistance = Mathf.Lerp(m_orbitDistance, distance, distanceDamping * deltaTime);
self.rotation = Quaternion.Slerp(self.rotation, Quaternion.Euler(m_orbitY, m_orbitX, 0), orbitDamping * deltaTime);
if (m_vehicle != null && controller.cameraCollisions)
{
Vector3 origin = target.position + targetOffset;
Vector3 direction = self.rotation * -Vector3.forward;
// If a camera is present then perform a sphere cast. Otherwise do a raycast.
if (m_camera != null)
{
float radius = m_camera.nearClipPlane * Mathf.Tan(m_camera.fieldOfView * Mathf.Deg2Rad * 0.5f) + 0.05f;
float rayDistance = m_orbitDistance - m_camera.nearClipPlane;
self.position = origin + direction * m_vehicle.SphereRaycastOthers(origin, direction, radius, rayDistance, controller.collisionMask);
}
else
{
self.position = m_vehicle.RaycastOthers(origin, origin + direction * m_orbitDistance, controller.collisionMask);
}
}
else
{
self.position = target.position + targetOffset + self.rotation * new Vector3(0.0f, 0.0f, -m_orbitDistance);
}
}
}
// Look-at camera mode
[Serializable]
public class CameraLookAt : CameraMode
{
public float damping = 6.0f;
[Space(5)]
public float minFov = 10.0f;
public float maxFov = 60.0f;
public float fovSpeed = 20.0f;
public float fovDamping = 4.0f;
public string fovAxis = "Mouse ScrollWheel";
[Space(5)]
public bool autoFov = false;
public float targetRadius = 5.0f;
public float targetRadiusSpeed = 5.0f;
[Space(5)]
public bool adjustNearPlane = false;
public float nearPlaneAtMinFov = 1.5f;
[Space(5)]
public bool enableMovement = false;
public float movementSpeed = 2.0f;
public float movementDamping = 5.0f;
public string forwardAxis = "";
public string sidewaysAxis = "";
public string verticalAxis = "";
Camera m_camera;
Vector3 m_position;
float m_fov = 0.0f;
float m_savedFov = 0.0f;
float m_savedNearPlane = 0.0f;
public override void SetViewConfig (VehicleViewConfig viewConfig)
{
targetRadius = viewConfig.targetDiameter;
}
public override void Initialize (Transform self)
{
m_camera = self.GetComponentInChildren<Camera>();
}
public override void OnEnable (Transform self, Transform target, Vector3 targetOffset)
{
m_position = self.position;
if (m_camera != null)
{
m_fov = m_camera.fieldOfView;
m_savedFov = m_camera.fieldOfView;
m_savedNearPlane = m_camera.nearClipPlane;
}
}
public override void Update (Transform self, Transform target, Vector3 targetOffset, float deltaTime)
{
// Position
if (enableMovement)
{
float stepSize = movementSpeed * deltaTime;
m_position += GetInputForAxis(forwardAxis) * stepSize * new Vector3(self.forward.x, 0.0f, self.forward.z).normalized;
m_position += GetInputForAxis(sidewaysAxis) * stepSize * self.right;
m_position += GetInputForAxis(verticalAxis) * stepSize * self.up;
}
self.position = Vector3.Lerp(self.position, m_position, movementDamping * deltaTime);
// Rotation
if (target != null)
{
Quaternion lookAtRotation = Quaternion.LookRotation(target.position + targetOffset - self.position);
if (damping > 0)
self.rotation = Quaternion.Slerp(self.rotation, lookAtRotation, damping * deltaTime);
else
self.rotation = lookAtRotation;
}
// Zoom
if (m_camera != null)
{
if (autoFov && target != null)
{
targetRadius -= GetInputForAxis(fovAxis) * targetRadiusSpeed;
if (targetRadius < 0.0f) targetRadius = 0.0f;
float distance = Vector3.Distance(target.position, self.position);
m_fov = Mathf.Atan2(targetRadius, distance) * Mathf.Rad2Deg;
}
else
{
m_fov -= GetInputForAxis(fovAxis) * fovSpeed;
}
m_fov = Mathf.Clamp(m_fov, minFov, maxFov);
m_camera.fieldOfView = Mathf.Lerp(m_camera.fieldOfView, m_fov, fovDamping * deltaTime);
if (adjustNearPlane)
{
m_camera.nearClipPlane = Mathf.Lerp(m_savedNearPlane, nearPlaneAtMinFov,
Mathf.InverseLerp(maxFov, minFov, m_camera.fieldOfView));
}
}
}
public override void OnDisable (Transform self, Transform target, Vector3 targetOffset)
{
if (m_camera != null)
{
m_camera.fieldOfView = m_savedFov;
m_camera.nearClipPlane = m_savedNearPlane;
}
}
}
// Free camera mode
[Serializable]
public class CameraFree : CameraMode
{
public float minVerticalAngle = -60.0f;
public float maxVerticalAngle = 60.0f;
public float horizontalSpeed = 4f;
public float verticalSpeed = 2f;
public float damping = 4.0f;
[Space(5)]
public bool adjustFov = true;
public float minFov = 10.0f;
public float maxFov = 60.0f;
public float fovSpeed = 20.0f;
public float fovDamping = 4.0f;
[Space(5)]
public bool adjustNearPlane = false;
public float nearPlaneAtMinFov = 1.5f;
[Space(5)]
public string horizontalAxis = "Mouse X";
public string verticalAxis = "Mouse Y";
public string fovAxis = "Mouse ScrollWheel";
[Space(5)]
public bool enableMovement = false;
public float movementSpeed = 2.0f;
public float movementDamping = 5.0f;
public string forwardAxis = "";
public string sidewaysAxis = "";
public string upwardsAxis = "";
Camera m_camera;
Vector3 m_position;
float m_fov = 0.0f;
float m_savedFov = 0.0f;
float m_savedNearPlane = 0.0f;
float m_horizontal;
float m_vertical;
public override void Initialize (Transform self)
{
m_camera = self.GetComponent<Camera>();
// If camera is not found at this object, try the children.
// Will be used for the FOV.
if (m_camera == null)
m_camera = self.GetComponentInChildren<Camera>();
}
public override void OnEnable (Transform self, Transform target, Vector3 targetOffset)
{
m_position = self.position;
Vector3 angles = self.eulerAngles;
m_horizontal = angles.y;
m_vertical = -angles.x;
if (m_camera != null)
{
m_fov = m_camera.fieldOfView;
m_savedFov = m_camera.fieldOfView;
m_savedNearPlane = m_camera.nearClipPlane;
}
}
public override void Update (Transform self, Transform target, Vector3 targetOffset, float deltaTime)
{
// Rotation
m_horizontal += GetInputForAxis(horizontalAxis) * horizontalSpeed;
m_vertical += GetInputForAxis(verticalAxis) * verticalSpeed;
m_vertical = Mathf.Clamp(m_vertical, minVerticalAngle, maxVerticalAngle);
// Position
if (enableMovement)
{
float stepSize = movementSpeed * deltaTime;
m_position += GetInputForAxis(forwardAxis) * stepSize * new Vector3(self.forward.x, 0.0f, self.forward.z).normalized;
m_position += GetInputForAxis(sidewaysAxis) * stepSize * self.right;
m_position += GetInputForAxis(upwardsAxis) * stepSize * self.up;
}
self.position = Vector3.Lerp(self.position, m_position, movementDamping * deltaTime);
self.rotation = Quaternion.Slerp(self.rotation, Quaternion.Euler(-m_vertical, m_horizontal, 0), damping * deltaTime);
// Zoom
if (m_camera != null)
{
m_fov -= GetInputForAxis(fovAxis) * fovSpeed;
m_fov = Mathf.Clamp(m_fov, minFov, maxFov);
if (adjustFov)
m_camera.fieldOfView = Mathf.Lerp(m_camera.fieldOfView, m_fov, fovDamping * deltaTime);
if (adjustNearPlane)
{
m_camera.nearClipPlane = Mathf.Lerp(m_savedNearPlane, nearPlaneAtMinFov,
Mathf.InverseLerp(maxFov, minFov, m_camera.fieldOfView));
}
}
}
public override void OnDisable (Transform self, Transform target, Vector3 targetOffset)
{
if (m_camera != null)
{
m_camera.fieldOfView = m_savedFov;
m_camera.nearClipPlane = m_savedNearPlane;
}
}
}
// Camera controller
public class VehicleCameraController : MonoBehaviour
{
public enum Mode { AttachTo, SmoothFollow, MouseOrbit, LookAt, Free };
public Mode mode = Mode.SmoothFollow;
public Transform target;
public bool followCenterOfMass = true;
public bool useUnscaledTime = true;
[Space(5)]
public bool cameraCollisions = true;
public LayerMask collisionMask = Physics.DefaultRaycastLayers;
[Space(5)]
public KeyCode changeCameraKey = KeyCode.C;
[Space(5)]
public CameraAttachTo attachTo = new CameraAttachTo();
[FormerlySerializedAs("smoothFollowSettings")]
public CameraSmoothFollow smoothFollow = new CameraSmoothFollow();
[FormerlySerializedAs("orbitSettings")]
public CameraMouseOrbit mouseOrbit = new CameraMouseOrbit();
public CameraLookAt lookAt = new CameraLookAt();
public CameraFree free = new CameraFree();
Transform m_transform;
Mode m_prevMode;
CameraMode[] m_cameraModes = new CameraMode[0];
Transform m_prevTarget;
Rigidbody m_targetRigidbody;
Vector3 m_localTargetOffset;
Vector3 m_targetOffset;
void OnEnable ()
{
m_transform = GetComponent<Transform>();
// Initialize the array with the camera modes.
// Must be the same length as the Mode enum.
m_cameraModes = new CameraMode[]
{
attachTo, smoothFollow, mouseOrbit, lookAt, free
};
// Initialize all modes
foreach (CameraMode cam in m_cameraModes)
{
cam.controller = this;
cam.Initialize(m_transform);
}
// Adquire the target and its rigidbody if specified/available
AdquireTarget();
ComputeTargetOffset();
m_prevTarget = target;
// Enable current mode
m_cameraModes[(int)mode].OnEnable(m_transform, target, m_targetOffset);
m_cameraModes[(int)mode].Reset(m_transform, target, m_targetOffset);
m_prevMode = mode;
}
void OnDisable ()
{
m_cameraModes[(int)mode].OnDisable(m_transform, target, m_targetOffset);
}
void LateUpdate ()
{
// Target changed?
if (target != m_prevTarget)
{
AdquireTarget();
m_prevTarget = target;
}
ComputeTargetOffset();
// Detect camera hotkey
if (Input.GetKeyDown(changeCameraKey))
{
NextCameraMode();
}
else
{
for (int i=0; i<m_cameraModes.Length; i++)
{
if (Input.GetKeyDown(m_cameraModes[i].hotKey))
mode = (Mode)i;
}
}
// Camera mode changed?
if (mode != m_prevMode)
{
// Disable previous mode, then enable new one.
m_cameraModes[(int)m_prevMode].OnDisable(m_transform, target, m_targetOffset);
m_cameraModes[(int)mode].OnEnable(m_transform, target, m_targetOffset);
m_cameraModes[(int)mode].Reset(m_transform, target, m_targetOffset);
m_prevMode = mode;
}
float deltaTime = useUnscaledTime? Time.unscaledDeltaTime : Time.deltaTime;
m_cameraModes[(int)mode].Update(m_transform, target, m_targetOffset, deltaTime);
}
public void NextCameraMode ()
{
if (!enabled) return;
mode++;
if ((int)mode >= m_cameraModes.Length)
mode = (Mode)0;
}
public void ResetCamera ()
{
if (enabled)
m_cameraModes[(int)mode].Reset(m_transform, target, m_targetOffset);
}
public void SetViewConfig (VehicleViewConfig viewConfig)
{
foreach (CameraMode cam in m_cameraModes)
cam.SetViewConfig(viewConfig);
}
void AdquireTarget ()
{
// Get the view configuration if exists and configure the camera modes
if (target != null)
{
VehicleViewConfig viewConfig = target.GetComponent<VehicleViewConfig>();
if (viewConfig != null)
{
if (viewConfig.lookAtPoint != null)
target = viewConfig.lookAtPoint;
SetViewConfig(viewConfig);
}
}
// Find the rigidbody if exists and get the center of mass
if (followCenterOfMass && target != null)
{
m_targetRigidbody = target.GetComponent<Rigidbody>();
if (m_targetRigidbody)
m_localTargetOffset = m_targetRigidbody.centerOfMass;
}
else
{
m_targetRigidbody = null;
}
// Everything ready. Reset the camera for the target.
ResetCamera();
}
void ComputeTargetOffset ()
{
if (followCenterOfMass && m_targetRigidbody != null)
{
// centerOfMass is not affected by scale. No need to use TransformVector.
m_targetOffset = target.TransformDirection(m_localTargetOffset);
}
else
{
m_targetOffset = Vector3.zero;
}
}
}
}