using System.Collections.Generic; using System.Linq; using Unity.VisualScripting; using UnityEditor.Rendering; using UnityEngine; using UnityEngine.UIElements; using static Unity.Cinemachine.CinemachineTargetGroup; using static UnityEngine.GraphicsBuffer; public class CameraController : MonoBehaviour { [Header("Target")] [SerializeField] private Transform target; [SerializeField] private Vector3 targetOffset = new Vector3(0f, 1.5f, 0f); public Camera MainCamera; public Transform playerCamHome; [Header("Orbit")] [SerializeField] private float distance = 5f; [SerializeField] private float minDistance = 1f; [SerializeField] private float maxDistance = 8f; [SerializeField] private float rotationSpeed = 180f; [SerializeField] private float rotationOffset = 270f; [SerializeField] private float minPitch = -30f; [SerializeField] private float maxPitch = 75f; [Header("Zoom")] [SerializeField] private float zoomSpeed = 2f; [SerializeField] private float zoomSmoothness = 10f; [Header("Camera Collision")] [SerializeField] private LayerMask collisionLayers; [SerializeField] private float collisionRadius = 0.25f; [SerializeField] private float collisionOffset = 0.1f; [SerializeField] private float positionSmoothness = 15f; [Header("Occlusion Settings")] [SerializeField] private LayerMask obstructionMask; [SerializeField] private float fadedAlpha = 0.25f; [SerializeField] private float fadeSpeed = 5f; private Dictionary currentAlpha = new Dictionary(); private Dictionary targetAlpha = new Dictionary(); private float yaw; private float pitch = 20f; private float targetDistance; private Vector3 currentVelocity; public Vector3 Forward { get { Vector3 forward = MainCamera.transform.forward; forward.y = 0f; return forward.normalized; } } public Vector3 Right { get { Vector3 right = MainCamera.transform.right; right.y = 0f; return right.normalized; } } private void Start() { targetDistance = distance; Vector3 startingAngles = MainCamera.transform.eulerAngles; yaw = startingAngles.y + rotationOffset; pitch = startingAngles.x; if (pitch > 180f) { pitch -= 360f; } } private void LateUpdate() { if (target == null) { if (GameManager.Instance.PlayerController.DougBody != null) target = GameManager.Instance.PlayerController.DougBody.transform; else return; } UpdateOcclusion(); UpdateFade(); HandleRotation(); HandleZoom(); UpdateCameraPosition(); } private void HandleRotation() { if (!Input.GetMouseButton(1)) { UnityEngine.Cursor.visible = true; UnityEngine.Cursor.lockState = CursorLockMode.None; return; } UnityEngine.Cursor.visible = false; UnityEngine.Cursor.lockState = CursorLockMode.Locked; yaw += Input.GetAxis("Mouse X") * rotationSpeed * Time.deltaTime; pitch -= Input.GetAxis("Mouse Y") * rotationSpeed * Time.deltaTime; pitch = Mathf.Clamp(pitch, minPitch, maxPitch); } private void HandleZoom() { if (!Input.GetMouseButton(1)) { return; } float scrollInput = Input.GetAxis("Mouse ScrollWheel"); targetDistance -= scrollInput * zoomSpeed; targetDistance = Mathf.Clamp(targetDistance, minDistance, maxDistance); distance = Mathf.Lerp(distance, targetDistance, zoomSmoothness * Time.deltaTime); } private void UpdateCameraPosition() { Vector3 targetPosition = target.position + targetOffset; Quaternion orbitRotation = Quaternion.Euler(pitch, yaw, 0f); Vector3 desiredDirection = orbitRotation * Vector3.back; Vector3 desiredPosition = targetPosition + desiredDirection * distance; float correctedDistance = distance; if (Physics.SphereCast(targetPosition, collisionRadius, desiredDirection, out RaycastHit hit, distance, collisionLayers, QueryTriggerInteraction.Ignore)) { correctedDistance = Mathf.Max(minDistance, hit.distance - collisionOffset); } Vector3 finalPosition = targetPosition + desiredDirection * correctedDistance; MainCamera.transform.position = Vector3.SmoothDamp(MainCamera.transform.position, finalPosition, ref currentVelocity, 1f / positionSmoothness); MainCamera.transform.rotation = Quaternion.LookRotation(targetPosition - MainCamera.transform.position, Vector3.up); } void UpdateOcclusion() { // Reset all renderers to visible foreach (Renderer r in new List(targetAlpha.Keys)) { targetAlpha[r] = 1f; } Vector3 dir = MainCamera.transform.position - target.position; float dist = dir.magnitude; RaycastHit[] hits = Physics.RaycastAll( target.position, dir.normalized, dist, obstructionMask, QueryTriggerInteraction.Ignore); foreach (RaycastHit hit in hits) { Renderer r = hit.collider.GetComponent(); if (r == null) continue; if (!currentAlpha.ContainsKey(r)) { currentAlpha[r] = 1f; targetAlpha[r] = 1f; } targetAlpha[r] = fadedAlpha; } } void UpdateFade() { List renderers = new List(currentAlpha.Keys); foreach (Renderer r in renderers) { float current = currentAlpha[r]; float target = targetAlpha[r]; current = Mathf.Lerp( current, target, Time.deltaTime * fadeSpeed); currentAlpha[r] = current; foreach (Material mat in r.materials) { Color c = mat.color; c.a = current; if (Mathf.Abs(current - targetAlpha[r]) < 0.05f) { c.a = targetAlpha[r]; } mat.color = c; } // Cleanup once fully restored if (Mathf.Abs(current - 1f) < 0.01f && Mathf.Abs(target - 1f) < 0.01f) { currentAlpha.Remove(r); targetAlpha.Remove(r); } } } } public enum CameraControlMode { SnapRotate, FreeRotate }