Characters
This commit is contained in:
204
Game/Scripts/ThirdPersonCamera.cs
Normal file
204
Game/Scripts/ThirdPersonCamera.cs
Normal file
@@ -0,0 +1,204 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user