1436 lines
59 KiB
C#
1436 lines
59 KiB
C#
using UnityEngine;
|
|
using System.Collections;
|
|
|
|
// Sci-Fi Ship Controller. Copyright (c) 2018-2023 SCSM Pty Ltd. All rights reserved.
|
|
namespace SciFiShipController
|
|
{
|
|
[AddComponentMenu("Sci-Fi Ship Controller/Misc/Ship Camera Module")]
|
|
[HelpURL("http://scsmmedia.com/ssc-documentation")]
|
|
[RequireComponent(typeof(Camera))]
|
|
[RequireComponent(typeof(Rigidbody))]
|
|
public class ShipCameraModule : MonoBehaviour
|
|
{
|
|
#region Public Enumerations
|
|
|
|
public enum CameraUpdateType
|
|
{
|
|
/// <summary>
|
|
/// The update occurs during FixedUpdate. Recommended for rigidbodies with Interpolation set to None.
|
|
/// </summary>
|
|
FixedUpdate = 0,
|
|
/// <summary>
|
|
/// The update occurs during LateUpdate. Recommended for rigidbodies with Interpolation set to Interpolate.
|
|
/// </summary>
|
|
LateUpdate = 1,
|
|
/// <summary>
|
|
/// When the update occurs is automatically determined.
|
|
/// </summary>
|
|
Automatic = 2
|
|
}
|
|
|
|
public enum CameraRotationMode
|
|
{
|
|
/// <summary>
|
|
/// The camera rotates to face in the direction the ship is moving in.
|
|
/// </summary>
|
|
FollowVelocity = 0,
|
|
/// <summary>
|
|
/// The camera rotates to face the direction the ship is facing in.
|
|
/// </summary>
|
|
FollowTargetRotation = 1,
|
|
/// <summary>
|
|
/// The camera rotates to face towards the ship itself.
|
|
/// </summary>
|
|
AimAtTarget = 2,
|
|
/// <summary>
|
|
/// The camera faces downwards and rotates so that the top of the screen is in the direction
|
|
/// the ship is moving in.
|
|
/// </summary>
|
|
TopDownFollowVelocity = 5,
|
|
/// <summary>
|
|
/// The camera faces downwards and rotates so that the top of the screen is in the direction
|
|
/// the ship is facing in.
|
|
/// </summary>
|
|
TopDownFollowTargetRotation = 6,
|
|
/// <summary>
|
|
/// The camera rotation is fixed.
|
|
/// </summary>
|
|
Fixed = 20
|
|
}
|
|
|
|
public enum TargetOffsetCoordinates
|
|
{
|
|
/// <summary>
|
|
/// The target offset is relative to the rotation of the camera.
|
|
/// </summary>
|
|
CameraRotation = 0,
|
|
/// <summary>
|
|
/// The target offset is relative to the rotation of the target.
|
|
/// </summary>
|
|
TargetRotation = 1,
|
|
/// <summary>
|
|
/// The target offset is relative to the flat rotation of the target.
|
|
/// </summary>
|
|
TargetRotationFlat = 2,
|
|
/// <summary>
|
|
/// The target offset is relative to the world coordinate system.
|
|
/// </summary>
|
|
World = 10
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Variables - General
|
|
|
|
/// <summary>
|
|
/// Start the camera rendering when it is initialised.
|
|
/// See also StartCamera() and StopCamera()
|
|
/// </summary>
|
|
public bool startOnInitialise = true;
|
|
|
|
/// <summary>
|
|
/// Enable camera movement when it is initialised and startOnInitialise is true.
|
|
/// See also EnableCamera() and DisableCamera()
|
|
/// </summary>
|
|
public bool enableOnInitialise = true;
|
|
|
|
/// <summary>
|
|
/// The target ship for this camera to follow. At runtime call GetTarget() or SetTarget(newTarget)
|
|
/// </summary>
|
|
public ShipControlModule target;
|
|
|
|
/// <summary>
|
|
/// The offset from the target (in local space) for the camera to aim for.
|
|
/// </summary>
|
|
public Vector3 targetOffset = Vector3.zero;
|
|
|
|
/// <summary>
|
|
/// The coordinate system used to interpret the target offset.
|
|
/// CameraRotation: The target offset is relative to the rotation of the camera.
|
|
/// TargetRotation: The target offset is relative to the rotation of the target.
|
|
/// TargetRotationFlat: The target offset is relative to the flat rotation of the target.
|
|
/// World: The target offset is relative to the world coordinate system.
|
|
/// </summary>
|
|
public TargetOffsetCoordinates targetOffsetCoordinates = TargetOffsetCoordinates.CameraRotation;
|
|
|
|
/// <summary>
|
|
/// If enabled, the camera will stay locked to the optimal camera position.
|
|
/// </summary>
|
|
public bool lockToTargetPosition = false;
|
|
/// <summary>
|
|
/// How quickly the camera moves towards the optimal camera position. Only relevant when lockToTargetPosition is disabled.
|
|
/// </summary>
|
|
[Range(1f, 100f)] public float moveSpeed = 15f;
|
|
|
|
/// <summary>
|
|
/// Damp or modify the target position offset based upon the ship pitch and yaw inputs
|
|
/// </summary>
|
|
public bool targetOffsetDamping = false;
|
|
/// <summary>
|
|
/// The rate at which Target Offset Y is modified by ship pitch input. Higher values are more responsive.
|
|
/// </summary>
|
|
[Range(0.01f, 1f)] public float dampingPitchRate = 0.25f;
|
|
/// <summary>
|
|
/// The rate at which the Target Offset Y returns to normal when there is no ship pitch input. Higher values are more responsive.
|
|
/// </summary>
|
|
[Range(0.01f, 1f)] public float dampingPitchGravity = 0.25f;
|
|
/// <summary>
|
|
/// The rate at which Target Offset X is modified by ship yaw input. Higher values are more responsive.
|
|
/// </summary>
|
|
[Range(0.01f, 1f)] public float dampingYawRate = 0.25f;
|
|
/// <summary>
|
|
/// The rate at which the Target Offset X returns to normal when there is no ship yaw input. Higher values are more responsive.
|
|
/// </summary>
|
|
[Range(0.01f, 1f)] public float dampingYawGravity = 0.25f;
|
|
/// <summary>
|
|
/// The damping maximum pitch Target Offset Up (y-axis)
|
|
/// </summary>
|
|
public float dampingMaxPitchOffsetUp = 2f;
|
|
/// <summary>
|
|
/// The damping maximum pitch Target Offset Down (y-axis)
|
|
/// </summary>
|
|
public float dampingMaxPitchOffsetDown = -2f;
|
|
/// <summary>
|
|
/// The damping maximum yaw Target Offset right (x-axis)
|
|
/// </summary>
|
|
public float dampingMaxYawOffsetRight = 2f;
|
|
/// <summary>
|
|
/// The damping maximum yaw Target Offset left (x-axis)
|
|
/// </summary>
|
|
public float dampingMaxYawOffsetLeft = -2f;
|
|
|
|
/// <summary>
|
|
/// If enabled, the camera will stay locked to the optimal camera rotation.
|
|
/// </summary>
|
|
public bool lockToTargetRotation = false;
|
|
/// <summary>
|
|
/// How quickly the camera turns towards the optimal camera rotation. Only relevant when lockToTargetRotation is disabled.
|
|
/// </summary>
|
|
[Range(1f, 100f)] public float turnSpeed = 15f;
|
|
/// <summary>
|
|
/// When cameraRotationMode is Aim At Target, enabling this will enable the camera to track the target
|
|
/// without moving in the scene.
|
|
/// </summary>
|
|
public bool lockCameraPosition = false;
|
|
/// <summary>
|
|
/// How the camera rotation is determined.
|
|
/// FollowVelocity: The camera rotates to face in the direction the ship is moving in.
|
|
/// FollowTargetRotation: The camera rotates to face the direction the ship is facing in.
|
|
/// AimAtTarget: The camera rotates to face towards the ship itself.
|
|
/// </summary>
|
|
public CameraRotationMode cameraRotationMode = CameraRotationMode.FollowTargetRotation;
|
|
/// <summary>
|
|
/// Below this velocity (in metres per second) the forwards direction of the target will be followed instead of the velocity.
|
|
/// Only relevant when cameraRotationMode is set to FollowVelocity or TopDownFollowVelocity.
|
|
/// </summary>
|
|
public float followVelocityThreshold = 10f;
|
|
/// <summary>
|
|
/// If enabled, the camera will orient with respect to the world up direction rather than the target's up direction.
|
|
/// </summary>
|
|
public bool orientUpwards = false;
|
|
/// <summary>
|
|
/// The rotation of the camera. Only relevant when cameraRotationMode is set to Fixed.
|
|
/// </summary>
|
|
public Vector3 cameraFixedRotation = Vector3.zero;
|
|
|
|
/// <summary>
|
|
/// When the camera position/rotation is updated.
|
|
/// FixedUpdate: The update occurs during FixedUpdate. Recommended for rigidbodies with Interpolation set to None.
|
|
/// LateUpdate: The update occurs during LateUpdate. Recommended for rigidbodies with Interpolation set to Interpolate.
|
|
/// Automatic: When the update occurs is automatically determined.
|
|
/// </summary>
|
|
public CameraUpdateType updateType = CameraUpdateType.Automatic;
|
|
|
|
/// <summary>
|
|
/// The maximum strength of the camera shake. Smaller numbers are better.
|
|
/// This can be overridden by calling ShakeCamera(duration,strength)
|
|
/// If modifying at runtime, you must call ReinitialiseTargetVariables().
|
|
/// </summary>
|
|
[Range(0f,0.5f)] public float maxShakeStrength = 0f;
|
|
|
|
/// <summary>
|
|
/// The maximum duration (in seconds) the camera will shake per incident.
|
|
/// This can be overridden by calling ShakeCamera(duration,strength).
|
|
/// If modifying at runtime, you must call ReinitialiseTargetVariables().
|
|
/// </summary>
|
|
[Range(0.1f, 5f)] public float maxShakeDuration = 0.2f;
|
|
|
|
/// <summary>
|
|
/// [INTERNAL ONLY]
|
|
/// </summary>
|
|
[HideInInspector] public bool allowRepaint = false;
|
|
|
|
#endregion
|
|
|
|
#region Public Variables - Object Clipping
|
|
|
|
/// <summary>
|
|
/// Adjust the camera position to attempt to avoid the camera flying through objects between the ship and the camera.
|
|
/// </summary>
|
|
public bool clipObjects = false;
|
|
|
|
/// <summary>
|
|
/// The minimum speed the camera will move to avoid flying through objects between the ship and the camera.
|
|
/// High values make clipping more effective. Lower values will make it smoother.
|
|
/// Currently this has no effect if Lock to Target Position is enabled.
|
|
/// </summary>
|
|
[Range(1f, 100f)] public float minClipMoveSpeed = 10f;
|
|
|
|
/// <summary>
|
|
/// When clipObjects is true, the minimum distance the camera can be from the Ship (target) position.
|
|
/// Typically this is the spheric radius of the ship. If the ship has colliders that do not overlay the
|
|
/// target position, this value should be set, else set to 0 to improve performance.
|
|
/// </summary>
|
|
[Range(0f,1000f)] public float clipMinDistance = 0f;
|
|
|
|
/// <summary>
|
|
/// The minimum offset on the x-axis, in metres, the camera can be from the Ship (target) when object clipping. This should be less than or equal to the Target Offset X value.
|
|
/// </summary>
|
|
[Range(0f, 50f)] public float clipMinOffsetX = 0f;
|
|
|
|
/// <summary>
|
|
/// The minimum offset on the y-axis, in metres, the camera can be from the Ship (target) when object clipping. This should be less than or equal to the Target Offset Y value.
|
|
/// </summary>
|
|
[Range(0f, 50)] public float clipMinOffsetY = 0f;
|
|
|
|
/// <summary>
|
|
/// Clip objects in the selected Unity Layers.
|
|
/// Start with Nothing (0) and call ResetClipObjectSettings()
|
|
/// </summary>
|
|
public LayerMask clipObjectMask = 0;
|
|
|
|
#endregion
|
|
|
|
#region Public Variables - Zoom
|
|
|
|
/// <summary>
|
|
/// The time, in seconds, to zoom fully in or out
|
|
/// </summary>
|
|
[Range(0.1f, 20f)] public float zoomDuration = 3f;
|
|
|
|
/// <summary>
|
|
/// The delay, in seconds, before zoom starts to return to the non-zoomed position
|
|
/// </summary>
|
|
[Range(0f, 3600f)] public float unzoomDelay = 0f;
|
|
|
|
/// <summary>
|
|
/// The camera field-of-view when no zoom is applied
|
|
/// </summary>
|
|
[Range(20f, 85f)] public float unzoomedFoV = 60f;
|
|
|
|
/// <summary>
|
|
/// The camera field-of-view when the camera is fully zoomed in.
|
|
/// </summary>
|
|
[Range(1f, 50f)] public float zoomedInFoV = 10f;
|
|
|
|
/// <summary>
|
|
/// The camera field-of-view when the camera is fully zoomed out.
|
|
/// </summary>
|
|
[Range(40f, 150f)] public float zoomedOutFoV = 90f;
|
|
|
|
/// <summary>
|
|
/// The amount of damping applied when starting or stopping camera zoom
|
|
/// </summary>
|
|
[Range(0f, 1f)] public float zoomDamping = 0.1f;
|
|
|
|
#endregion
|
|
|
|
#region Public Member Properties
|
|
|
|
/// <summary>
|
|
/// Is the camera being moved using FixedUpdate()?
|
|
/// </summary>
|
|
public bool IsCameraInFixedUpdate { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Is camera started and rendering?
|
|
/// </summary>
|
|
public bool IsCameraStarted { get { return camera1 == null ? false : camera1.enabled; } }
|
|
|
|
/// <summary>
|
|
/// Is the camera enabled for movement?
|
|
/// </summary>
|
|
public bool IsCameraEnabled { get { return cameraIsEnabled; } }
|
|
|
|
/// <summary>
|
|
/// Get the camera being used by this module
|
|
/// </summary>
|
|
public Camera GetCamera1 { get { return camera1; } }
|
|
|
|
#endregion
|
|
|
|
#region Public Static Properties
|
|
|
|
/// <summary>
|
|
/// Everything, except TransparentFX (1), IgnoreRaycast (2), UI (5)
|
|
/// </summary>
|
|
public static LayerMask DefaultClipObjectMask { get { return ~((1 << 1) | (1 << 2) | (1 << 5)); } }
|
|
|
|
#endregion
|
|
|
|
#region Private Variables
|
|
|
|
private Rigidbody rBody;
|
|
|
|
private Camera camera1;
|
|
|
|
private Transform targetTrfm;
|
|
private Rigidbody targetRBody;
|
|
private Vector3 currentPosition;
|
|
|
|
private Vector3 targetPosition;
|
|
private Quaternion targetRotation;
|
|
|
|
private Vector3 targetTrfmUp;
|
|
private Vector3 targetTrfmFwd;
|
|
|
|
private Vector3 targetOffsetDamped = Vector3.zero;
|
|
private float dampingOffsetAdjPitch = 0f, dampingOffsetAdjYaw = 0f;
|
|
|
|
private Vector3 optimalCameraPosition;
|
|
private Vector3 previousOptimalCameraPosition = Vector3.zero;
|
|
private Quaternion optimalCameraRotation;
|
|
private Vector3 optimalCameraForward;
|
|
private Vector3 optimalCameraUp;
|
|
|
|
//private Vector3 currentMoveDampVelocity = Vector3.zero;
|
|
private Vector3 currentTurnDampVelocity = Vector3.zero;
|
|
private Vector3 optimalCameraRotationEulerAngles;
|
|
private Vector3 currentCameraRotationEulerAngles;
|
|
|
|
private bool cameraIsEnabled = true;
|
|
|
|
// Camera shake variables - See MoveCamera() and ShakeCamera(..).
|
|
private bool isShaking = false;
|
|
private float shakeStrength = 1f;
|
|
private float shakeTimer = 0f;
|
|
|
|
// When initialised, was there an audioListener component present and enabled?
|
|
// This may not be the case when it is a point-of-interest camera in the scene,
|
|
// like in TechDemo2.
|
|
private bool isAudioListenerConfigured = false;
|
|
private AudioListener audioListener = null;
|
|
|
|
// Object clipping
|
|
private Vector3 viewHalfExtents = Vector3.zero;
|
|
|
|
// Zoom
|
|
private float currentZoomInput = 0f;
|
|
private float previousZoomInput = 0f;
|
|
private float zoomFactor = 0f;
|
|
private float unzoomTimer = 0f;
|
|
private bool isZoomInputProcessed = false;
|
|
|
|
[SerializeField] private bool isZoomEnabled = false;
|
|
|
|
#endregion
|
|
|
|
#region Awake Method
|
|
|
|
// Use this for initialization
|
|
void Awake()
|
|
{
|
|
// Get the rigidbody attached to the camera
|
|
// Caters for the situation when script was already attached, but without a rigidbody
|
|
if (!TryGetComponent(out rBody))
|
|
{
|
|
rBody = gameObject.AddComponent<Rigidbody>();
|
|
#if UNITY_EDITOR
|
|
Debug.Log("ShipCameraModule - Adding missing Rigidbody");
|
|
#endif
|
|
}
|
|
|
|
// Set up the camera rigidbody
|
|
rBody.mass = 1f;
|
|
rBody.isKinematic = true;
|
|
rBody.interpolation = RigidbodyInterpolation.None;
|
|
|
|
// Initialise target variables
|
|
if (target != null) { ReinitialiseTargetVariables(); }
|
|
|
|
isShaking = false;
|
|
|
|
audioListener = GetComponent<AudioListener>();
|
|
isAudioListenerConfigured = audioListener == null ? false : audioListener.enabled;
|
|
|
|
ReinitialiseCameraSettings();
|
|
|
|
if (startOnInitialise)
|
|
{
|
|
StartCamera();
|
|
if (!enableOnInitialise) { DisableCamera(); }
|
|
}
|
|
else { StopCamera(); }
|
|
|
|
previousOptimalCameraPosition = transform.position;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Update Methods
|
|
|
|
// FixedUpdate is called once per physics update
|
|
void FixedUpdate ()
|
|
{
|
|
// Move camera in FixedUpdate if either:
|
|
// - We are in FixedUpdate mode
|
|
// - We are in Automatic mode and the target rigidbody has interpolation disabled
|
|
if (cameraIsEnabled && target != null && targetRBody != null && (updateType == CameraUpdateType.FixedUpdate || (updateType == CameraUpdateType.Automatic &&
|
|
targetRBody.interpolation == RigidbodyInterpolation.None)))
|
|
{
|
|
IsCameraInFixedUpdate = true;
|
|
MoveCamera();
|
|
}
|
|
}
|
|
|
|
// LateUpdate is called once per frame, after all update functions have been called
|
|
void LateUpdate ()
|
|
{
|
|
// Move camera in LateUpdate if either:
|
|
// - We are in LateUpdate mode
|
|
// - We are in Automatic mode and the target rigidbody has interpolation enabled
|
|
if (cameraIsEnabled && target != null && targetRBody != null && (updateType == CameraUpdateType.LateUpdate || (updateType == CameraUpdateType.Automatic &&
|
|
targetRBody.interpolation != RigidbodyInterpolation.None)))
|
|
{
|
|
IsCameraInFixedUpdate = false;
|
|
MoveCamera();
|
|
}
|
|
|
|
if (cameraIsEnabled && isZoomEnabled)
|
|
{
|
|
CalcZoomFactor();
|
|
ZoomCamera();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private and Internal Non-Static Methods
|
|
|
|
/// <summary>
|
|
/// Calculate the zoom factor based on zoom input and any unzoom delay.
|
|
/// </summary>
|
|
private void CalcZoomFactor()
|
|
{
|
|
// Is the user (or program) attempting to manually zoom in or out
|
|
if (currentZoomInput != 0f)
|
|
{
|
|
zoomFactor += currentZoomInput * Time.deltaTime / zoomDuration;
|
|
|
|
if (zoomFactor > 1f) { zoomFactor = 1f; }
|
|
else if (zoomFactor < -1f) { zoomFactor = -1f; }
|
|
|
|
unzoomTimer = unzoomDelay;
|
|
|
|
// Reset zoom input after use
|
|
isZoomInputProcessed = true;
|
|
currentZoomInput = 0f;
|
|
}
|
|
else if (unzoomTimer <= 0f)
|
|
{
|
|
previousZoomInput = 0f;
|
|
|
|
// +ve zoom, start reducing zoom towards no zoom
|
|
if (zoomFactor > 0f)
|
|
{
|
|
// If it has almost returned to 0, stop zooming
|
|
if (zoomFactor < Vector3.kEpsilon) { zoomFactor = 0f; }
|
|
else
|
|
{
|
|
zoomFactor -= Time.deltaTime / zoomDuration;
|
|
if (zoomFactor < 0f) { zoomFactor = 0f; }
|
|
}
|
|
}
|
|
// -ve zoom, start increasing zoom towards no zoom
|
|
else
|
|
{
|
|
// If it has almost returned to 0, stop zooming
|
|
if (-zoomFactor < Vector3.kEpsilon) { zoomFactor = 0f; }
|
|
else
|
|
{
|
|
zoomFactor += Time.deltaTime / zoomDuration;
|
|
if (zoomFactor > 0f) { zoomFactor = 0f; }
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
unzoomTimer -= Time.deltaTime;
|
|
if (unzoomTimer < 0f) { unzoomTimer = 0f; }
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enable or Disable the zoom feature
|
|
/// </summary>
|
|
/// <param name="isEnabled"></param>
|
|
private void EnableOrDisableZoom(bool isEnabled)
|
|
{
|
|
if (isEnabled)
|
|
{
|
|
SetZoomAmount(0f);
|
|
currentZoomInput = 0f;
|
|
previousZoomInput = 0f;
|
|
}
|
|
else
|
|
{
|
|
SetZoomAmount(0f);
|
|
ZoomCamera();
|
|
}
|
|
|
|
isZoomEnabled = isEnabled;
|
|
}
|
|
|
|
private Vector3 SmoothFollowPosition(Vector3 currentPosition, Vector3 previousTargetPosition, Vector3 currentTargetPosition, float deltaTime, float speed)
|
|
{
|
|
// Avoid NaN when speed = 0.
|
|
if (speed < 0.001f) { return currentPosition; }
|
|
else
|
|
{
|
|
float speedTimeProduct = deltaTime * speed;
|
|
Vector3 v = (currentTargetPosition - previousTargetPosition) / speedTimeProduct;
|
|
Vector3 f = currentPosition - previousTargetPosition + v;
|
|
return currentTargetPosition - v + f * Mathf.Exp(-speedTimeProduct);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This is called by a ship when it is hit.
|
|
/// The shakeAmount should be proportion of the maximum duration and strength as a
|
|
/// normalised value (between 0.0 and 1.0).
|
|
/// </summary>
|
|
/// <param name="shakeAmountNormalised">Normalised amount between 0.0 and 1.0</param>
|
|
internal void ShakeCameraInternal(float shakeAmountNormalised)
|
|
{
|
|
ShakeCamera(maxShakeDuration * shakeAmountNormalised, maxShakeStrength * shakeAmountNormalised);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempt to not fly through objects in the scene that are in the clipObjectMask layer(s)
|
|
/// </summary>
|
|
private bool ObjectClipping(Vector3 aimAtPosition, Quaternion targetRot, ref Vector3 cameraPosition, ref Quaternion cameraRotation)
|
|
{
|
|
// Get the direction looking from the target to the camera.
|
|
Vector3 clipLookDirection = (cameraPosition - aimAtPosition).normalized;
|
|
|
|
Vector3 clipStartPosition = clipMinDistance > 0f ? aimAtPosition + (clipLookDirection * clipMinDistance) : aimAtPosition;
|
|
|
|
// The distance between the ship (or ship + clipMinDistance) and the camera LESS the camera near clip plane (don't need
|
|
// to check what camera does not render).
|
|
float clipCheckDistance = Vector3.Distance(clipStartPosition, cameraPosition) - camera1.nearClipPlane;
|
|
|
|
RaycastHit clipHit;
|
|
if (Physics.BoxCast(clipStartPosition, viewHalfExtents, clipLookDirection, out clipHit,
|
|
cameraRotation, clipCheckDistance, clipObjectMask, QueryTriggerInteraction.Ignore))
|
|
{
|
|
// The near clip plane should be placed in front of the obstacle. By not adding the nearClipPlane
|
|
// onto the hit distance, it should cater for situations where the obstacle normal is not aligned with the clipLookDirection.
|
|
cameraPosition = clipStartPosition + (clipLookDirection * clipHit.distance);
|
|
|
|
if (clipMinOffsetX > 0f || clipMinOffsetY > 0f)
|
|
{
|
|
// Get local space offset
|
|
Vector3 currentOffset = Quaternion.Inverse(targetRot) * (cameraPosition - aimAtPosition);
|
|
|
|
if (currentOffset.y < clipMinOffsetY || currentOffset.x < clipMinOffsetX)
|
|
{
|
|
cameraPosition = aimAtPosition + (targetRot * new Vector3(currentOffset.x < clipMinOffsetX ? clipMinOffsetX : currentOffset.x, currentOffset.y < clipMinOffsetY ? clipMinOffsetY : currentOffset.y, currentOffset.z));
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else { return false; }
|
|
}
|
|
|
|
//Vector3 clipStartPosition = Vector3.zero, clipLookDirection = Vector3.zero;
|
|
//float clipCheckDistance;
|
|
|
|
//private void OnDrawGizmos()
|
|
//{
|
|
// if (camera1 != null)
|
|
// {
|
|
// // Test Object Clipping
|
|
// //Gizmos.color = Color.yellow;
|
|
// //Gizmos.DrawRay(optimalCameraPosition, clipLookDirection * 20f);
|
|
|
|
// Gizmos.color = Color.blue;
|
|
// Gizmos.DrawRay(clipStartPosition, clipLookDirection * (Vector3.Distance(clipStartPosition, optimalCameraPosition) - camera1.nearClipPlane));
|
|
// Gizmos.DrawWireCube(clipStartPosition, viewHalfExtents * 2f);
|
|
// }
|
|
//}
|
|
|
|
/// <summary>
|
|
/// Adjust the target position based on the player or AI input.
|
|
/// This should only be called when target is not null and
|
|
/// isLockCameraPosition is not true.
|
|
/// </summary>
|
|
private void TargetOffsetPositionDamping()
|
|
{
|
|
// Get pitch and yaw input from the ship
|
|
Vector3 momentInput = target.pilotMomentInput;
|
|
float _dTFactor = Time.deltaTime * 10f;
|
|
|
|
// Pitch - ship goes toward bottom of screen while pointing up
|
|
if (momentInput.x > 0f)
|
|
{
|
|
dampingOffsetAdjPitch += -momentInput.x * dampingPitchRate * _dTFactor;
|
|
if (dampingOffsetAdjPitch < dampingMaxPitchOffsetDown)
|
|
{
|
|
dampingOffsetAdjPitch = dampingMaxPitchOffsetDown;
|
|
}
|
|
}
|
|
// Pitch - ship goes toward top of screen while pointing down
|
|
else if (momentInput.x < 0f)
|
|
{
|
|
dampingOffsetAdjPitch += -momentInput.x * dampingPitchRate * _dTFactor;
|
|
if (dampingOffsetAdjPitch > dampingMaxPitchOffsetUp)
|
|
{
|
|
dampingOffsetAdjPitch = dampingMaxPitchOffsetUp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (dampingOffsetAdjPitch > targetOffset.y)
|
|
{
|
|
dampingOffsetAdjPitch -= dampingPitchGravity * _dTFactor;
|
|
if (dampingOffsetAdjPitch < targetOffset.y)
|
|
{
|
|
dampingOffsetAdjPitch = targetOffset.y;
|
|
}
|
|
}
|
|
else if (dampingOffsetAdjPitch < targetOffset.y)
|
|
{
|
|
dampingOffsetAdjPitch += dampingPitchGravity * _dTFactor;
|
|
if (dampingOffsetAdjPitch > targetOffset.y)
|
|
{
|
|
dampingOffsetAdjPitch = targetOffset.y;
|
|
}
|
|
}
|
|
}
|
|
|
|
//dampingOffsetAdjPitch = targetOffset.y;
|
|
|
|
// Yaw - ship goes right, camera goes left
|
|
if (momentInput.y > 0f)
|
|
{
|
|
dampingOffsetAdjYaw += momentInput.y * dampingYawRate * _dTFactor;
|
|
if (dampingOffsetAdjYaw > dampingMaxYawOffsetRight)
|
|
{
|
|
dampingOffsetAdjYaw = dampingMaxYawOffsetRight;
|
|
}
|
|
}
|
|
// Yaw - ship goes left, camera goes right
|
|
else if (momentInput.y < 0f)
|
|
{
|
|
dampingOffsetAdjYaw += momentInput.y * dampingYawRate * _dTFactor;
|
|
if (dampingOffsetAdjYaw < dampingMaxYawOffsetLeft)
|
|
{
|
|
dampingOffsetAdjYaw = dampingMaxYawOffsetLeft;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (dampingOffsetAdjYaw > targetOffset.x)
|
|
{
|
|
dampingOffsetAdjYaw -= dampingYawGravity * _dTFactor;
|
|
if (dampingOffsetAdjYaw < targetOffset.x)
|
|
{
|
|
dampingOffsetAdjYaw = targetOffset.x;
|
|
}
|
|
}
|
|
else if (dampingOffsetAdjYaw < targetOffset.x)
|
|
{
|
|
dampingOffsetAdjYaw += dampingYawGravity * _dTFactor;
|
|
if (dampingOffsetAdjYaw > targetOffset.x)
|
|
{
|
|
dampingOffsetAdjYaw = targetOffset.x;
|
|
}
|
|
}
|
|
}
|
|
|
|
targetOffsetDamped.x = dampingOffsetAdjYaw;
|
|
targetOffsetDamped.y = dampingOffsetAdjPitch;
|
|
targetOffsetDamped.z = targetOffset.z;
|
|
|
|
//Debug.Log("[DEBUG] targetOffsetDamped: " + targetOffsetDamped + " T:" + Time.time);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public API Non-Static Methods
|
|
|
|
/// <summary>
|
|
/// Attempt to apply camera settings from a ScriptableObject to this ShipCameraModule.
|
|
/// See also ExportCameraSettings(..)
|
|
/// </summary>
|
|
/// <param name="shipCameraSettings"></param>
|
|
public virtual void ApplyCameraSettings (ShipCameraSettings shipCameraSettings)
|
|
{
|
|
if (shipCameraSettings != null)
|
|
{
|
|
cameraFixedRotation = shipCameraSettings.cameraFixedRotation;
|
|
cameraRotationMode = shipCameraSettings.cameraRotationMode;
|
|
clipMinDistance = shipCameraSettings.clipMinDistance;
|
|
clipMinOffsetX = shipCameraSettings.clipMinOffsetX;
|
|
clipMinOffsetY = shipCameraSettings.clipMinOffsetY;
|
|
clipObjectMask = shipCameraSettings.clipObjectMask;
|
|
clipObjects = shipCameraSettings.clipObjects;
|
|
dampingMaxPitchOffsetDown = shipCameraSettings.dampingMaxPitchOffsetDown;
|
|
dampingMaxPitchOffsetUp = shipCameraSettings.dampingMaxPitchOffsetUp;
|
|
dampingMaxYawOffsetLeft = shipCameraSettings.dampingMaxYawOffsetLeft;
|
|
dampingMaxYawOffsetRight = shipCameraSettings.dampingMaxYawOffsetRight;
|
|
dampingPitchGravity = shipCameraSettings.dampingPitchGravity;
|
|
dampingPitchRate = shipCameraSettings.dampingPitchRate;
|
|
dampingYawGravity = shipCameraSettings.dampingYawGravity;
|
|
dampingYawRate = shipCameraSettings.dampingYawRate;
|
|
followVelocityThreshold = shipCameraSettings.followVelocityThreshold;
|
|
isZoomEnabled = shipCameraSettings.isZoomEnabled;
|
|
lockCameraPosition = shipCameraSettings.lockCameraPosition;
|
|
lockToTargetPosition = shipCameraSettings.lockToTargetPosition;
|
|
lockToTargetRotation = shipCameraSettings.lockToTargetRotation;
|
|
maxShakeDuration = shipCameraSettings.maxShakeDuration;
|
|
maxShakeStrength = shipCameraSettings.maxShakeStrength;
|
|
minClipMoveSpeed = shipCameraSettings.minClipMoveSpeed;
|
|
moveSpeed = shipCameraSettings.moveSpeed;
|
|
orientUpwards = shipCameraSettings.orientUpwards;
|
|
targetOffset = shipCameraSettings.targetOffset;
|
|
targetOffsetCoordinates = shipCameraSettings.targetOffsetCoordinates;
|
|
targetOffsetDamping = shipCameraSettings.targetOffsetDamping;
|
|
turnSpeed = shipCameraSettings.turnSpeed;
|
|
unzoomDelay = shipCameraSettings.unzoomDelay;
|
|
unzoomedFoV = shipCameraSettings.unzoomedFoV;
|
|
updateType = shipCameraSettings.updateType;
|
|
zoomDamping = shipCameraSettings.zoomDamping;
|
|
zoomDuration = shipCameraSettings.zoomDuration;
|
|
zoomedInFoV = shipCameraSettings.zoomedInFoV;
|
|
zoomedOutFoV = shipCameraSettings.zoomedOutFoV;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Disables the camera, preventing it from moving (does not stop the camera rendering).
|
|
/// </summary>
|
|
public void DisableCamera()
|
|
{
|
|
cameraIsEnabled = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Turn off the zoom feature and reset the camera field of view to the default setting.
|
|
/// </summary>
|
|
public void DisableZoom()
|
|
{
|
|
EnableOrDisableZoom(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enables the camera, allowing it to move.
|
|
/// See StartCamera() to allow it to render.
|
|
/// </summary>
|
|
public void EnableCamera()
|
|
{
|
|
cameraIsEnabled = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Turn on the zoom feature
|
|
/// </summary>
|
|
public void EnableZoom()
|
|
{
|
|
EnableOrDisableZoom(true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempt to export the camera setings from this ShipCameraModule to a pre-created ShipCameraSettings ScriptableObject.
|
|
/// See also ApplyCameraSettings(..)
|
|
/// </summary>
|
|
/// <param name="shipCameraSettings"></param>
|
|
public virtual void ExportCameraSettings (ref ShipCameraSettings shipCameraSettings)
|
|
{
|
|
if (shipCameraSettings != null)
|
|
{
|
|
shipCameraSettings.cameraFixedRotation = cameraFixedRotation;
|
|
shipCameraSettings.cameraRotationMode = cameraRotationMode;
|
|
shipCameraSettings.clipMinDistance = clipMinDistance;
|
|
shipCameraSettings.clipMinOffsetX = clipMinOffsetX;
|
|
shipCameraSettings.clipMinOffsetY = clipMinOffsetY;
|
|
shipCameraSettings.clipObjectMask = clipObjectMask;
|
|
shipCameraSettings.clipObjects = clipObjects;
|
|
shipCameraSettings.dampingMaxPitchOffsetDown = dampingMaxPitchOffsetDown;
|
|
shipCameraSettings.dampingMaxPitchOffsetUp = dampingMaxPitchOffsetUp;
|
|
shipCameraSettings.dampingMaxYawOffsetLeft = dampingMaxYawOffsetLeft;
|
|
shipCameraSettings.dampingMaxYawOffsetRight = dampingMaxYawOffsetRight;
|
|
shipCameraSettings.dampingPitchGravity = dampingPitchGravity;
|
|
shipCameraSettings.dampingPitchRate = dampingPitchRate;
|
|
shipCameraSettings.dampingYawGravity = dampingYawGravity;
|
|
shipCameraSettings.dampingYawRate = dampingYawRate;
|
|
shipCameraSettings.followVelocityThreshold = followVelocityThreshold;
|
|
shipCameraSettings.isZoomEnabled = isZoomEnabled;
|
|
shipCameraSettings.lockCameraPosition = lockCameraPosition;
|
|
shipCameraSettings.lockToTargetPosition = lockToTargetPosition;
|
|
shipCameraSettings.lockToTargetRotation = lockToTargetRotation;
|
|
shipCameraSettings.maxShakeDuration = maxShakeDuration;
|
|
shipCameraSettings.maxShakeStrength = maxShakeStrength;
|
|
shipCameraSettings.minClipMoveSpeed = minClipMoveSpeed;
|
|
shipCameraSettings.moveSpeed = moveSpeed;
|
|
shipCameraSettings.orientUpwards = orientUpwards;
|
|
shipCameraSettings.targetOffset = targetOffset;
|
|
shipCameraSettings.targetOffsetCoordinates = targetOffsetCoordinates;
|
|
shipCameraSettings.targetOffsetDamping = targetOffsetDamping;
|
|
shipCameraSettings.turnSpeed = turnSpeed;
|
|
shipCameraSettings.unzoomDelay = unzoomDelay;
|
|
shipCameraSettings.unzoomedFoV = unzoomedFoV;
|
|
shipCameraSettings.updateType = updateType;
|
|
shipCameraSettings.zoomDamping = zoomDamping;
|
|
shipCameraSettings.zoomDuration = zoomDuration;
|
|
shipCameraSettings.zoomedInFoV = zoomedInFoV;
|
|
shipCameraSettings.zoomedOutFoV = zoomedOutFoV;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the current target ship for the camera. If the camera is currently not assigned to a ship, it will return null.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public ShipControlModule GetTarget()
|
|
{
|
|
return target;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The amount of zoom currently used.
|
|
/// 0 is no zoom.
|
|
/// 1 is fully zoomed in.
|
|
/// -1 is fully zoomed out.
|
|
/// </summary>
|
|
/// <param name="zoomAmount"></param>
|
|
public float GetZoomAmount()
|
|
{
|
|
return zoomFactor;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Start the camera rendering. If the camera is disabled, it will remain so.
|
|
/// Call EnableCamera() to allow it to also move.
|
|
/// If there was an Audio Listener component attached and enabled when this
|
|
/// module was initialised, the listener will be re-enabled.
|
|
/// </summary>
|
|
public void StartCamera()
|
|
{
|
|
if (camera1 != null && !camera1.enabled)
|
|
{
|
|
camera1.enabled = true;
|
|
if (isAudioListenerConfigured && audioListener != null) { audioListener.enabled = true; }
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stops the camera from rendering. Also disables the camera from moving.
|
|
/// If there was an Audio Listener component attached and enabled when this
|
|
/// module was initialised, the listener will be disabled.
|
|
/// </summary>
|
|
public void StopCamera()
|
|
{
|
|
StopCameraShake();
|
|
cameraIsEnabled = false;
|
|
if (camera1 != null && camera1.enabled) { camera1.enabled = false; }
|
|
if (isAudioListenerConfigured && audioListener != null) { audioListener.enabled = false; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set's a new target for the camera and calls ReinitialiseTargetVariables().
|
|
/// </summary>
|
|
/// <param name="shipControlModule"></param>
|
|
public void SetTarget(ShipControlModule shipControlModule)
|
|
{
|
|
// reset the previous target's camera shake callback method
|
|
if (target != null && target.shipInstance != null) { target.shipInstance.callbackOnCameraShake = null; }
|
|
|
|
target = shipControlModule;
|
|
ReinitialiseTargetVariables();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Moves the camera. Should be called during an update function (Update, LateUpdate or FixedUpdate).
|
|
/// Typically, you should not call this yourself, as it is called automatically each frame. However,
|
|
/// it can prove useful in the case where you need to force a camera movement update for this frame.
|
|
/// </summary>
|
|
public void MoveCamera()
|
|
{
|
|
#region Initialisation
|
|
|
|
targetPosition = targetTrfm.position;
|
|
targetRotation = targetTrfm.rotation;
|
|
|
|
// Get current position
|
|
currentPosition = transform.position;
|
|
|
|
// Get target transform up and forwards directions
|
|
targetTrfmUp = targetRotation * Vector3.up;
|
|
targetTrfmFwd = targetRotation * Vector3.forward;
|
|
|
|
// By default this always off. Is only available below
|
|
// when CameraRotationMode is AimAtTarget
|
|
bool isLockCameraPosition = false;
|
|
|
|
// Is camera still shaking?
|
|
if (isShaking)
|
|
{
|
|
shakeTimer -= Time.deltaTime;
|
|
if (shakeTimer <= 0f) { StopCameraShake(); }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Find Optimal Camera Rotation
|
|
|
|
//bool followingTargetRotation = false;
|
|
|
|
// In late update mode, use the interpolated world velocity of the rigidbody instead of the normal world velocity
|
|
Vector3 shipWorldVelocity = Vector3.zero;
|
|
if (cameraRotationMode == CameraRotationMode.FollowVelocity || cameraRotationMode == CameraRotationMode.TopDownFollowVelocity)
|
|
{
|
|
if (IsCameraInFixedUpdate)
|
|
{
|
|
shipWorldVelocity = target.shipInstance.WorldVelocity;
|
|
}
|
|
else
|
|
{
|
|
shipWorldVelocity = target.shipInstance.GetInterpolatedWorldVelocity();
|
|
}
|
|
}
|
|
|
|
// Find the optimal camera up direction
|
|
if (cameraRotationMode == CameraRotationMode.TopDownFollowVelocity)
|
|
{
|
|
// When above a given velocity, this is the ship's flattened velocity vector
|
|
if (shipWorldVelocity.sqrMagnitude > followVelocityThreshold * followVelocityThreshold)
|
|
{
|
|
optimalCameraUp = shipWorldVelocity;
|
|
optimalCameraUp.y = 0f;
|
|
optimalCameraUp.Normalize();
|
|
}
|
|
// When below that velocity, this is the ship's flattened forward vector
|
|
else
|
|
{
|
|
optimalCameraUp = targetTrfmFwd;
|
|
optimalCameraUp.y = 0f;
|
|
optimalCameraUp.Normalize();
|
|
}
|
|
}
|
|
else if (cameraRotationMode == CameraRotationMode.TopDownFollowTargetRotation)
|
|
{
|
|
// If we are using the top down follow target rotation mode, this is the ship's flattened forward vector
|
|
optimalCameraUp = targetTrfmFwd;
|
|
optimalCameraUp.y = 0f;
|
|
optimalCameraUp.Normalize();
|
|
}
|
|
else if (cameraRotationMode == CameraRotationMode.Fixed)
|
|
{
|
|
// If we are using the fixed mode, this is the camera rotation's up vector
|
|
optimalCameraUp = Quaternion.Euler(cameraFixedRotation) * Vector3.up;
|
|
}
|
|
else
|
|
{
|
|
// If we are orienting upwards, this is the world up direction
|
|
if (orientUpwards) { optimalCameraUp = Vector3.up; }
|
|
// Otherwise this is the target up direction
|
|
else { optimalCameraUp = targetTrfmUp; }
|
|
}
|
|
|
|
// Find the optimal camera forwards direction
|
|
if (cameraRotationMode == CameraRotationMode.AimAtTarget)
|
|
{
|
|
// Aim at target mode: Forwards is towards the target
|
|
optimalCameraForward = (targetPosition - currentPosition).normalized;
|
|
isLockCameraPosition = lockCameraPosition;
|
|
//followingTargetRotation = false;
|
|
}
|
|
else if (cameraRotationMode == CameraRotationMode.FollowVelocity)
|
|
{
|
|
// Follow velocity mode: When above a given velocity, forwards is in direction of target velocity
|
|
if (shipWorldVelocity.sqrMagnitude > followVelocityThreshold * followVelocityThreshold)
|
|
{
|
|
optimalCameraForward = shipWorldVelocity.normalized;
|
|
//followingTargetRotation = false;
|
|
}
|
|
// When below that velocity, forwards is in direction of target forwards direction
|
|
else
|
|
{
|
|
optimalCameraForward = targetTrfmFwd;
|
|
//followingTargetRotation = true;
|
|
}
|
|
}
|
|
else if (cameraRotationMode == CameraRotationMode.FollowTargetRotation)
|
|
{
|
|
// Follow target rotation mode: Forwards is in direction of target forwards direction
|
|
optimalCameraForward = targetTrfmFwd;
|
|
}
|
|
else if (cameraRotationMode == CameraRotationMode.Fixed)
|
|
{
|
|
// Fixed mode: Forwards is camera rotation's forwards vector
|
|
optimalCameraForward = Quaternion.Euler(cameraFixedRotation) * Vector3.forward;
|
|
}
|
|
else
|
|
{
|
|
// Top down modes: Forwards is world down direction
|
|
optimalCameraForward = Vector3.down;
|
|
}
|
|
|
|
// Set optimal camera rotation from optimal camera forwards and up directions
|
|
optimalCameraRotation = Quaternion.LookRotation(optimalCameraForward, optimalCameraUp);
|
|
|
|
#endregion
|
|
|
|
#region Find Optimal Camera Position
|
|
|
|
if (!isLockCameraPosition)
|
|
{
|
|
if (targetOffsetDamping) { TargetOffsetPositionDamping(); }
|
|
else { targetOffsetDamped = targetOffset; }
|
|
|
|
//targetOffsetDamped = targetOffset;
|
|
|
|
// Optimal camera position is always target position plus some offset
|
|
if (targetOffsetCoordinates == TargetOffsetCoordinates.TargetRotation)
|
|
{
|
|
// Offset is applied with respect to target rotation
|
|
optimalCameraPosition = targetPosition + (targetRotation * targetOffsetDamped);
|
|
}
|
|
else if (targetOffsetCoordinates == TargetOffsetCoordinates.TargetRotationFlat)
|
|
{
|
|
// Offset is applied with respect to target rotation, but only rotation on the world y-axis
|
|
Vector3 targetTrfmFwdFlat = targetTrfmFwd;
|
|
targetTrfmFwdFlat.y = 0f;
|
|
optimalCameraPosition = targetPosition + (Quaternion.LookRotation(targetTrfmFwdFlat, Vector3.up) * targetOffsetDamped);
|
|
}
|
|
else if (targetOffsetCoordinates == TargetOffsetCoordinates.CameraRotation)
|
|
{
|
|
// Offset is applied with respect to optimal camera rotation forwards and up direction vectors
|
|
optimalCameraPosition = targetPosition + (targetOffsetDamped.z * optimalCameraForward) + (targetOffsetDamped.y * optimalCameraUp);
|
|
if (targetOffsetDamped.x != 0f)
|
|
{
|
|
optimalCameraPosition += targetOffsetDamped.x * Vector3.Cross(optimalCameraUp, optimalCameraForward);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Offset is applied with respect to the world coordinate system
|
|
optimalCameraPosition = targetPosition + targetOffsetDamped;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Object Clipping
|
|
bool isClippingThisFrame = false;
|
|
if (clipObjects) { isClippingThisFrame = ObjectClipping(targetPosition, targetRotation, ref optimalCameraPosition, ref optimalCameraRotation); }
|
|
#endregion
|
|
|
|
#region Compute Movement Towards Optimal Camera Position / Rotation
|
|
|
|
if (isLockCameraPosition && isShaking)
|
|
{
|
|
// Might be a way of doing this in one step
|
|
//transform.position = previousOptimalCameraPosition;
|
|
//transform.Translate(Random.insideUnitCircle * shakeStrength);
|
|
}
|
|
if (!isLockCameraPosition)
|
|
{
|
|
// Move immediately to optimal camera position
|
|
if (lockToTargetPosition)
|
|
{
|
|
if (isShaking)
|
|
{
|
|
// Might be a way of doing this in one step
|
|
transform.position = optimalCameraPosition;
|
|
transform.Translate(Random.insideUnitCircle * shakeStrength);
|
|
}
|
|
else { transform.position = optimalCameraPosition; }
|
|
}
|
|
// Move gradually towards optimal camera position
|
|
else
|
|
{
|
|
// Smooth follow code (substitute for Lerp)
|
|
// [IMPORTANT] If you get a NaN error message, it means you didn't call DisableCamera() before setting Time.timeScale = 0.
|
|
// When clipObjects is enabled, and something is blocking camera, move quickly to target position
|
|
transform.position = SmoothFollowPosition(currentPosition, previousOptimalCameraPosition, optimalCameraPosition, Time.deltaTime, isClippingThisFrame && moveSpeed < minClipMoveSpeed ? minClipMoveSpeed : moveSpeed);
|
|
|
|
if (isShaking)
|
|
{
|
|
transform.Translate(Random.insideUnitCircle * shakeStrength);
|
|
}
|
|
|
|
previousOptimalCameraPosition = optimalCameraPosition;
|
|
}
|
|
}
|
|
|
|
// Move immediately to optimal camera rotation
|
|
if (lockToTargetRotation) { transform.rotation = optimalCameraRotation; }
|
|
// Move gradually towards optimal camera rotation
|
|
else
|
|
{
|
|
// Smooth damp code (substitute for Lerp) - modified to work with rotation
|
|
// First, convert quaternions into Euler angles vectors
|
|
currentCameraRotationEulerAngles = transform.rotation.eulerAngles;
|
|
optimalCameraRotationEulerAngles = optimalCameraRotation.eulerAngles;
|
|
// Then, use SmoothDampAngle on each of the three axes separately
|
|
currentCameraRotationEulerAngles.x = Mathf.SmoothDampAngle(currentCameraRotationEulerAngles.x, optimalCameraRotationEulerAngles.x,
|
|
ref currentTurnDampVelocity.x, 1f / turnSpeed, Mathf.Infinity, Time.deltaTime);
|
|
currentCameraRotationEulerAngles.y = Mathf.SmoothDampAngle(currentCameraRotationEulerAngles.y, optimalCameraRotationEulerAngles.y,
|
|
ref currentTurnDampVelocity.y, 1f / turnSpeed, Mathf.Infinity, Time.deltaTime);
|
|
currentCameraRotationEulerAngles.z = Mathf.SmoothDampAngle(currentCameraRotationEulerAngles.z, optimalCameraRotationEulerAngles.z,
|
|
ref currentTurnDampVelocity.z, 1f / turnSpeed, Mathf.Infinity, Time.deltaTime);
|
|
// Finally, convert Euler angles vector back to quaternion
|
|
transform.rotation = Quaternion.Euler(currentCameraRotationEulerAngles);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tele-port the camera to a new position.
|
|
/// Rotation is xyz euler angles in degrees.
|
|
/// See also TelePort(..)
|
|
/// </summary>
|
|
/// <param name="position"></param>
|
|
/// <param name="rotation"></param>
|
|
public void MoveTo(Vector3 position, Vector3 rotation)
|
|
{
|
|
// Should be the same as setting the transform directly but included
|
|
// we'll update the variables too.
|
|
|
|
previousOptimalCameraPosition = position;
|
|
optimalCameraRotation = Quaternion.Euler(rotation);
|
|
|
|
transform.SetPositionAndRotation(position, optimalCameraRotation);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Call this whenever changing camera properties
|
|
/// </summary>
|
|
public void ReinitialiseCameraSettings()
|
|
{
|
|
if (camera1 == null) { camera1 = GetComponent<Camera>(); }
|
|
|
|
if (camera1 != null)
|
|
{
|
|
// Calculate the half width, height and depth of the camera view. This can be used with
|
|
// Physics.Boxcast to "see" objects in front of the camera.
|
|
viewHalfExtents = Vector3.zero;
|
|
// fieldOfView is in radians. Convert to degrees.
|
|
viewHalfExtents.y = camera1.nearClipPlane * Mathf.Tan(0.5f * Mathf.Deg2Rad * camera1.fieldOfView);
|
|
viewHalfExtents.x = viewHalfExtents.y * camera1.aspect;
|
|
|
|
// Reset zoom
|
|
SetZoomAmount(0f);
|
|
}
|
|
|
|
// The start value is set to Nothing (0) intentially. We then set it to something sensible
|
|
if (clipObjectMask == 0) { ResetClipObjectSettings(); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reset the clip object settings to their default values
|
|
/// </summary>
|
|
public void ResetClipObjectSettings()
|
|
{
|
|
// Everything, except TransparentFX (1), IgnoreRaycast (2), UI (5)
|
|
//clipObjectMask = ~((1 << 1) | (1 << 2) | (1 << 5));
|
|
clipObjectMask = DefaultClipObjectMask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Re-initialises variables related to the target.
|
|
/// Call after modifying target or shake public variables.
|
|
/// This will stop camera from shaking.
|
|
/// </summary>
|
|
public void ReinitialiseTargetVariables()
|
|
{
|
|
if (target != null)
|
|
{
|
|
targetTrfm = target.transform;
|
|
targetRBody = target.GetComponent<Rigidbody>();
|
|
|
|
if (target.shipInstance != null)
|
|
{
|
|
if (maxShakeStrength > 0f && maxShakeDuration > 0f) { target.shipInstance.callbackOnCameraShake = ShakeCameraInternal; }
|
|
else { target.shipInstance.callbackOnCameraShake = null; }
|
|
}
|
|
}
|
|
else
|
|
{
|
|
targetTrfm = null;
|
|
targetRBody = null;
|
|
}
|
|
|
|
dampingOffsetAdjPitch = 0f;
|
|
dampingOffsetAdjYaw = 0f;
|
|
|
|
StopCameraShake();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shake the camera for maxShakeDuration seconds which maxShakeStrength or force.
|
|
/// If the camera is not enabled or the duration and/or strength are 0 or less,
|
|
/// StopCameraShake() will be automatically called and the inputs ignored.
|
|
/// </summary>
|
|
public void ShakeCamera()
|
|
{
|
|
ShakeCamera(maxShakeDuration, maxShakeStrength);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shake the camera with strength and duration relative to
|
|
/// the current maxShakeDuration and maxShakeStrength.
|
|
/// Range should be between 0.0 and 1.0.
|
|
/// </summary>
|
|
/// <param name="relativeStrength">Normalised value between 0.0 and 1.0</param>
|
|
public void ShakeCamera (float relativeStrength)
|
|
{
|
|
if (relativeStrength < 0.0001f) { StopCameraShake(); }
|
|
else if (relativeStrength >= 1f) { ShakeCamera(maxShakeDuration, maxShakeStrength); }
|
|
else { ShakeCameraInternal(relativeStrength); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shake the camera for specified seconds which the given relative strength or force.
|
|
/// If the camera is not enabled or the duration and/or strength are 0 or less,
|
|
/// StopCameraShake() will be automatically called and the inputs ignored.
|
|
/// </summary>
|
|
/// <param name="duration"></param>
|
|
/// <param name="strength"></param>
|
|
public void ShakeCamera (float duration, float strength)
|
|
{
|
|
if (cameraIsEnabled && duration > 0f && strength > 0f)
|
|
{
|
|
isShaking = true;
|
|
shakeStrength = strength;
|
|
shakeTimer = duration;
|
|
}
|
|
else
|
|
{
|
|
StopCameraShake();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shake the camera after initial delay in seconds, with the
|
|
/// current maxShakeDuration and maxShakeStrength.
|
|
/// </summary>
|
|
/// <param name="delayTime"></param>
|
|
public void ShakeCameraDelayed (float delayTime)
|
|
{
|
|
if (delayTime > 0f)
|
|
{
|
|
Invoke("ShakeCamera", delayTime);
|
|
}
|
|
else { ShakeCamera(maxShakeDuration, maxShakeStrength); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Send zoom input to the camera. It is ignored if Zoom is not enabled.
|
|
/// Values should be between -1.0 and 1.0.
|
|
/// Zoom in for +ve values. Zoom out for -ve values.
|
|
/// The value is automatically reset to 0 after use.
|
|
/// </summary>
|
|
/// <param name="zoomInput"></param>
|
|
public void SendZoomInput (float zoomInput)
|
|
{
|
|
if (isZoomEnabled)
|
|
{
|
|
currentZoomInput = zoomInput < -1f ? -1f : zoomInput > 1f ? 1f : zoomInput;
|
|
|
|
currentZoomInput = SSCMath.DampValue(previousZoomInput, currentZoomInput, Time.deltaTime, zoomDamping);
|
|
previousZoomInput = currentZoomInput;
|
|
|
|
if (isZoomInputProcessed) { isZoomInputProcessed = false; }
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the unzoom delay, in seconds, for the camera.
|
|
/// </summary>
|
|
/// <param name="newValue"></param>
|
|
public void SetUnzoomDelay(float newValue)
|
|
{
|
|
if (newValue < 0f) { newValue = 0f; }
|
|
|
|
unzoomDelay = newValue;
|
|
if (newValue < unzoomTimer) { unzoomTimer = newValue; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the camera to use a particular display or screen. Displays or monitors are numbered from 1 to 8.
|
|
/// </summary>
|
|
/// <param name="displayNumber">1 to 8</param>
|
|
public void SetCameraTargetDisplay(int displayNumber)
|
|
{
|
|
Camera camera = GetComponent<Camera>();
|
|
|
|
if (camera != null && SSCUtils.VerifyTargetDisplay(displayNumber, true)) { camera.targetDisplay = displayNumber - 1; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempt to set a new maxShakeDuration
|
|
/// </summary>
|
|
/// <param name="newDuration"></param>
|
|
public void SetMaxShakeDuration (float newDuration)
|
|
{
|
|
if (newDuration > 0f)
|
|
{
|
|
maxShakeDuration = newDuration;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempt to set a new maxShakeStrenth
|
|
/// </summary>
|
|
/// <param name="newStrength"></param>
|
|
public void SetMaxShakeStrength (float newStrength)
|
|
{
|
|
if (newStrength > 0f)
|
|
{
|
|
maxShakeStrength = newStrength;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The amount of zoom to apply.
|
|
/// 0 is no zoom.
|
|
/// 1 is fully zoomed in.
|
|
/// -1 is fully zoomed out.
|
|
/// </summary>
|
|
/// <param name="zoomAmount"></param>
|
|
public void SetZoomAmount(float zoomAmount)
|
|
{
|
|
if (zoomAmount > 1f) { zoomFactor = 1f; }
|
|
else if (zoomAmount < -1f) { zoomFactor = 1f; }
|
|
else { zoomFactor = zoomAmount; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stop the camera from shaking
|
|
/// </summary>
|
|
public void StopCameraShake()
|
|
{
|
|
isShaking = false;
|
|
shakeTimer = 0f;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Teleport the camera to a new location by moving by an amount
|
|
/// in the x, y and z directions. This could be useful if changing
|
|
/// the origin or centre of your world to compensate for float-point
|
|
/// error.
|
|
/// </summary>
|
|
/// <param name="delta"></param>
|
|
public void TelePort (Vector3 delta)
|
|
{
|
|
previousOptimalCameraPosition += delta;
|
|
transform.position += delta;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Teleport the camera to a new location with given rotation in world space
|
|
/// </summary>
|
|
/// <param name="newPosition"></param>
|
|
/// <param name="newRotation"></param>
|
|
public void TelePort (Vector3 newPosition, Quaternion newRotation)
|
|
{
|
|
Vector3 delta = newPosition - transform.position;
|
|
previousOptimalCameraPosition += delta;
|
|
transform.position = newPosition;
|
|
transform.rotation = newRotation;
|
|
}
|
|
|
|
/// <summary>
|
|
/// TODO - Zoom WIP
|
|
/// Apply the current zoom amount to the camera.
|
|
/// This gets automatically applied in LateUpdate()
|
|
/// when zoom is enabled.
|
|
/// </summary>
|
|
public void ZoomCamera()
|
|
{
|
|
if (camera1 != null)
|
|
{
|
|
if (zoomFactor == 0f)
|
|
{
|
|
camera1.fieldOfView = unzoomedFoV;
|
|
}
|
|
else if (zoomFactor > 0f)
|
|
{
|
|
camera1.fieldOfView = unzoomedFoV - (zoomFactor * (unzoomedFoV - zoomedInFoV));
|
|
}
|
|
else
|
|
{
|
|
camera1.fieldOfView = unzoomedFoV - (zoomFactor * (zoomedOutFoV - unzoomedFoV));
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|