using UnityEngine; using UnityEngine.InputSystem; public class CameraController : MonoBehaviour { [Header("Target Tracking")] [SerializeField] private Transform target; [SerializeField] private Vector3 targetOffset = new Vector3(0, 1.5f, 0); [Header("Orbit & Distance Settings")] [SerializeField] private float distance = 12.0f; [SerializeField] private float minDistance = 5.0f; [SerializeField] private float maxDistance = 25.0f; [Header("Rotation Limits & Speed")] [SerializeField] private float rotationSpeed = 60.0f; // degrees per second for Q/E yaw [SerializeField] private float tiltSpeed = 45.0f; // degrees per second for W/S pitch [SerializeField] private float minPitch = 15.0f; // Minimum angle looking up/down [SerializeField] private float maxPitch = 80.0f; // Maximum top-down angle [Header("Smooth Movement")] [SerializeField] private float smoothSpeed = 10.0f; private float currentYaw = 0.0f; // Horizontal rotation angle around target private float currentPitch = 45.0f; // Vertical tilt angle private void Start() { // Auto-find player target if not assigned if (target == null) { GameObject player = GameObject.FindWithTag("Player"); if (player != null) { target = player.transform; } else { PlayerController pc = FindFirstObjectByType(); if (pc != null) target = pc.transform; } } // Initialize angles from starting rotation Vector3 angles = transform.eulerAngles; currentYaw = angles.y; currentPitch = angles.x; } private void LateUpdate() { if (target == null) return; HandleCameraInput(); UpdateCameraPosition(); } private void HandleCameraInput() { Keyboard currentKeyboard = Keyboard.current; if (currentKeyboard == null) return; // Q and E: Rotate Yaw around Player if (currentKeyboard.qKey.isPressed) { currentYaw -= rotationSpeed * Time.deltaTime; } if (currentKeyboard.eKey.isPressed) { currentYaw += rotationSpeed * Time.deltaTime; } // W and S: Tilt Pitch up and down if (currentKeyboard.wKey.isPressed) { currentPitch += tiltSpeed * Time.deltaTime; } if (currentKeyboard.sKey.isPressed) { currentPitch -= tiltSpeed * Time.deltaTime; } // Clamp vertical tilt so camera doesn't flip or go beneath ground currentPitch = Mathf.Clamp(currentPitch, minPitch, maxPitch); // Optional Zoom via Mouse Scroll Mouse currentMouse = Mouse.current; if (currentMouse != null) { float scroll = currentMouse.scroll.ReadValue().y; if (Mathf.Abs(scroll) > 0.01f) { distance -= Mathf.Sign(scroll) * 1.5f; distance = Mathf.Clamp(distance, minDistance, maxDistance); } } } [Header("Obstacle Collision Avoidance")] [SerializeField] private bool avoidCollisions = true; [SerializeField] private LayerMask collisionLayers = ~0; // Default to all layers [SerializeField] private float cameraRadius = 0.3f; // Radius for SphereCast check [SerializeField] private float minCollisionDistance = 1.5f; private void UpdateCameraPosition() { // Calculate focus point on player Vector3 focusPoint = target.position + targetOffset; // Calculate orbit rotation & offset vector Quaternion rotation = Quaternion.Euler(currentPitch, currentYaw, 0); Vector3 desiredCameraDir = (rotation * Vector3.back).normalized; Vector3 targetCameraPos = focusPoint + (desiredCameraDir * distance); float adjustedDistance = distance; // Check for obstacle collision along the line from focusPoint to desired camera position if (avoidCollisions) { if (Physics.SphereCast(focusPoint, cameraRadius, desiredCameraDir, out RaycastHit hit, distance, collisionLayers)) { // Move camera in front of the hit point adjustedDistance = Mathf.Max(hit.distance - 0.1f, minCollisionDistance); targetCameraPos = focusPoint + (desiredCameraDir * adjustedDistance); } } // Smoothly interpolate camera position and rotation transform.position = Vector3.Lerp(transform.position, targetCameraPos, smoothSpeed * Time.deltaTime); transform.rotation = Quaternion.Slerp(transform.rotation, rotation, smoothSpeed * Time.deltaTime); } private void OnDrawGizmosSelected() { if (target != null) { Gizmos.color = Color.cyan; Gizmos.DrawWireSphere(target.position + targetOffset, 0.5f); Gizmos.DrawLine(transform.position, target.position + targetOffset); } } }