You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
209 lines
6.7 KiB
C#
209 lines
6.7 KiB
C#
6 months ago
|
using UnityEngine;
|
||
|
using System.Collections;
|
||
|
|
||
|
namespace RootMotion {
|
||
|
|
||
|
/// <summary>
|
||
|
/// 3rd person camera controller.
|
||
|
/// </summary>
|
||
|
public class CameraController : MonoBehaviour {
|
||
|
|
||
|
// When to update the camera?
|
||
|
[System.Serializable]
|
||
|
public enum UpdateMode {
|
||
|
Update,
|
||
|
FixedUpdate,
|
||
|
LateUpdate,
|
||
|
FixedLateUpdate
|
||
|
}
|
||
|
|
||
|
public Transform target; // The target Transform to follow
|
||
|
public Transform rotationSpace; // If assigned, will use this Transform's rotation as the rotation space instead of the world space. Useful with spherical planets.
|
||
|
public UpdateMode updateMode = UpdateMode.LateUpdate; // When to update the camera?
|
||
|
public bool lockCursor = true; // If true, the mouse will be locked to screen center and hidden
|
||
|
|
||
|
[Header("Position")]
|
||
|
public bool smoothFollow; // If > 0, camera will smoothly interpolate towards the target
|
||
|
public Vector3 offset = new Vector3(0, 1.5f, 0.5f); // The offset from target relative to camera rotation
|
||
|
public float followSpeed = 10f; // Smooth follow speed
|
||
|
|
||
|
[Header("Rotation")]
|
||
|
public float rotationSensitivity = 3.5f; // The sensitivity of rotation
|
||
|
public float yMinLimit = -20; // Min vertical angle
|
||
|
public float yMaxLimit = 80; // Max vertical angle
|
||
|
public bool rotateAlways = true; // Always rotate to mouse?
|
||
|
public bool rotateOnLeftButton; // Rotate to mouse when left button is pressed?
|
||
|
public bool rotateOnRightButton; // Rotate to mouse when right button is pressed?
|
||
|
public bool rotateOnMiddleButton; // Rotate to mouse when middle button is pressed?
|
||
|
|
||
|
[Header("Distance")]
|
||
|
public float distance = 10.0f; // The current distance to target
|
||
|
public float minDistance = 4; // The minimum distance to target
|
||
|
public float maxDistance = 10; // The maximum distance to target
|
||
|
public float zoomSpeed = 10f; // The speed of interpolating the distance
|
||
|
public float zoomSensitivity = 1f; // The sensitivity of mouse zoom
|
||
|
|
||
|
[Header("Blocking")]
|
||
|
public LayerMask blockingLayers;
|
||
|
public float blockingRadius = 1f;
|
||
|
public float blockingSmoothTime = 0.1f;
|
||
|
public float blockingOriginOffset;
|
||
|
[Range(0f, 1f)] public float blockedOffset = 0.5f;
|
||
|
|
||
|
public float x { get; private set; } // The current x rotation of the camera
|
||
|
public float y { get; private set; } // The current y rotation of the camera
|
||
|
public float distanceTarget { get; private set; } // Get/set distance
|
||
|
|
||
|
private Vector3 targetDistance, 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, blockedDistanceV;
|
||
|
|
||
|
public void SetAngles(Quaternion rotation)
|
||
|
{
|
||
|
Vector3 euler = rotation.eulerAngles;
|
||
|
this.x = euler.y;
|
||
|
this.y = euler.x;
|
||
|
}
|
||
|
|
||
|
public void SetAngles(float yaw, float pitch)
|
||
|
{
|
||
|
this.x = yaw;
|
||
|
this.y = pitch;
|
||
|
}
|
||
|
|
||
|
// Initiate, set the params to the current transformation of the camera relative to the target
|
||
|
protected virtual void Awake () {
|
||
|
Vector3 angles = transform.eulerAngles;
|
||
|
x = angles.y;
|
||
|
y = angles.x;
|
||
|
|
||
|
distanceTarget = distance;
|
||
|
smoothPosition = 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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Read the user input
|
||
|
public void UpdateInput() {
|
||
|
if (!cam.enabled) return;
|
||
|
|
||
|
// Cursors
|
||
|
Cursor.lockState = lockCursor? CursorLockMode.Locked: CursorLockMode.None;
|
||
|
Cursor.visible = lockCursor? false: true;
|
||
|
|
||
|
// Should we rotate the camera?
|
||
|
bool rotate = rotateAlways || (rotateOnLeftButton && Input.GetMouseButton(0)) || (rotateOnRightButton && Input.GetMouseButton(1)) || (rotateOnMiddleButton && Input.GetMouseButton(2));
|
||
|
|
||
|
// delta rotation
|
||
|
if (rotate) {
|
||
|
x += Input.GetAxis("Mouse X") * rotationSensitivity;
|
||
|
y = ClampAngle(y - Input.GetAxis("Mouse Y") * rotationSensitivity, yMinLimit, yMaxLimit);
|
||
|
}
|
||
|
|
||
|
// Distance
|
||
|
distanceTarget = Mathf.Clamp(distanceTarget + zoomAdd, minDistance, maxDistance);
|
||
|
}
|
||
|
|
||
|
// Update the camera transform
|
||
|
public void UpdateTransform() {
|
||
|
UpdateTransform(Time.deltaTime);
|
||
|
}
|
||
|
|
||
|
public void UpdateTransform(float deltaTime) {
|
||
|
if (!cam.enabled) return;
|
||
|
|
||
|
// Rotation
|
||
|
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
|
||
|
distance += (distanceTarget - distance) * zoomSpeed * deltaTime;
|
||
|
|
||
|
// Smooth follow
|
||
|
if (!smoothFollow) smoothPosition = target.position;
|
||
|
else smoothPosition = Vector3.Lerp(smoothPosition, target.position, deltaTime * followSpeed);
|
||
|
|
||
|
// Position
|
||
|
Vector3 t = smoothPosition + rotation * offset;
|
||
|
Vector3 f = rotation * -Vector3.forward;
|
||
|
|
||
|
if (blockingLayers != -1)
|
||
|
{
|
||
|
RaycastHit hit;
|
||
|
if (Physics.SphereCast(t - f * blockingOriginOffset, blockingRadius, f, out hit, blockingOriginOffset + distanceTarget - blockingRadius, blockingLayers))
|
||
|
{
|
||
|
blockedDistance = Mathf.SmoothDamp(blockedDistance, hit.distance + blockingRadius * (1f - blockedOffset) - blockingOriginOffset, ref blockedDistanceV, blockingSmoothTime);
|
||
|
}
|
||
|
else blockedDistance = distanceTarget;
|
||
|
|
||
|
distance = Mathf.Min(distance, blockedDistance);
|
||
|
}
|
||
|
|
||
|
position = t + f * distance;
|
||
|
|
||
|
// Translating the camera
|
||
|
transform.position = position;
|
||
|
}
|
||
|
|
||
|
transform.rotation = rotation;
|
||
|
}
|
||
|
|
||
|
// Zoom input
|
||
|
private float zoomAdd {
|
||
|
get {
|
||
|
float scrollAxis = Input.GetAxis("Mouse ScrollWheel");
|
||
|
if (scrollAxis > 0) return -zoomSensitivity;
|
||
|
if (scrollAxis < 0) return zoomSensitivity;
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Clamping Euler angles
|
||
|
private float ClampAngle (float angle, float min, float max) {
|
||
|
if (angle < -360) angle += 360;
|
||
|
if (angle > 360) angle -= 360;
|
||
|
return Mathf.Clamp (angle, min, max);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|