CinemachineCameraController.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using Unity.Cinemachine;
  5. using Unity.VisualScripting;
  6. using UnityEngine;
  7. using UnityEngine.EventSystems;
  8. public class CinemachineCameraController : MonoBehaviour
  9. {
  10. public static CinemachineCameraController Instance { get; private set; }
  11. public CinemachineCamera actionCam;
  12. public GameObject groundPlane;
  13. [Header("Camera Controls")]
  14. public float minZoom = 15f;
  15. public float maxZoom = 80f;
  16. public float zoomSpeed = 10f;
  17. public float panSpeed = 30f;
  18. public float cameraAngle = 60f; // degrees
  19. private Vector3 framingPosition;
  20. private float framingHeight;
  21. private float framingDistance;
  22. private float zoomLevel;
  23. private Coroutine cameraMoveCoroutine;
  24. private Vector3 panOffset = Vector3.zero;
  25. private float yaw = 0f; // Add this field at the top with your other private fields
  26. private void Awake()
  27. {
  28. Instance = this;
  29. }
  30. private void Start()
  31. {
  32. FrameGroundPlane();
  33. }
  34. private void Update()
  35. {
  36. HandleCameraInput();
  37. }
  38. private void HandleCameraInput()
  39. {
  40. Rect gameArea = new Rect(0, 0, Screen.width, Screen.height);
  41. // Zoom
  42. if (Application.isFocused && !EventSystem.current.IsPointerOverGameObject() && gameArea.Contains(Input.mousePosition))
  43. {
  44. float scroll = Input.GetAxisRaw("Mouse ScrollWheel");
  45. if (Mathf.Abs(scroll) > 0.01f)
  46. {
  47. zoomLevel -= scroll * zoomSpeed;
  48. zoomLevel = Mathf.Clamp(zoomLevel, minZoom, maxZoom);
  49. FrameGroundPlane();
  50. }
  51. }
  52. // Pan (WASD or Arrow keys) - works even when paused
  53. Vector3 move = Vector3.zero;
  54. if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow))
  55. move.x -= 1f;
  56. if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow))
  57. move.x += 1f;
  58. if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow))
  59. move.z += 1f;
  60. if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow))
  61. move.z -= 1f;
  62. if (move != Vector3.zero)
  63. {
  64. panOffset += move.normalized * panSpeed * Time.unscaledDeltaTime;
  65. FrameGroundPlane();
  66. }
  67. // Rotate with middle mouse button
  68. if (Input.GetMouseButton(2))
  69. {
  70. float mouseX = Input.GetAxisRaw("Mouse X");
  71. yaw += mouseX * 3f;
  72. FrameGroundPlane();
  73. }
  74. }
  75. private void FrameGroundPlane()
  76. {
  77. if (groundPlane == null)
  78. {
  79. Debug.LogError("Ground plane is not assigned in CinemachineCameraController.");
  80. return;
  81. }
  82. // Use collider bounds to frame the ground plane
  83. Collider col = groundPlane.GetComponent<Collider>();
  84. if (col == null)
  85. {
  86. Debug.LogError("Ground plane does not have a collider to frame.");
  87. return;
  88. }
  89. Bounds bounds = col.bounds;
  90. Vector3 center = bounds.center + panOffset;
  91. float size = Mathf.Max(bounds.size.x, bounds.size.z);
  92. if (zoomLevel < 0.01f)
  93. {
  94. zoomLevel = size + 5f; // Ensure zoom level is not negative
  95. }
  96. float angleRadians = cameraAngle * Mathf.Deg2Rad;
  97. float y = Mathf.Sin(angleRadians) * zoomLevel;
  98. float z = -Mathf.Cos(angleRadians) * zoomLevel;
  99. Quaternion rotation = Quaternion.Euler(0, yaw, 0); // Apply yaw rotation
  100. Vector3 offset = rotation * new Vector3(0, y, z);
  101. framingPosition = center + offset;
  102. actionCam.transform.position = framingPosition;
  103. actionCam.transform.LookAt(center);
  104. actionCam.Follow = null;
  105. actionCam.LookAt = null;
  106. framingHeight = y;
  107. framingDistance = Mathf.Abs(z);
  108. }
  109. public void FocusOnCharacter(Transform character)
  110. {
  111. actionCam.Follow = character;
  112. actionCam.LookAt = character;
  113. }
  114. public void FollowTargetingLine(Vector3 mouseWorldPosition)
  115. {
  116. Vector3 target = new Vector3(mouseWorldPosition.x, 0, mouseWorldPosition.z) + panOffset;
  117. float angleRadians = cameraAngle * Mathf.Deg2Rad;
  118. float y = Mathf.Sin(angleRadians) * zoomLevel;
  119. float z = -Mathf.Cos(angleRadians) * zoomLevel;
  120. Vector3 camPos = target + new Vector3(0, y, z);
  121. actionCam.transform.position = camPos;
  122. actionCam.transform.LookAt(target);
  123. actionCam.Follow = null;
  124. actionCam.LookAt = null;
  125. }
  126. public void FrameAllCharacters(List<GameObject> characters)
  127. {
  128. if (characters == null || characters.Count == 0) return;
  129. Bounds bounds = new Bounds(characters[0].transform.position, Vector3.zero);
  130. foreach (var go in characters)
  131. bounds.Encapsulate(go.transform.position);
  132. Vector3 center = bounds.center + panOffset;
  133. float size = Mathf.Max(bounds.size.x, bounds.size.z);
  134. // Keep current zoomLevel, but allow zoom out if needed to fit all
  135. float requiredZoom = size + 5f;
  136. if (zoomLevel < requiredZoom)
  137. {
  138. zoomLevel = requiredZoom;
  139. }
  140. float angleRadians = cameraAngle * Mathf.Deg2Rad;
  141. float y = Mathf.Sin(angleRadians) * zoomLevel;
  142. float z = -Mathf.Cos(angleRadians) * zoomLevel;
  143. Vector3 camPos = center + new Vector3(0, y, z);
  144. // Start smooth movement
  145. if (cameraMoveCoroutine != null)
  146. StopCoroutine(cameraMoveCoroutine);
  147. cameraMoveCoroutine = StartCoroutine(SmoothMoveCamera(camPos, center, 0.7f));
  148. }
  149. private IEnumerator SmoothMoveCamera(Vector3 targetPosition, Vector3 lookAtTarget, float duration)
  150. {
  151. Vector3 startPos = actionCam.transform.position;
  152. Quaternion startRot = actionCam.transform.rotation;
  153. Quaternion endRot = Quaternion.LookRotation(lookAtTarget - targetPosition, Vector3.up);
  154. float elapsed = 0f;
  155. while (elapsed < duration)
  156. {
  157. float t = elapsed / duration;
  158. actionCam.transform.position = Vector3.Lerp(startPos, targetPosition, t);
  159. actionCam.transform.rotation = Quaternion.Slerp(startRot, endRot, t);
  160. elapsed += Time.deltaTime;
  161. yield return null;
  162. }
  163. actionCam.transform.position = targetPosition;
  164. actionCam.transform.rotation = endRot;
  165. actionCam.Follow = null;
  166. actionCam.LookAt = null;
  167. }
  168. }