Untitled
unknown
csharp
8 months ago
6.5 kB
1
Indexable
Never
public class CharacterCamera : MonoBehaviour { [Header("Input")] public bool handleInput = true; [ShowIf(nameof(handleInput))] public InputActionReference lookAction; [ShowIf(nameof(handleInput))] public InputActionReference zoomAction; [Header("Framing")] public Transform followTransform; public Vector2 followPointFraming = new Vector2(0f, 0f); public float followSharpness = 100f; [Header("Distance")] public float defaultDistance = 2f; public float minDistance = 1f; public float maxDistance = 4f; public float distanceSpeed = 0.01f; public float distanceSharpness = 10f; [Header("Rotation")] public bool invertX = false; public bool invertY = false; [Range(-90f, 90f)] public float defaultVerticalAngle = 20f; [Range(-90f, 90f)] public float minVerticalAngle = -70f; [Range(-90f, 90f)] public float maxVerticalAngle = 90f; public float xSensitivity = 30f; public float ySensitivity = 20f; public float rotationSharpness = 100f; [Header("Collision")] public float collisionRadius = 0.2f; public LayerMask collisionLayers = -1; public float collisionSharpness = 10000f; public List<Collider> ignoredColliders = new List<Collider>(); private bool distanceIsObstructed; private float currentDistance; private float targetDistance; private float targetVerticalAngle; private int collisionCount; private RaycastHit[] collisions = new RaycastHit[maxObstructions]; private Vector3 currentFollowPosition; private Vector3 planarDirection; private const int maxObstructions = 32; public Vector3 InputRotation { get; set; } public float ZoomInput { get; set; } public void SetTarget(Transform target) { followTransform = target; var coll = followTransform.GetComponentInParent<Collider>(); if (!ignoredColliders.Contains(coll)) ignoredColliders.Add(coll); currentDistance = defaultDistance; targetDistance = currentDistance; targetVerticalAngle = 0f; planarDirection = followTransform.forward; currentFollowPosition = followTransform.position; } private void Update() { if (!handleInput) return; var lookInput = lookAction.action.ReadValue<Vector2>(); var scrollInput = zoomAction.action.ReadValue<float>(); InputRotation = lookInput; ZoomInput = -scrollInput; } private void LateUpdate() { if (!followTransform) return; var targetRotation = HandleRotation(); transform.rotation = targetRotation; HandleDistance(); HandleCollisions(); // Find the smoothed camera orbit position var targetPosition = currentFollowPosition - ((targetRotation * Vector3.forward) * currentDistance); // Handle framing targetPosition += transform.right * followPointFraming.x; targetPosition += transform.up * followPointFraming.y; // Apply position transform.position = targetPosition; } private Quaternion HandleRotation() { var inputX = InputRotation.x; var inputY = InputRotation.y; if (invertX) inputX *= -1f; if (invertY) inputY *= -1f; // Process rotation input var rotationFromInput = Quaternion.Euler(followTransform.up * (inputX * xSensitivity * Time.deltaTime)); planarDirection = rotationFromInput * planarDirection; planarDirection = Vector3.Cross(followTransform.up, Vector3.Cross(planarDirection, followTransform.up)); var planarRot = Quaternion.LookRotation(planarDirection, followTransform.up); targetVerticalAngle -= (inputY * ySensitivity * Time.deltaTime); targetVerticalAngle = Mathf.Clamp(targetVerticalAngle, minVerticalAngle, maxVerticalAngle); var verticalRot = Quaternion.Euler(targetVerticalAngle, 0, 0); var targetRotation = Quaternion.Slerp(transform.rotation, planarRot * verticalRot, 1f - Mathf.Exp(-rotationSharpness * Time.deltaTime)); return targetRotation; } private void HandleDistance() { if (distanceIsObstructed && Mathf.Abs(ZoomInput) > 0f) { targetDistance = currentDistance; } targetDistance += ZoomInput * distanceSpeed; targetDistance = Mathf.Clamp(targetDistance, minDistance, maxDistance); // Find the smoothed follow position currentFollowPosition = Vector3.Lerp(currentFollowPosition, followTransform.position, 1f - Mathf.Exp(-followSharpness * Time.deltaTime)); } private void HandleCollisions() { var closestHit = new RaycastHit(); closestHit.distance = Mathf.Infinity; collisionCount = Physics.SphereCastNonAlloc(currentFollowPosition, collisionRadius, -transform.forward, collisions, targetDistance, collisionLayers, QueryTriggerInteraction.Ignore); for (int i = 0; i < collisionCount; i++) { bool isIgnored = false; for (int j = 0; j < ignoredColliders.Count; j++) { if (ignoredColliders[j] == collisions[i].collider) { isIgnored = true; break; } } for (int j = 0; j < ignoredColliders.Count; j++) { if (ignoredColliders[j] == collisions[i].collider) { isIgnored = true; break; } } if (!isIgnored && collisions[i].distance < closestHit.distance && collisions[i].distance > 0) closestHit = collisions[i]; } // If obstructions detected if (closestHit.distance < Mathf.Infinity) { distanceIsObstructed = true; currentDistance = Mathf.Lerp(currentDistance, closestHit.distance, 1 - Mathf.Exp(-collisionSharpness * Time.deltaTime)); } else { distanceIsObstructed = false; currentDistance = Mathf.Lerp(currentDistance, targetDistance, 1 - Mathf.Exp(-distanceSharpness * Time.deltaTime)); } } }
Leave a Comment