Files
2026-03-04 10:03:45 +08:00

248 lines
5.2 KiB
C#

using System;
using UnityEngine;
namespace RootMotion
{
public class CameraController : MonoBehaviour
{
[Serializable]
public enum UpdateMode
{
Update = 0,
FixedUpdate = 1,
LateUpdate = 2,
FixedLateUpdate = 3
}
public Transform target;
public Transform rotationSpace;
public UpdateMode updateMode = UpdateMode.LateUpdate;
public bool lockCursor = true;
[Header("Position")]
public bool smoothFollow;
public Vector3 offset = new Vector3(0f, 1.5f, 0.5f);
public float followSpeed = 10f;
[Header("Rotation")]
public float rotationSensitivity = 3.5f;
public float yMinLimit = -20f;
public float yMaxLimit = 80f;
public bool rotateAlways = true;
public bool rotateOnLeftButton;
public bool rotateOnRightButton;
public bool rotateOnMiddleButton;
[Header("Distance")]
public float distance = 10f;
public float minDistance = 4f;
public float maxDistance = 10f;
public float zoomSpeed = 10f;
public float zoomSensitivity = 1f;
[Header("Blocking")]
public LayerMask blockingLayers;
public float blockingRadius = 1f;
public float blockingSmoothTime = 0.1f;
[Range(0f, 1f)]
public float blockedOffset = 0.5f;
private Vector3 targetDistance;
private Vector3 position;
private Quaternion rotation = Quaternion.identity;
private Vector3 smoothPosition;
private Camera cam;
private bool fixedFrame;
private float fixedDeltaTime;
private Quaternion r = Quaternion.identity;
private Vector3 lastUp;
private float blockedDistance = 10f;
private float blockedDistanceV;
public float x { get; private set; }
public float y { get; private set; }
public float distanceTarget { get; private set; }
private float zoomAdd
{
get
{
float axis = Input.GetAxis("Mouse ScrollWheel");
if (axis > 0f)
{
return 0f - zoomSensitivity;
}
if (axis < 0f)
{
return zoomSensitivity;
}
return 0f;
}
}
public void SetAngles(Quaternion rotation)
{
Vector3 eulerAngles = rotation.eulerAngles;
x = eulerAngles.y;
y = eulerAngles.x;
}
public void SetAngles(float yaw, float pitch)
{
x = yaw;
y = pitch;
}
protected virtual void Awake()
{
Vector3 eulerAngles = base.transform.eulerAngles;
x = eulerAngles.y;
y = eulerAngles.x;
distanceTarget = distance;
smoothPosition = base.transform.position;
cam = GetComponent<Camera>();
lastUp = ((rotationSpace != null) ? rotationSpace.up : Vector3.up);
}
protected virtual void Update()
{
if (updateMode == UpdateMode.Update)
{
UpdateTransform();
}
}
protected virtual void FixedUpdate()
{
fixedFrame = true;
fixedDeltaTime += Time.deltaTime;
if (updateMode == UpdateMode.FixedUpdate)
{
UpdateTransform();
}
}
protected virtual void LateUpdate()
{
UpdateInput();
if (updateMode == UpdateMode.LateUpdate)
{
UpdateTransform();
}
if (updateMode == UpdateMode.FixedLateUpdate && fixedFrame)
{
UpdateTransform(fixedDeltaTime);
fixedDeltaTime = 0f;
fixedFrame = false;
}
}
public void UpdateInput()
{
if (cam.enabled)
{
Cursor.lockState = (lockCursor ? CursorLockMode.Locked : CursorLockMode.None);
Cursor.visible = !lockCursor;
if (rotateAlways || (rotateOnLeftButton && Input.GetMouseButton(0)) || (rotateOnRightButton && Input.GetMouseButton(1)) || (rotateOnMiddleButton && Input.GetMouseButton(2)))
{
x += Input.GetAxis("Mouse X") * rotationSensitivity;
y = ClampAngle(y - Input.GetAxis("Mouse Y") * rotationSensitivity, yMinLimit, yMaxLimit);
}
distanceTarget = Mathf.Clamp(distanceTarget + zoomAdd, minDistance, maxDistance);
}
}
public void UpdateTransform()
{
UpdateTransform(Time.deltaTime);
}
public void UpdateTransform(float deltaTime)
{
if (!cam.enabled)
{
return;
}
rotation = Quaternion.AngleAxis(x, Vector3.up) * Quaternion.AngleAxis(y, Vector3.right);
if (rotationSpace != null)
{
r = Quaternion.FromToRotation(lastUp, rotationSpace.up) * r;
rotation = r * rotation;
lastUp = rotationSpace.up;
}
if (target != null)
{
distance += (distanceTarget - distance) * zoomSpeed * deltaTime;
if (!smoothFollow)
{
smoothPosition = target.position;
}
else
{
smoothPosition = Vector3.Lerp(smoothPosition, target.position, deltaTime * followSpeed);
}
Vector3 vector = smoothPosition + rotation * offset;
Vector3 vector2 = rotation * -Vector3.forward;
if ((int)blockingLayers != -1)
{
if (Physics.SphereCast(vector, blockingRadius, vector2, out var hitInfo, distanceTarget - blockingRadius, blockingLayers))
{
blockedDistance = Mathf.SmoothDamp(blockedDistance, hitInfo.distance + blockingRadius * (1f - blockedOffset), ref blockedDistanceV, blockingSmoothTime);
}
else
{
blockedDistance = distanceTarget;
}
distance = Mathf.Min(distance, blockedDistance);
}
position = vector + vector2 * distance;
base.transform.position = position;
}
base.transform.rotation = rotation;
}
private float ClampAngle(float angle, float min, float max)
{
if (angle < -360f)
{
angle += 360f;
}
if (angle > 360f)
{
angle -= 360f;
}
return Mathf.Clamp(angle, min, max);
}
}
}