Files
2026-07-09 21:33:33 +02:00

196 lines
6.8 KiB
C#

using Sirenix.OdinInspector;
using UnityEngine;
using VOID.Generic.Player.Input;
namespace VOID.Generic.Player
{
/// <summary>
/// Manager with full control of camera with movement by player.
/// </summary>
[DefaultExecutionOrder(-1)]
public class CameraManager : MonoBehaviour
{
public static CameraManager current {get; private set;}
// Camera Movement Settings
[Title("Camera Horizontal Movement")]
[SerializeField, MinValue(1f), MaxValue(10f), SuffixLabel("m/s")]
private float moveSpeed = 5f;
[SerializeField, MinValue(1f), MaxValue(5f)]
private float moveSpeedFastMultiplier = 2.5f;
// Camera's target elevating
[Title("Camera Vertical Movement")]
[SerializeField, MinValue(1f), MaxValue(20f), SuffixLabel("m/s")]
private float elevateSpeed = 1f;
// Camera Rotate and Tilt Settings
[Title("Camera Rotate and Tilt")]
[SerializeField, MinValue(0.01f), MaxValue(1f)]
private float rotateSensitivity = 0.1f;
[SerializeField, MinValue(0.01f), MaxValue(1f)]
private float tiltSensitivity = 0.06f;
[SerializeField, MinMaxSlider(10f, 90f, true)]
private Vector2 tiltLimit = new(20f, 85f);
// Camera Zoom Settings
[Title("Camera Zoom")]
[SerializeField, MinValue(0.05f), MaxValue(1f)]
private float zoomSensitivity = 0.3f;
[SerializeField, MinMaxSlider(1f, 30f, true)]
private Vector2 zoomLimit = new(2f, 15f);
[SerializeField]
private float zoomCurrent = 8f;
// Camera Offset
[Title("Camera Offset")]
[SerializeField, MinValue(0f), MaxValue(3f)]
private float targetVerticalOffset = 1.0f;
[SerializeField]
private Vector3 cameraOffset = new(0f, 0f, -1f);
// Camera and Target
private Camera _camera;
private GameObject _target;
// Stick properties
[Title("Sticking to transform")]
public Transform stickTo;
// Aditional Components
private CameraInputManager _cameraInputManager;
private void Awake()
{
current = this;
}
private void Start()
{
_cameraInputManager = CameraInputManager.current;
_camera = Camera.main;
_target = CameraTarget.InstantiateNew().gameObject;
}
private void FixedUpdate()
{
MoveHorizontally();
MoveVertically();
RotateAndTilt();
Zoom();
Offset();
CameraLookAtTarget();
}
/// <summary>
/// Moves camera's target to given position.
/// </summary>
public void MoveTo(Vector3 position)
{
_target.transform.position = position;
stickTo = null;
}
/// <summary>
/// Moving camera's target horizontally.
/// </summary>
private void MoveHorizontally()
{
// No camera movement from player, but it can be sticked to something
if (!_cameraInputManager.isMoving) {
if (stickTo) _target.transform.position = stickTo.position + new Vector3(0f, 1.5f, 0f);
return;
}
// Player moved camera - unstick camera
stickTo = null;
// Calculate camera's target next relative position
var moveVector = _cameraInputManager.movementDelta * (moveSpeed * Time.deltaTime);
if (_cameraInputManager.isMovingFaster) moveVector *= moveSpeedFastMultiplier;
var position = _target.transform.TransformDirection(moveVector.x, 0, moveVector.y);
_target.transform.position += position;
}
/// <summary>
/// Moving camera's target vertically.
/// </summary>
private void MoveVertically()
{
// IMPORTANT - target's pos already include offset!
var targetPos = _target.transform.position;
// Find point where camera's target can stick to
Physics.Raycast(
new Ray(targetPos + Vector3.up*50f, Vector3.down),
out var rayCastHit,
100f,
LayerManager.Static);
// Nothing - seems like camera floating and there is nothing above or below
if (!rayCastHit.collider) return;
// IMPORTANT - target include offset by default, but not raycast - we need add offset to raycast point!
var rayCastPos = rayCastHit.point + new Vector3(0, targetVerticalOffset, 0);
// If distance is positive we go UP, otherwise we go DOWN
var distance = rayCastPos.y - targetPos.y;
// Move camera's target towards new position, if really close then move remaining distance to prevent jiggling
var speed = Mathf.Sign(distance) * Mathf.Min(Mathf.Abs(distance), elevateSpeed * Time.fixedDeltaTime);
_target.transform.position += new Vector3(0f, speed, 0f);
}
/// <summary>
/// Rotating and tilting camera.<br/>
/// Rotating camera's target.
/// </summary>
private void RotateAndTilt()
{
if (_cameraInputManager.isRotatingAndTilting == false) return;
var delta = _cameraInputManager.rotateAndTiltDelta;
var tiltDelta = delta.y * tiltSensitivity * -1;
var rotateDelta = delta.x * rotateSensitivity;
var previousEuler = _camera.transform.rotation.eulerAngles;
var nextEuler = previousEuler + new Vector3(tiltDelta, rotateDelta, 0);
if (nextEuler.x < tiltLimit.x) nextEuler.x = tiltLimit.x;
if (nextEuler.x > tiltLimit.y) nextEuler.x = tiltLimit.y;
_camera.transform.rotation = Quaternion.Euler(nextEuler);
_target.transform.rotation = Quaternion.Euler(0f, nextEuler.y, 0f);
}
/// <summary>
/// Updating camera's zoom.
/// </summary>
private void Zoom()
{
if (_cameraInputManager.isZooming == false) return;
zoomCurrent -= _cameraInputManager.zoomDelta * zoomSensitivity;
if (zoomCurrent > zoomLimit.y) zoomCurrent = zoomLimit.y;
if (zoomCurrent < zoomLimit.x) zoomCurrent = zoomLimit.x;
}
/// <summary>
/// Updating camera's offset from target.
/// </summary>
private void Offset()
{
var offset = _camera.transform.TransformDirection(Vector3.back * zoomCurrent + cameraOffset);
_camera.transform.position = _target.transform.position + offset;
}
private void CameraLookAtTarget()
{
_camera.transform.LookAt(_target.transform);
}
}
}