205 lines
7.2 KiB
C#
205 lines
7.2 KiB
C#
using UnityEngine;
|
|
|
|
namespace MegaKoop.Game
|
|
{
|
|
public class ThirdPersonCamera : MonoBehaviour
|
|
{
|
|
[Header("Target")]
|
|
[SerializeField] private Transform target;
|
|
[SerializeField] private Vector3 focusOffset = new Vector3(0f, 1.6f, 0f);
|
|
|
|
[Header("Orbit")]
|
|
[SerializeField] private float mouseSensitivity = 180f;
|
|
[SerializeField] private float minPitch = -35f;
|
|
[SerializeField] private float maxPitch = 75f;
|
|
[SerializeField] private float rotationSmoothTime = 0.1f;
|
|
|
|
[Header("Distance")]
|
|
[SerializeField] private float distance = 5f;
|
|
[SerializeField] private float minDistance = 2f;
|
|
[SerializeField] private float maxDistance = 8f;
|
|
[SerializeField] private float zoomSpeed = 4f;
|
|
[SerializeField] private float distanceSmoothTime = 0.1f;
|
|
|
|
[Header("Collision")]
|
|
[SerializeField] private float obstructionRadius = 0.25f;
|
|
[SerializeField] private LayerMask obstructionMask = ~0;
|
|
[SerializeField] private float obstructionBuffer = 0.1f;
|
|
|
|
[Header("Cursor")]
|
|
[SerializeField] private bool lockCursor = true;
|
|
|
|
private Vector2 orbitAngles = new Vector2(20f, 0f);
|
|
private Vector2 currentOrbitAngles;
|
|
private float pitchVelocity;
|
|
private float yawVelocity;
|
|
private float desiredDistance;
|
|
private float distanceVelocity;
|
|
private static readonly RaycastHit[] ObstructionHits = new RaycastHit[8];
|
|
|
|
private void OnEnable()
|
|
{
|
|
desiredDistance = Mathf.Clamp(distance, minDistance, maxDistance);
|
|
distance = desiredDistance;
|
|
currentOrbitAngles = orbitAngles;
|
|
pitchVelocity = yawVelocity = 0f;
|
|
|
|
if (lockCursor)
|
|
{
|
|
Cursor.lockState = CursorLockMode.Locked;
|
|
Cursor.visible = false;
|
|
}
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
if (lockCursor)
|
|
{
|
|
Cursor.lockState = CursorLockMode.None;
|
|
Cursor.visible = true;
|
|
}
|
|
}
|
|
|
|
private void LateUpdate()
|
|
{
|
|
if (target == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
float deltaTime = Time.deltaTime;
|
|
ReadOrbitInput(deltaTime);
|
|
ReadZoomInput();
|
|
|
|
if (rotationSmoothTime <= 0f)
|
|
{
|
|
currentOrbitAngles = orbitAngles;
|
|
pitchVelocity = yawVelocity = 0f;
|
|
}
|
|
else
|
|
{
|
|
currentOrbitAngles.x = Mathf.SmoothDamp(currentOrbitAngles.x, orbitAngles.x, ref pitchVelocity, rotationSmoothTime, Mathf.Infinity, deltaTime);
|
|
currentOrbitAngles.y = Mathf.SmoothDamp(currentOrbitAngles.y, orbitAngles.y, ref yawVelocity, rotationSmoothTime, Mathf.Infinity, deltaTime);
|
|
}
|
|
Quaternion lookRotation = Quaternion.Euler(currentOrbitAngles.x, currentOrbitAngles.y, 0f);
|
|
|
|
Vector3 focusPoint = target.TransformPoint(focusOffset);
|
|
float smoothedDistance;
|
|
if (distanceSmoothTime <= 0f)
|
|
{
|
|
distanceVelocity = 0f;
|
|
smoothedDistance = desiredDistance;
|
|
}
|
|
else
|
|
{
|
|
smoothedDistance = Mathf.SmoothDamp(distance, desiredDistance, ref distanceVelocity, distanceSmoothTime, Mathf.Infinity, deltaTime);
|
|
}
|
|
float unobstructedDistance = smoothedDistance;
|
|
float adjustedDistance = ResolveObstructions(focusPoint, lookRotation, unobstructedDistance);
|
|
float finalDistance = Mathf.Min(unobstructedDistance, adjustedDistance);
|
|
Vector3 finalPosition = focusPoint - lookRotation * Vector3.forward * finalDistance;
|
|
|
|
transform.SetPositionAndRotation(finalPosition, lookRotation);
|
|
distance = finalDistance;
|
|
}
|
|
|
|
private void ReadOrbitInput(float deltaTime)
|
|
{
|
|
float lookX = Input.GetAxis("Mouse X");
|
|
float lookY = Input.GetAxis("Mouse Y");
|
|
|
|
if (Mathf.Abs(lookX) > 0.0001f || Mathf.Abs(lookY) > 0.0001f)
|
|
{
|
|
orbitAngles.y += lookX * mouseSensitivity * deltaTime;
|
|
orbitAngles.x -= lookY * mouseSensitivity * deltaTime;
|
|
orbitAngles.x = Mathf.Clamp(orbitAngles.x, minPitch, maxPitch);
|
|
}
|
|
}
|
|
|
|
private void ReadZoomInput()
|
|
{
|
|
float scroll = Input.GetAxis("Mouse ScrollWheel");
|
|
if (Mathf.Abs(scroll) > 0.0001f)
|
|
{
|
|
desiredDistance = Mathf.Clamp(desiredDistance - scroll * zoomSpeed, minDistance, maxDistance);
|
|
}
|
|
else
|
|
{
|
|
desiredDistance = Mathf.Clamp(desiredDistance, minDistance, maxDistance);
|
|
}
|
|
}
|
|
|
|
private float ResolveObstructions(Vector3 focusPoint, Quaternion lookRotation, float targetDistance)
|
|
{
|
|
if (targetDistance <= 0.001f)
|
|
{
|
|
return targetDistance;
|
|
}
|
|
|
|
Vector3 direction = lookRotation * Vector3.back;
|
|
int hitCount = Physics.SphereCastNonAlloc(focusPoint, obstructionRadius, direction, ObstructionHits, targetDistance, obstructionMask, QueryTriggerInteraction.Ignore);
|
|
|
|
if (hitCount == 0)
|
|
{
|
|
return targetDistance;
|
|
}
|
|
|
|
float closestDistance = targetDistance;
|
|
|
|
for (int i = 0; i < hitCount; i++)
|
|
{
|
|
RaycastHit hit = ObstructionHits[i];
|
|
if (hit.collider == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Transform hitTransform = hit.collider.transform;
|
|
if (target != null && (hitTransform == target || hitTransform.IsChildOf(target)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (hit.distance < closestDistance)
|
|
{
|
|
closestDistance = Mathf.Max(0f, hit.distance - obstructionBuffer);
|
|
}
|
|
}
|
|
|
|
return closestDistance;
|
|
}
|
|
|
|
public void SetTarget(Transform newTarget)
|
|
{
|
|
target = newTarget;
|
|
}
|
|
|
|
private void OnValidate()
|
|
{
|
|
minPitch = Mathf.Clamp(minPitch, -89f, 89f);
|
|
maxPitch = Mathf.Clamp(maxPitch, -89f, 89f);
|
|
if (maxPitch < minPitch)
|
|
{
|
|
float temp = maxPitch;
|
|
maxPitch = minPitch;
|
|
minPitch = temp;
|
|
}
|
|
|
|
mouseSensitivity = Mathf.Max(0f, mouseSensitivity);
|
|
zoomSpeed = Mathf.Max(0f, zoomSpeed);
|
|
obstructionRadius = Mathf.Max(0f, obstructionRadius);
|
|
obstructionBuffer = Mathf.Clamp(obstructionBuffer, 0f, 1f);
|
|
rotationSmoothTime = Mathf.Max(0f, rotationSmoothTime);
|
|
distanceSmoothTime = Mathf.Max(0f, distanceSmoothTime);
|
|
|
|
minDistance = Mathf.Max(0.1f, minDistance);
|
|
maxDistance = Mathf.Max(minDistance, maxDistance);
|
|
distance = Mathf.Clamp(distance, minDistance, maxDistance);
|
|
desiredDistance = Mathf.Clamp(distance, minDistance, maxDistance);
|
|
|
|
currentOrbitAngles = orbitAngles;
|
|
pitchVelocity = yawVelocity = 0f;
|
|
}
|
|
}
|
|
}
|