using System; using System.Collections; using System.Collections.Generic; using Unity.Cinemachine; using Unity.VisualScripting; using UnityEngine; using UnityEngine.EventSystems; public class CinemachineCameraController : MonoBehaviour { public static CinemachineCameraController Instance { get; private set; } public CinemachineCamera actionCam; public GameObject groundPlane; [Header("Camera Controls")] public float minZoom = 15f; public float maxZoom = 200f; public float zoomSpeed = 10f; public float panSpeed = 30f; public float cameraAngle = 60f; // degrees private Vector3 framingPosition; private float framingHeight; private float framingDistance; private float zoomLevel; private Coroutine cameraMoveCoroutine; private Vector3 panOffset = Vector3.zero; private float yaw = 0f; // Add this field at the top with your other private fields private void Awake() { Instance = this; } private void Start() { FrameGroundPlane(); } private void Update() { HandleCameraInput(); } private void HandleCameraInput() { Rect gameArea = new Rect(0, 0, Screen.width, Screen.height); // Zoom if (Application.isFocused && !EventSystem.current.IsPointerOverGameObject() && gameArea.Contains(Input.mousePosition)) { float scroll = Input.GetAxisRaw("Mouse ScrollWheel"); if (Mathf.Abs(scroll) > 0.01f) { zoomLevel -= scroll * zoomSpeed; zoomLevel = Mathf.Clamp(zoomLevel, minZoom, maxZoom); FrameGroundPlane(); } } // Pan (WASD or Arrow keys) - works even when paused Vector3 move = Vector3.zero; if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow)) move.x -= 1f; if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow)) move.x += 1f; if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow)) move.z += 1f; if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow)) move.z -= 1f; if (move != Vector3.zero) { panOffset += move.normalized * panSpeed * Time.unscaledDeltaTime; FrameGroundPlane(); } // Rotate with middle mouse button if (Input.GetMouseButton(2)) { float mouseX = Input.GetAxisRaw("Mouse X"); yaw += mouseX * 3f; FrameGroundPlane(); } } private void FrameGroundPlane() { if (groundPlane == null) { Debug.LogError("Ground plane is not assigned in CinemachineCameraController."); return; } // Use collider bounds to frame the ground plane Collider col = groundPlane.GetComponent(); if (col == null) { Debug.LogError("Ground plane does not have a collider to frame."); return; } Bounds bounds = col.bounds; Vector3 center = bounds.center + panOffset; float size = Mathf.Max(bounds.size.x, bounds.size.z); if (zoomLevel < 0.01f) { zoomLevel = size + 5f; // Ensure zoom level is not negative } float angleRadians = cameraAngle * Mathf.Deg2Rad; float y = Mathf.Sin(angleRadians) * zoomLevel; float z = -Mathf.Cos(angleRadians) * zoomLevel; Quaternion rotation = Quaternion.Euler(0, yaw, 0); // Apply yaw rotation Vector3 offset = rotation * new Vector3(0, y, z); framingPosition = center + offset; actionCam.transform.position = framingPosition; actionCam.transform.LookAt(center); actionCam.Follow = null; actionCam.LookAt = null; framingHeight = y; framingDistance = Mathf.Abs(z); } public void FocusOnCharacter(Transform character) { actionCam.Follow = character; actionCam.LookAt = character; } public void FollowTargetingLine(Vector3 mouseWorldPosition) { Vector3 target = new Vector3(mouseWorldPosition.x, 0, mouseWorldPosition.z) + panOffset; float angleRadians = cameraAngle * Mathf.Deg2Rad; float y = Mathf.Sin(angleRadians) * zoomLevel; float z = -Mathf.Cos(angleRadians) * zoomLevel; Vector3 camPos = target + new Vector3(0, y, z); actionCam.transform.position = camPos; actionCam.transform.LookAt(target); actionCam.Follow = null; actionCam.LookAt = null; } public void FrameAllCharacters(List characters) { if (characters == null || characters.Count == 0) return; Bounds bounds = new Bounds(characters[0].transform.position, Vector3.zero); foreach (var go in characters) bounds.Encapsulate(go.transform.position); Vector3 center = bounds.center + panOffset; float size = Mathf.Max(bounds.size.x, bounds.size.z); // Keep current zoomLevel, but allow zoom out if needed to fit all float requiredZoom = size + 5f; if (zoomLevel < requiredZoom) { zoomLevel = requiredZoom; } float angleRadians = cameraAngle * Mathf.Deg2Rad; float y = Mathf.Sin(angleRadians) * zoomLevel; float z = -Mathf.Cos(angleRadians) * zoomLevel; Vector3 camPos = center + new Vector3(0, y, z); // Start smooth movement if (cameraMoveCoroutine != null) StopCoroutine(cameraMoveCoroutine); cameraMoveCoroutine = StartCoroutine(SmoothMoveCamera(camPos, center, 0.7f)); } private IEnumerator SmoothMoveCamera(Vector3 targetPosition, Vector3 lookAtTarget, float duration) { Vector3 startPos = actionCam.transform.position; Quaternion startRot = actionCam.transform.rotation; Quaternion endRot = Quaternion.LookRotation(lookAtTarget - targetPosition, Vector3.up); float elapsed = 0f; while (elapsed < duration) { float t = elapsed / duration; actionCam.transform.position = Vector3.Lerp(startPos, targetPosition, t); actionCam.transform.rotation = Quaternion.Slerp(startRot, endRot, t); elapsed += Time.deltaTime; yield return null; } actionCam.transform.position = targetPosition; actionCam.transform.rotation = endRot; actionCam.Follow = null; actionCam.LookAt = null; } }