1193 lines
39 KiB
C#
1193 lines
39 KiB
C#
//----------------------------------------------
|
|
// Realistic Car Controller
|
|
//
|
|
// Copyright © 2014 - 2023 BoneCracker Games
|
|
// https://www.bonecrackergames.com
|
|
// Buğra Özdoğanlar
|
|
//
|
|
//----------------------------------------------
|
|
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
|
|
/// <summary>
|
|
/// Main RCC Camera controller. Includes 6 different camera modes with many customizable settings. It doesn't use different cameras on your scene like *other* assets. Simply it parents the camera to their positions that's all. No need to be Einstein.
|
|
/// Also supports collision detection.
|
|
/// </summary>
|
|
[AddComponentMenu("BoneCracker Games/Realistic Car Controller/Camera/RCC Camera")]
|
|
public class RCC_Camera : MonoBehaviour {
|
|
|
|
#region OBSOLETE VARIABLES
|
|
|
|
[System.Obsolete("Use FixedCamera instead of fixedCamera")]
|
|
private RCC_FixedCamera fixedCamera {
|
|
|
|
get {
|
|
|
|
return FixedCamera;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
[System.Obsolete("Use CinematicCamera instead of cinematicCamera")]
|
|
private RCC_CinematicCamera cinematicCamera {
|
|
|
|
get {
|
|
|
|
return CinematicCamera;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endregion
|
|
|
|
// Main camera target, which is our player vehicle.
|
|
[System.Serializable]
|
|
public class CameraTarget {
|
|
|
|
public RCC_CarControllerV3 playerVehicle; // Player vehicle.
|
|
|
|
// Vehicle speed.
|
|
public float Speed {
|
|
|
|
get {
|
|
|
|
if (!playerVehicle)
|
|
return 0f;
|
|
|
|
return playerVehicle.speed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Vehicle velocity.
|
|
public Vector3 Velocity {
|
|
|
|
get {
|
|
|
|
if (!playerVehicle)
|
|
return Vector3.zero;
|
|
|
|
return playerVehicle.transform.InverseTransformDirection(playerVehicle.Rigid.velocity);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Hood camera of the vehicle.
|
|
public RCC_HoodCamera HoodCamera {
|
|
|
|
get {
|
|
|
|
if (!playerVehicle)
|
|
return null;
|
|
|
|
if (!_hoodCamera)
|
|
_hoodCamera = playerVehicle.GetComponentInChildren<RCC_HoodCamera>();
|
|
|
|
return _hoodCamera;
|
|
|
|
}
|
|
|
|
}
|
|
private RCC_HoodCamera _hoodCamera;
|
|
|
|
// Wheel camera of the vehicle.
|
|
public RCC_WheelCamera WheelCamera {
|
|
|
|
get {
|
|
|
|
if (!playerVehicle)
|
|
return null;
|
|
|
|
if (!_wheelCamera)
|
|
_wheelCamera = playerVehicle.GetComponentInChildren<RCC_WheelCamera>();
|
|
|
|
return _wheelCamera;
|
|
|
|
}
|
|
|
|
}
|
|
private RCC_WheelCamera _wheelCamera;
|
|
|
|
}
|
|
|
|
public CameraTarget cameraTarget = new CameraTarget(); // Target of the camera, which is our player vehicle with custom class. Can be assigned manually with "SetTarget" method.
|
|
|
|
public bool isRendering = true; // Currently rendering?
|
|
public Camera actualCamera; // Camera is not attached to this main gameobject. Camera is parented to pivot gameobject. Therefore, we can apply additional position and rotation changes.
|
|
public GameObject pivot; // Pivot center of the camera. Used for making offsets and collision movements.
|
|
|
|
// Camera Modes.
|
|
public enum CameraMode { TPS, FPS, WHEEL, FIXED, CINEMATIC, TOP }
|
|
public CameraMode cameraMode = CameraMode.TPS;
|
|
private CameraMode lastCameraMode = CameraMode.TPS;
|
|
|
|
private RCC_FixedCamera FixedCamera { get { return RCC_FixedCamera.Instance; } }
|
|
private RCC_CinematicCamera CinematicCamera { get { return RCC_CinematicCamera.Instance; } }
|
|
|
|
public bool TPSLockX = true; // Locks X angle to vehicle's X.
|
|
public bool TPSLockY = true; // Locks Y angle to vehicle's Y.
|
|
public bool TPSLockZ = true; // Locks Z angle to vehicle's Z.
|
|
public bool TPSFreeFall = true; // Camera rotation won't track vehicle if it's not grounded.
|
|
public bool TPSDynamic = false; // Use dynamic distance, height, and pitch angle related to vehicle rigidbody velocity.
|
|
|
|
public bool useTopCameraMode = false; // Shall we use top camera mode?
|
|
public bool useHoodCameraMode = true; // Shall we use hood camera mode?
|
|
public bool useOrbitInTPSCameraMode = true; // Shall we use orbit control in TPS camera mode?
|
|
public bool useOrbitInHoodCameraMode = true; // Shall we use orbit control in hood camera mode?
|
|
public bool useWheelCameraMode = true; //Shall we use wheel camera mode?
|
|
public bool useFixedCameraMode = true; // Shall we use fixed camera mode?
|
|
public bool useCinematicCameraMode = true; // Shall we use cinematic camera mode?
|
|
public bool useOrthoForTopCamera = false; // Shall we use ortho in top camera mode?
|
|
public bool useOcclusion = true; // Shall we use camera occlusion?
|
|
public LayerMask occlusionLayerMask = -1; // Camera will be ocluded by these layers.
|
|
private bool occluded = false; // Currently camera is occluding?
|
|
|
|
public bool useAutoChangeCamera = false; // Shall we change camera mode by auto? (For cinematics)
|
|
private float autoChangeCameraTimer = 0f; // Timer for changing the camera mode auto.
|
|
|
|
public Vector3 topCameraAngle = new Vector3(45f, 45f, 0f); // We will use this Vector3 angle for top camera mode if it's enabled.
|
|
public float topCameraDistance = 100f; // Top camera height / distance.
|
|
public float maximumZDistanceOffset = 10f; // Distance offset for top camera mode. Related with vehicle speed. If vehicle speed is higher, camera will move to front of the vehicle.
|
|
private float topCameraDistanceOffset = 0f;
|
|
|
|
// Target position.
|
|
private Vector3 targetPosition = Vector3.zero;
|
|
|
|
// Used for resetting orbit values when direction of the vehicle has been changed.
|
|
private int direction = 1;
|
|
private int lastDirection = 1;
|
|
|
|
[Range(0f, 20f)] public float TPSDistance = 6f; // The distance for TPS camera mode.
|
|
[Range(0f, 10f)] public float TPSHeight = 2f; // The height we want the camera to be above the target for TPS camera mode.
|
|
[Range(0f, 1f)] public float TPSRotationDamping = .7f; // Rotation movement damper.
|
|
[Range(0f, 25f)] public float TPSTiltMaximum = 15f; // Maximum tilt angle related with rigidbody local velocity.
|
|
[Range(0f, 10f)] public float TPSTiltMultiplier = 1.5f; // Tilt angle multiplier.
|
|
[Range(-45f, 45f)] public float TPSYaw = 0f; // Yaw angle.
|
|
[Range(-45f, 45f)] public float TPSPitch = 10f; // Pitch angle.
|
|
|
|
public bool TPSAutoFocus = true; // Auto focus to player vehicle. Adjusts distance and height depends on vehicle bounds.
|
|
public bool TPSAutoReverse = true; // Auto reverse when player vehicle is at reverse gear.
|
|
public bool TPSCollision = true; // Collision effect when player vehicle crashes.
|
|
public Vector3 TPSOffset = new Vector3(0f, 0f, .2f); // TPS position offset.
|
|
public Vector3 TPSStartRotation = new Vector3(0f, 0f, 0f); // Rotation of the camera will be this when game starts.
|
|
private Quaternion TPSLastRotation; // Last rotation of the camera.
|
|
private float TPSTiltAngle = 0f; // Current tilt angle.
|
|
|
|
internal float targetFieldOfView = 60f; // Camera will adapt its field of view to this target field of view. All field of views below this line will feed this value.
|
|
|
|
[Range(10f, 90f)] public float TPSMinimumFOV = 40f; // Minimum field of view related with vehicle speed.
|
|
[Range(10f, 160f)] public float TPSMaximumFOV = 60f; // Maximum field of view related with vehicle speed.
|
|
|
|
[Range(10f, 160f)] public float hoodCameraFOV = 60f; // Hood field of view.
|
|
[Range(10f, 160f)] public float wheelCameraFOV = 60f; // Wheel field of view.
|
|
|
|
public float minimumOrtSize = 10f; // Minimum ortho size related with vehicle speed.
|
|
public float maximumOrtSize = 20f; // Maximum ortho size related with vehicle speed.
|
|
|
|
internal int cameraSwitchCount = 0; // Used in switch case for running corresponding camera mode method.
|
|
|
|
private float xVelocity, yVelocity, zVelocity = 0f; // Ref values for smooth damp and rotation.
|
|
private Vector3 accelerationVelocity = Vector3.zero;
|
|
|
|
public Vector3 acceleration = Vector3.zero;
|
|
public Vector3 lastVelocity = Vector3.zero;
|
|
public Vector3 acceleration_Smoothed = Vector3.zero;
|
|
|
|
private Vector3 collisionDirection = Vector3.zero; // Collision direction.
|
|
private Vector3 collisionPos = Vector3.zero; // Collision position.
|
|
private Quaternion collisionRot = Quaternion.identity; // Collision rotation.
|
|
|
|
[Range(.5f, 10f)] public float zoomScrollMultiplier = 5f; // Zoom scroll multiplier.
|
|
private float zoomScroll = 0; // Zoom scroll amount now.
|
|
public float minimumScroll = 0f; // Minimum distane for the zoom.
|
|
public float maximumScroll = 5f; // Maximum distance for the zoom.
|
|
|
|
// Raw Orbit X and Y inputs.
|
|
private float orbitX, orbitY = 0f;
|
|
|
|
// Smooth Orbit X and Y inputs.
|
|
private float orbitX_Smoothed, orbitY_Smoothed = 0f;
|
|
|
|
// Minimum and maximum Orbit X, Y degrees.
|
|
public float minOrbitY = -15f;
|
|
public float maxOrbitY = 70f;
|
|
|
|
// Orbit X and Y speeds.
|
|
public float orbitXSpeed = 100f;
|
|
public float orbitYSpeed = 100f;
|
|
public float orbitSmooth = 40f;
|
|
|
|
// Resetting orbits.
|
|
public bool orbitReset = false;
|
|
private float orbitResetTimer = 0f;
|
|
private float oldOrbitX, oldOrbitY = 0f;
|
|
private float timeSinceSpawn = 0f;
|
|
|
|
public bool lookBackNow = false; // Camera is looking back now?
|
|
|
|
// Event when camera spawned.
|
|
public delegate void onBCGCameraSpawned(GameObject BCGCamera);
|
|
public static event onBCGCameraSpawned OnBCGCameraSpawned;
|
|
|
|
private void Awake() {
|
|
|
|
// Getting Camera.
|
|
actualCamera = GetComponentInChildren<Camera>();
|
|
|
|
// If pivot of the camera is not found, create it.
|
|
if (!pivot) {
|
|
|
|
pivot = transform.Find("Pivot").gameObject;
|
|
|
|
if (!pivot)
|
|
pivot = new GameObject("Pivot");
|
|
|
|
pivot.transform.SetParent(transform);
|
|
pivot.transform.localPosition = Vector3.zero;
|
|
pivot.transform.localRotation = Quaternion.identity;
|
|
actualCamera.transform.SetParent(pivot.transform, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
private void OnEnable() {
|
|
|
|
ResetCameraVariables();
|
|
|
|
// Calling this event when BCG Camera spawned.
|
|
if (OnBCGCameraSpawned != null)
|
|
OnBCGCameraSpawned(gameObject);
|
|
|
|
// Listening player vehicle collisions for crashing effects.
|
|
RCC_CarControllerV3.OnRCCPlayerCollision += RCC_CarControllerV3_OnRCCPlayerCollision;
|
|
|
|
// Listening input events for camera modes and look back.
|
|
RCC_InputManager.OnChangeCamera += RCC_InputManager_OnChangeCamera;
|
|
RCC_InputManager.OnLookBack += RCC_InputManager_OnLookBack;
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Listening collision info of the target vehicle.
|
|
/// </summary>
|
|
/// <param name="RCC"></param>
|
|
/// <param name="collision"></param>
|
|
private void RCC_CarControllerV3_OnRCCPlayerCollision(RCC_CarControllerV3 RCC, Collision collision) {
|
|
|
|
Collision(collision);
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Look back with button event.
|
|
/// </summary>
|
|
/// <param name="state"></param>
|
|
private void RCC_InputManager_OnLookBack(bool state) {
|
|
|
|
lookBackNow = state;
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Change camera with button event.
|
|
/// </summary>
|
|
private void RCC_InputManager_OnChangeCamera() {
|
|
|
|
ChangeCamera();
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets target vehicle of the camera.
|
|
/// </summary>
|
|
/// <param name="player"></param>
|
|
public void SetTarget(RCC_CarControllerV3 player) {
|
|
|
|
// Setting target vehicle.
|
|
cameraTarget = new CameraTarget {
|
|
playerVehicle = player
|
|
};
|
|
|
|
// If auto focus is enabled, adjust distance and height of the camera automatically.
|
|
if (TPSAutoFocus)
|
|
StartCoroutine(AutoFocus());
|
|
|
|
// And reset the camera modes.
|
|
SetupForNewMode();
|
|
|
|
TPSLastRotation = player.transform.rotation;
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes target vehicle.
|
|
/// </summary>
|
|
public void RemoveTarget() {
|
|
|
|
transform.SetParent(null);
|
|
cameraTarget.playerVehicle = null;
|
|
|
|
}
|
|
|
|
private void Update() {
|
|
|
|
timeSinceSpawn += Time.deltaTime;
|
|
acceleration_Smoothed = Vector3.SmoothDamp(acceleration_Smoothed, acceleration, ref accelerationVelocity, .3f);
|
|
|
|
// If it's active, enable the camera. If it's not, disable the camera.
|
|
if (!isRendering) {
|
|
|
|
if (actualCamera.gameObject.activeSelf)
|
|
actualCamera.gameObject.SetActive(false);
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
if (!actualCamera.gameObject.activeSelf)
|
|
actualCamera.gameObject.SetActive(true);
|
|
|
|
}
|
|
|
|
// Lerping current field of view to target field of view.
|
|
actualCamera.fieldOfView = Mathf.Lerp(actualCamera.fieldOfView, targetFieldOfView, Time.deltaTime * 5f);
|
|
|
|
// Early out if we don't have the player vehicle.
|
|
if (!cameraTarget.playerVehicle)
|
|
return;
|
|
|
|
// Receive inputs.
|
|
Inputs();
|
|
|
|
}
|
|
|
|
private void LateUpdate() {
|
|
|
|
// Early out if we don't have the player vehicle.
|
|
if (!cameraTarget.playerVehicle)
|
|
return;
|
|
|
|
// Even if we have the player vehicle and it's disabled, return.
|
|
if (!cameraTarget.playerVehicle.gameObject.activeSelf)
|
|
return;
|
|
|
|
if (Time.timeScale <= 0)
|
|
return;
|
|
|
|
// Run the corresponding method with choosen camera mode.
|
|
switch (cameraMode) {
|
|
|
|
case CameraMode.TPS:
|
|
|
|
if (useOrbitInTPSCameraMode)
|
|
ORBIT();
|
|
|
|
TPS();
|
|
|
|
break;
|
|
|
|
case CameraMode.FPS:
|
|
|
|
if (useOrbitInHoodCameraMode)
|
|
ORBIT();
|
|
|
|
FPS();
|
|
|
|
break;
|
|
|
|
case CameraMode.WHEEL:
|
|
WHEEL();
|
|
break;
|
|
|
|
case CameraMode.FIXED:
|
|
FIXED();
|
|
break;
|
|
|
|
case CameraMode.CINEMATIC:
|
|
CINEMATIC();
|
|
break;
|
|
|
|
case CameraMode.TOP:
|
|
TOP();
|
|
break;
|
|
|
|
}
|
|
|
|
// If camera mode has been changed in last frame, reset the modes.
|
|
if (lastCameraMode != cameraMode)
|
|
SetupForNewMode();
|
|
|
|
lastCameraMode = cameraMode;
|
|
|
|
// If auto change camera is enabled, change the camera mode each 10 seconds.
|
|
if (useAutoChangeCamera)
|
|
autoChangeCameraTimer += Time.deltaTime;
|
|
|
|
// If auto change camera is enabled, change the camera mode each 10 seconds.
|
|
if (useAutoChangeCamera && autoChangeCameraTimer >= 10) {
|
|
|
|
autoChangeCameraTimer = 0f;
|
|
ChangeCamera();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
private void FixedUpdate() {
|
|
|
|
// Early out if we don't have the player vehicle.
|
|
if (!cameraTarget.playerVehicle)
|
|
return;
|
|
|
|
// Even if we have the player vehicle and it's disabled, return.
|
|
if (!cameraTarget.playerVehicle.gameObject.activeSelf)
|
|
return;
|
|
|
|
// Checking if camera is occluded by some colliders.
|
|
CheckIfOccluded();
|
|
|
|
acceleration = (cameraTarget.playerVehicle.transform.InverseTransformDirection(cameraTarget.playerVehicle.Rigid.velocity) - lastVelocity) / Time.fixedDeltaTime;
|
|
lastVelocity = cameraTarget.playerVehicle.transform.InverseTransformDirection(cameraTarget.playerVehicle.Rigid.velocity);
|
|
|
|
acceleration.x = 0f;
|
|
acceleration.y = 0f;
|
|
acceleration = Vector3.ClampMagnitude(acceleration, 10f);
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Receiving player inputs for orbiting the camera and zoom.
|
|
/// </summary>
|
|
private void Inputs() {
|
|
|
|
if (timeSinceSpawn < 1f)
|
|
return;
|
|
|
|
// Receiving player inputs
|
|
RCC_Inputs inputs = RCC_InputManager.Instance.inputs;
|
|
|
|
// Setting orbits.
|
|
orbitX += inputs.orbitX;
|
|
orbitY -= inputs.orbitY;
|
|
|
|
// Clamping orbit Y.
|
|
orbitY = Mathf.Clamp(orbitY, minOrbitY, maxOrbitY);
|
|
|
|
// Smoothing orbits.
|
|
orbitX_Smoothed = Mathf.Lerp(orbitX_Smoothed, orbitX, Time.deltaTime * orbitSmooth);
|
|
orbitY_Smoothed = Mathf.Lerp(orbitY_Smoothed, orbitY, Time.deltaTime * orbitSmooth);
|
|
|
|
// Adjusting zoom scroll with player inputs.
|
|
zoomScroll += inputs.scroll.y * zoomScrollMultiplier;
|
|
zoomScroll = Mathf.Clamp(zoomScroll, minimumScroll, maximumScroll);
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Change camera by increasing camera switch counter.
|
|
/// </summary>
|
|
public void ChangeCamera() {
|
|
|
|
// Increasing camera switch counter at each camera changing.
|
|
cameraSwitchCount++;
|
|
|
|
// We have 6 camera modes at total. If camera switch counter is greater than maximum, set it to 0.
|
|
if (cameraSwitchCount >= 6)
|
|
cameraSwitchCount = 0;
|
|
|
|
switch (cameraSwitchCount) {
|
|
|
|
case 0:
|
|
cameraMode = CameraMode.TPS;
|
|
break;
|
|
|
|
case 1:
|
|
if (useHoodCameraMode && cameraTarget.HoodCamera)
|
|
cameraMode = CameraMode.FPS;
|
|
else
|
|
ChangeCamera();
|
|
break;
|
|
|
|
case 2:
|
|
if (useWheelCameraMode && cameraTarget.WheelCamera)
|
|
cameraMode = CameraMode.WHEEL;
|
|
else
|
|
ChangeCamera();
|
|
break;
|
|
|
|
case 3:
|
|
if (useFixedCameraMode && FixedCamera)
|
|
cameraMode = CameraMode.FIXED;
|
|
else
|
|
ChangeCamera();
|
|
break;
|
|
|
|
case 4:
|
|
if (useCinematicCameraMode && CinematicCamera)
|
|
cameraMode = CameraMode.CINEMATIC;
|
|
else
|
|
ChangeCamera();
|
|
break;
|
|
|
|
case 5:
|
|
if (useTopCameraMode)
|
|
cameraMode = CameraMode.TOP;
|
|
else
|
|
ChangeCamera();
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Change camera by directly setting it to specific mode.
|
|
/// </summary>
|
|
/// <param name="mode"></param>
|
|
public void ChangeCamera(CameraMode mode) {
|
|
|
|
cameraMode = mode;
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// FPS mode.
|
|
/// </summary>
|
|
private void FPS() {
|
|
|
|
// Assigning orbit rotation, and transform rotation.
|
|
if (useOrbitInHoodCameraMode)
|
|
transform.rotation = cameraTarget.playerVehicle.transform.rotation * Quaternion.Euler(orbitY_Smoothed, orbitX_Smoothed, 0f);
|
|
else
|
|
transform.rotation = cameraTarget.playerVehicle.transform.rotation;
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wheel mode.
|
|
/// </summary>
|
|
private void WHEEL() {
|
|
|
|
// If camera is occluded by some colliders, change mode to TPS.
|
|
if (useOcclusion && occluded)
|
|
ChangeCamera(CameraMode.TPS);
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unused another TPS method. For testing purposes only.
|
|
/// </summary>
|
|
private void TPS2() {
|
|
|
|
// Position at the target
|
|
Vector3 positionTarget = cameraTarget.playerVehicle.transform.position;
|
|
|
|
// Then offset by distance behind the new angle
|
|
positionTarget += cameraTarget.playerVehicle.transform.rotation * ((-Vector3.forward * TPSDistance) + (Vector3.forward * (cameraTarget.Speed / 15f)));
|
|
positionTarget += Vector3.up * TPSHeight;
|
|
|
|
Vector3 lookAtTarget = cameraTarget.playerVehicle.transform.position;
|
|
lookAtTarget += (cameraTarget.playerVehicle.transform.rotation * TPSOffset);
|
|
|
|
if (Vector3.Distance(transform.position, positionTarget) > 15) {
|
|
|
|
transform.position = cameraTarget.playerVehicle.transform.position;
|
|
transform.position += cameraTarget.playerVehicle.transform.rotation * (-Vector3.forward * TPSDistance);
|
|
transform.position += Vector3.up * TPSHeight;
|
|
|
|
}
|
|
|
|
transform.position = Vector3.Lerp(transform.position, positionTarget, Time.deltaTime * 3f);
|
|
transform.LookAt(lookAtTarget);
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// TPS mode.
|
|
/// </summary>
|
|
private void TPS() {
|
|
|
|
// Setting rotation of the camera to the latest (previos frame) rotation.
|
|
transform.rotation = TPSLastRotation;
|
|
|
|
// If TPS Auto Reverse is enabled and vehicle is moving backwards, reset X and Y orbits when vehicle direction is changed. Camera will look directly rear side of the vehicle.
|
|
if (lastDirection != cameraTarget.playerVehicle.direction)
|
|
direction = cameraTarget.playerVehicle.direction;
|
|
|
|
lastDirection = cameraTarget.playerVehicle.direction;
|
|
|
|
// Vehicle direction angle used for back side camera angle. 0 means forwards, 180 means backwards.
|
|
int dir = 0;
|
|
|
|
float rotDamp = TPSRotationDamping;
|
|
|
|
// Calculate the current rotation angles for TPS mode.
|
|
if (TPSAutoReverse)
|
|
dir = (direction == 1 ? 0 : 180);
|
|
|
|
// Looks back if it's enabled by player input.
|
|
if (lookBackNow) {
|
|
|
|
dir = 180;
|
|
rotDamp = 1f;
|
|
|
|
}
|
|
|
|
// Make sure camera is still checking rotation of the player vehicle.
|
|
if (TPSFreeFall && Time.timeSinceLevelLoad >= 1f) {
|
|
|
|
if (!cameraTarget.playerVehicle.isGrounded)
|
|
rotDamp = -500f;
|
|
|
|
}
|
|
|
|
// X angle.
|
|
float xAngle = 0f;
|
|
|
|
// If TPS Lock X is enabled, set X angle.
|
|
if (TPSLockX)
|
|
xAngle = Mathf.SmoothDampAngle(transform.eulerAngles.x, cameraTarget.playerVehicle.transform.eulerAngles.x * (dir == 180 ? -1f : 1f), ref xVelocity, 1f - rotDamp);
|
|
|
|
if (useOrbitInTPSCameraMode && orbitY != 0)
|
|
xAngle = orbitY_Smoothed;
|
|
|
|
// Y angle.
|
|
float yAngle = 0f;
|
|
|
|
// If TPS Lock Y is enabled, set Y angle.
|
|
if (TPSLockY) {
|
|
|
|
// If orbit for TPS is enabled, process orbit Y. Otherwise process Y of the player vehicle only.
|
|
if (!useOrbitInTPSCameraMode) {
|
|
|
|
yAngle = Mathf.SmoothDampAngle(transform.eulerAngles.y, cameraTarget.playerVehicle.transform.eulerAngles.y + dir, ref yVelocity, 1f - rotDamp);
|
|
|
|
} else {
|
|
|
|
if (orbitX != 0)
|
|
yAngle = Mathf.SmoothDampAngle(transform.eulerAngles.y, cameraTarget.playerVehicle.transform.eulerAngles.y + orbitX_Smoothed, ref yVelocity, .025f);
|
|
else
|
|
yAngle = Mathf.SmoothDampAngle(transform.eulerAngles.y, cameraTarget.playerVehicle.transform.eulerAngles.y + dir, ref yVelocity, 1f - rotDamp);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (useOrbitInTPSCameraMode && orbitX != 0)
|
|
yAngle = Mathf.SmoothDampAngle(transform.eulerAngles.y, orbitX_Smoothed, ref yVelocity, .025f);
|
|
|
|
}
|
|
|
|
// Z Angle.
|
|
float zAngle = 0f;
|
|
|
|
// If TPS Lock Z is enabled, set Z angle.
|
|
if (TPSLockZ)
|
|
zAngle = Mathf.SmoothDampAngle(transform.eulerAngles.z, cameraTarget.playerVehicle.transform.eulerAngles.z, ref zVelocity, 1f - rotDamp);
|
|
|
|
if (float.IsNaN(xAngle) || float.IsNaN(yAngle) || float.IsNaN(zAngle))
|
|
return;
|
|
|
|
// Position at the target.
|
|
Vector3 position = cameraTarget.playerVehicle.transform.position;
|
|
|
|
// Rotation at the target.
|
|
Quaternion rotation = Quaternion.Euler(xAngle, yAngle, zAngle);
|
|
|
|
// Then offset by distance behind the new angle.
|
|
position += rotation * (-Vector3.forward * (TPSDistance + zoomScroll));
|
|
position += cameraTarget.playerVehicle.transform.rotation * TPSOffset;
|
|
position += Vector3.up * TPSHeight;
|
|
|
|
// If TPS dynamic is enabled, reduce distance and height related to the vehicle speed.
|
|
if (TPSDynamic)
|
|
position -= cameraTarget.playerVehicle.transform.rotation * acceleration_Smoothed / 20f;
|
|
|
|
// Setting position and rotation.
|
|
transform.rotation = rotation;
|
|
transform.position = position;
|
|
|
|
// Collision positions and rotations that affects pivot of the camera.
|
|
collisionPos = Vector3.Lerp(collisionPos, Vector3.zero, Time.deltaTime * 5f);
|
|
collisionRot = Quaternion.Lerp(collisionRot, Quaternion.identity, Time.deltaTime * 5f);
|
|
|
|
// Lerping position and rotation of the pivot to collision.
|
|
pivot.transform.localPosition = Vector3.Lerp(pivot.transform.localPosition, collisionPos, Time.deltaTime * 10f);
|
|
pivot.transform.localRotation = Quaternion.Lerp(pivot.transform.localRotation, collisionRot, Time.deltaTime * 10f);
|
|
|
|
// Lerping targetFieldOfView from TPSMinimumFOV to TPSMaximumFOV related with vehicle speed.
|
|
targetFieldOfView = Mathf.Lerp(TPSMinimumFOV, TPSMaximumFOV, Mathf.Abs(cameraTarget.Speed) / 150f);
|
|
|
|
// Rotates camera by Z axis for tilt effect.
|
|
TPSTiltAngle = TPSTiltMaximum * (Mathf.Clamp(cameraTarget.Velocity.x, -1f, 1f) * Mathf.Abs(cameraTarget.Velocity.x) / 250f);
|
|
TPSTiltAngle *= TPSTiltMultiplier;
|
|
|
|
// Checks occlusion if it's enabled.
|
|
if (useOcclusion)
|
|
OccludeRay(cameraTarget.playerVehicle.transform.position);
|
|
|
|
// Assigning last rotation of the camera.
|
|
TPSLastRotation = transform.rotation;
|
|
|
|
// Setting TPS pitch, yaw, and tilt angles.
|
|
transform.rotation *= Quaternion.Euler(TPSPitch, 0f, TPSYaw + TPSTiltAngle);
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fixed mode.
|
|
/// </summary>
|
|
private void FIXED() {
|
|
|
|
// Parenting the camera to the fixed camera gameobject.
|
|
if (FixedCamera.transform.parent != null)
|
|
FixedCamera.transform.SetParent(null);
|
|
|
|
// If occluded by some colliders, change position.
|
|
if (useOcclusion && occluded) {
|
|
|
|
FixedCamera.ChangePosition();
|
|
occluded = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Top mode
|
|
/// </summary>
|
|
private void TOP() {
|
|
|
|
// Setting ortho or perspective?
|
|
actualCamera.orthographic = useOrthoForTopCamera;
|
|
|
|
// Setting distance and target field of view.
|
|
topCameraDistanceOffset = Mathf.Lerp(0f, maximumZDistanceOffset, Mathf.Abs(cameraTarget.Speed) / 100f);
|
|
targetFieldOfView = Mathf.Lerp(minimumOrtSize, maximumOrtSize, Mathf.Abs(cameraTarget.Speed) / 100f);
|
|
actualCamera.orthographicSize = targetFieldOfView;
|
|
|
|
// Setting proper targetPosition for top camera mode.
|
|
targetPosition = cameraTarget.playerVehicle.transform.position;
|
|
targetPosition += cameraTarget.playerVehicle.transform.rotation * Vector3.forward * topCameraDistanceOffset;
|
|
|
|
// Assigning position and rotation.
|
|
transform.position = targetPosition;
|
|
transform.rotation = Quaternion.Euler(topCameraAngle);
|
|
|
|
// Pivot position.
|
|
pivot.transform.localPosition = new Vector3(0f, 0f, -topCameraDistance);
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Orbit mode.
|
|
/// </summary>
|
|
private void ORBIT() {
|
|
|
|
// If latest orbit value is changed, set timer to 2 seconds. We'll be using this timer to reset orbit mode if enabled.
|
|
if (oldOrbitX != orbitX) {
|
|
|
|
oldOrbitX = orbitX;
|
|
orbitResetTimer = 2f;
|
|
|
|
}
|
|
|
|
// If latest orbit value is changed, set timer to 2 seconds. We'll be using this timer to reset orbit mode if enabled.
|
|
if (oldOrbitY != orbitY) {
|
|
|
|
oldOrbitY = orbitY;
|
|
orbitResetTimer = 2f;
|
|
|
|
}
|
|
|
|
// Timer for reset orbit mode if enabled.
|
|
if (orbitResetTimer > 0)
|
|
orbitResetTimer -= Time.deltaTime;
|
|
|
|
Mathf.Clamp(orbitResetTimer, 0f, 2f);
|
|
|
|
// If vehicle speed is above 25 km/h and reset timer is below 0, reset orbit if enabled.
|
|
if (orbitReset && cameraTarget.Speed >= 25f && orbitResetTimer <= 0f) {
|
|
|
|
orbitX = 0f;
|
|
orbitY = 0f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used with mobile UI drag panel.
|
|
/// </summary>
|
|
/// <param name="pointerData"></param>
|
|
public void OnDrag(PointerEventData pointerData) {
|
|
|
|
// Receiving drag input from UI.
|
|
orbitX += pointerData.delta.x * orbitXSpeed / 1000f;
|
|
orbitY -= pointerData.delta.y * orbitYSpeed / 1000f;
|
|
|
|
orbitResetTimer = 0f;
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used with mobile UI drag panel.
|
|
/// </summary>
|
|
/// <param name="x"></param>
|
|
/// <param name="y"></param>
|
|
public void OnDrag(float x, float y) {
|
|
|
|
// Receiving drag input from UI.
|
|
orbitX += x * orbitXSpeed / 10f;
|
|
orbitY -= y * orbitYSpeed / 10f;
|
|
|
|
orbitResetTimer = 0f;
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Cinematic mode.
|
|
/// </summary>
|
|
private void CINEMATIC() {
|
|
|
|
// Parenting the camera to the cinematic camera gameobject.
|
|
if (CinematicCamera.transform.parent != null)
|
|
CinematicCamera.transform.SetParent(null);
|
|
|
|
// Setting target field of view of the camera.
|
|
targetFieldOfView = CinematicCamera.targetFOV;
|
|
|
|
// If occluded by some colliders, change mode to TPS.
|
|
if (useOcclusion && occluded)
|
|
ChangeCamera(CameraMode.TPS);
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Listening collision info of the target vehicle.
|
|
/// </summary>
|
|
/// <param name="collision"></param>
|
|
public void Collision(Collision collision) {
|
|
|
|
// If it's not enable or camera mode is TPS, return.
|
|
if (!enabled || !isRendering || cameraMode != CameraMode.TPS || !TPSCollision)
|
|
return;
|
|
|
|
// Local relative velocity.
|
|
Vector3 colRelVel = collision.relativeVelocity;
|
|
colRelVel *= 1f - Mathf.Abs(Vector3.Dot(transform.up, collision.GetContact(0).normal));
|
|
|
|
float cos = Mathf.Abs(Vector3.Dot(collision.GetContact(0).normal, colRelVel.normalized));
|
|
|
|
if (colRelVel.magnitude * cos >= 5f) {
|
|
|
|
collisionDirection = transform.InverseTransformDirection(colRelVel) / (30f);
|
|
|
|
collisionPos -= collisionDirection * 5f;
|
|
collisionRot = Quaternion.Euler(new Vector3(-collisionDirection.z * 10f, -collisionDirection.y * 10f, -collisionDirection.x * 10f));
|
|
targetFieldOfView = actualCamera.fieldOfView - Mathf.Clamp(collision.relativeVelocity.magnitude, 0f, 15f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resetting camera while switching to the next mode.
|
|
/// </summary>
|
|
private void SetupForNewMode() {
|
|
|
|
ResetCameraVariables();
|
|
|
|
if (FixedCamera)
|
|
FixedCamera.canTrackNow = false;
|
|
|
|
switch (cameraMode) {
|
|
|
|
case CameraMode.TPS:
|
|
transform.SetParent(null);
|
|
targetFieldOfView = TPSMinimumFOV;
|
|
break;
|
|
|
|
case CameraMode.FPS:
|
|
transform.SetParent(cameraTarget.HoodCamera.transform, false);
|
|
transform.localPosition = Vector3.zero;
|
|
transform.localRotation = Quaternion.identity;
|
|
targetFieldOfView = hoodCameraFOV;
|
|
cameraTarget.HoodCamera.FixShake();
|
|
break;
|
|
|
|
case CameraMode.WHEEL:
|
|
transform.SetParent(cameraTarget.WheelCamera.transform, false);
|
|
transform.localPosition = Vector3.zero;
|
|
transform.localRotation = Quaternion.identity;
|
|
targetFieldOfView = wheelCameraFOV;
|
|
break;
|
|
|
|
case CameraMode.FIXED:
|
|
transform.SetParent(FixedCamera.transform, false);
|
|
transform.localPosition = Vector3.zero;
|
|
transform.localRotation = Quaternion.identity;
|
|
targetFieldOfView = 60;
|
|
FixedCamera.canTrackNow = true;
|
|
break;
|
|
|
|
case CameraMode.CINEMATIC:
|
|
transform.SetParent(CinematicCamera.pivot.transform, false);
|
|
transform.localPosition = Vector3.zero;
|
|
transform.localRotation = Quaternion.identity;
|
|
targetFieldOfView = 30f;
|
|
break;
|
|
|
|
case CameraMode.TOP:
|
|
transform.SetParent(null);
|
|
targetFieldOfView = minimumOrtSize;
|
|
pivot.transform.localPosition = Vector3.zero;
|
|
pivot.transform.localRotation = Quaternion.identity;
|
|
targetPosition = cameraTarget.playerVehicle.transform.position;
|
|
targetPosition += cameraTarget.playerVehicle.transform.rotation * Vector3.forward * topCameraDistanceOffset;
|
|
transform.position = cameraTarget.playerVehicle.transform.position;
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resetting camera. Useful when switching between camera modes.
|
|
/// </summary>
|
|
public void ResetCameraVariables() {
|
|
|
|
TPSTiltAngle = 0f;
|
|
|
|
collisionPos = Vector3.zero;
|
|
collisionRot = Quaternion.identity;
|
|
collisionDirection = Vector3.zero;
|
|
|
|
actualCamera.transform.localPosition = Vector3.zero;
|
|
actualCamera.transform.localRotation = Quaternion.identity;
|
|
|
|
pivot.transform.localPosition = collisionPos;
|
|
pivot.transform.localRotation = collisionRot;
|
|
|
|
orbitX = TPSStartRotation.y;
|
|
orbitY = TPSStartRotation.x;
|
|
|
|
zoomScroll = 0f;
|
|
|
|
if (TPSStartRotation != Vector3.zero)
|
|
TPSStartRotation = Vector3.zero;
|
|
|
|
actualCamera.orthographic = false;
|
|
occluded = false;
|
|
|
|
orbitResetTimer = 0f;
|
|
orbitX = 0f;
|
|
orbitY = 0f;
|
|
direction = 1;
|
|
lastDirection = 1;
|
|
lookBackNow = false;
|
|
autoChangeCameraTimer = 0f;
|
|
|
|
acceleration = Vector3.zero;
|
|
lastVelocity = Vector3.zero;
|
|
|
|
targetPosition = Vector3.zero;
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enables or disables the camera.
|
|
/// </summary>
|
|
/// <param name="state"></param>
|
|
public void ToggleCamera(bool state) {
|
|
|
|
// Enabling / disabling activity.
|
|
isRendering = state;
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks occlusion.
|
|
/// </summary>
|
|
/// <param name="targetFollow"></param>
|
|
private void OccludeRay(Vector3 targetFollow) {
|
|
|
|
//declare a new raycast hit.
|
|
RaycastHit wallHit = new RaycastHit();
|
|
|
|
if (Physics.Linecast(targetFollow, transform.position, out wallHit, occlusionLayerMask)) {
|
|
|
|
if (!wallHit.collider.isTrigger && !wallHit.transform.IsChildOf(cameraTarget.playerVehicle.transform)) {
|
|
|
|
//the x and z coordinates are pushed away from the wall by hit.normal.
|
|
//the y coordinate stays the same.
|
|
Vector3 occludedPosition = new Vector3(wallHit.point.x + wallHit.normal.x * .2f, wallHit.point.y + wallHit.normal.y * .2f, wallHit.point.z + wallHit.normal.z * .2f);
|
|
|
|
transform.position = occludedPosition;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks occlusion.
|
|
/// </summary>
|
|
/// <param name="targetFollow"></param>
|
|
private void CheckIfOccluded() {
|
|
|
|
//Declare a new raycast hit.
|
|
RaycastHit wallHit = new RaycastHit();
|
|
|
|
if (Physics.Linecast(cameraTarget.playerVehicle.transform.position, transform.position, out wallHit, occlusionLayerMask)) {
|
|
|
|
if (!wallHit.collider.isTrigger && !wallHit.transform.IsChildOf(cameraTarget.playerVehicle.transform))
|
|
occluded = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Autofocus to target vehicle. Calculates distance and height of the camera related to bounds of the target vehicle.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public IEnumerator AutoFocus() {
|
|
|
|
float timer = 3f;
|
|
float bounds = RCC_GetBounds.MaxBoundsExtent(cameraTarget.playerVehicle.transform);
|
|
|
|
while (timer > 0f) {
|
|
|
|
timer -= Time.deltaTime;
|
|
TPSDistance = Mathf.Lerp(TPSDistance, bounds * 2.8f, Time.deltaTime * 2f);
|
|
TPSHeight = Mathf.Lerp(TPSHeight, bounds * .65f, Time.deltaTime * 2f);
|
|
yield return null;
|
|
|
|
}
|
|
|
|
TPSDistance = bounds * 2.8f;
|
|
TPSHeight = bounds * .65f;
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Autofocus to target vehicle. Calculates distance and height of the camera related to bounds of the target vehicle.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public IEnumerator AutoFocus(Transform transformBounds) {
|
|
|
|
float timer = 3f;
|
|
float bounds = RCC_GetBounds.MaxBoundsExtent(transformBounds);
|
|
|
|
while (timer > 0f) {
|
|
|
|
timer -= Time.deltaTime;
|
|
TPSDistance = Mathf.Lerp(TPSDistance, bounds * 2.8f, Time.deltaTime * 2f);
|
|
TPSHeight = Mathf.Lerp(TPSHeight, bounds * .65f, Time.deltaTime * 2f);
|
|
yield return null;
|
|
|
|
}
|
|
|
|
TPSDistance = bounds * 2.8f;
|
|
TPSHeight = bounds * .65f;
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Autofocus to target vehicle. Calculates distance and height of the camera related to bounds of the target vehicle.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public IEnumerator AutoFocus(Transform transformBounds1, Transform transformBounds2) {
|
|
|
|
float timer = 3f;
|
|
float bounds = (RCC_GetBounds.MaxBoundsExtent(transformBounds1) + RCC_GetBounds.MaxBoundsExtent(transformBounds2));
|
|
|
|
while (timer > 0f) {
|
|
|
|
timer -= Time.deltaTime;
|
|
TPSDistance = Mathf.Lerp(TPSDistance, bounds * 2.8f, Time.deltaTime * 2f);
|
|
TPSHeight = Mathf.Lerp(TPSHeight, bounds * .65f, Time.deltaTime * 2f);
|
|
yield return null;
|
|
|
|
}
|
|
|
|
TPSDistance = bounds * 2.8f;
|
|
TPSHeight = bounds * .65f;
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Autofocus to target vehicle. Calculates distance and height of the camera related to bounds of the target vehicle.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public IEnumerator AutoFocus(Transform transformBounds1, Transform transformBounds2, Transform transformBounds3) {
|
|
|
|
float timer = 3f;
|
|
float bounds = (RCC_GetBounds.MaxBoundsExtent(transformBounds1) + RCC_GetBounds.MaxBoundsExtent(transformBounds2) + RCC_GetBounds.MaxBoundsExtent(transformBounds3));
|
|
|
|
while (timer > 0f) {
|
|
|
|
timer -= Time.deltaTime;
|
|
TPSDistance = Mathf.Lerp(TPSDistance, bounds * 2.8f, Time.deltaTime * 2f);
|
|
TPSHeight = Mathf.Lerp(TPSHeight, bounds * .65f, Time.deltaTime * 2f);
|
|
yield return null;
|
|
|
|
}
|
|
|
|
TPSDistance = bounds * 2.8f;
|
|
TPSHeight = bounds * .65f;
|
|
|
|
}
|
|
|
|
private void OnDisable() {
|
|
|
|
RCC_CarControllerV3.OnRCCPlayerCollision -= RCC_CarControllerV3_OnRCCPlayerCollision;
|
|
|
|
// Listening input events.
|
|
RCC_InputManager.OnChangeCamera -= RCC_InputManager_OnChangeCamera;
|
|
RCC_InputManager.OnLookBack -= RCC_InputManager_OnLookBack;
|
|
|
|
}
|
|
|
|
private void Reset() {
|
|
|
|
// If pivot of the camera is not found, create it.
|
|
if(transform.Find("Pivot"))
|
|
pivot = transform.Find("Pivot").gameObject;
|
|
|
|
if (!pivot) {
|
|
|
|
pivot = new GameObject("Pivot");
|
|
|
|
pivot.transform.SetParent(transform);
|
|
pivot.transform.localPosition = Vector3.zero;
|
|
pivot.transform.localRotation = Quaternion.identity;
|
|
|
|
}
|
|
|
|
Camera foundCamera = GetComponentInChildren<Camera>();
|
|
|
|
if (foundCamera)
|
|
Destroy(foundCamera);
|
|
|
|
GameObject newCamera = new GameObject("Camera");
|
|
newCamera.transform.SetParent(pivot.transform);
|
|
newCamera.transform.localPosition = Vector3.zero;
|
|
newCamera.transform.localRotation = Quaternion.identity;
|
|
newCamera.AddComponent<Camera>();
|
|
newCamera.AddComponent<AudioListener>();
|
|
newCamera.gameObject.tag = "MainCamera";
|
|
|
|
}
|
|
|
|
} |