using UnityEngine; using Cinemachine; using System.Collections; /// /// Resets the Main Camera to a known-good position BEFORE CinemachineBrain takes over, /// then snaps the camera to the correct position AFTER the character spawns. /// /// This solves the camera-underground problem with a two-phase approach: /// Phase 1 (Awake): Force camera above ground, set CinemachineBrain blend to Cut /// Phase 2 (Coroutine): Wait for player character, then force camera to correct orbit position /// /// Attach this to the MainCamera GameObject. /// [DefaultExecutionOrder(-100)] // Run before everything else public class CameraInitializer : MonoBehaviour { [Header("Safe Starting Position")] [Tooltip("Camera starts here before Cinemachine takes control")] public Vector3 resetPosition = new Vector3(0f, 10f, -10f); public Vector3 resetRotation = new Vector3(20f, 0f, 0f); [Header("Spawn Wait")] [Tooltip("Maximum time to wait for the player character to spawn")] [SerializeField] private float maxSpawnWaitTime = 5f; [Tooltip("How often to check for the player character")] [SerializeField] private float spawnCheckInterval = 0.1f; [Header("Debug")] [SerializeField] private bool debugLogging = true; private void Awake() { // ===== PHASE 1: Force camera to a known-good position ===== transform.position = resetPosition; transform.rotation = Quaternion.Euler(resetRotation); if (debugLogging) Debug.Log($"[CameraInitializer] Phase 1: Camera reset to position={resetPosition}"); // Set CinemachineBrain to Cut (instant snap, no blending from underground) CinemachineBrain brain = GetComponent(); if (brain != null) { brain.m_DefaultBlend = new CinemachineBlendDefinition( CinemachineBlendDefinition.Style.Cut, 0f ); if (debugLogging) Debug.Log("[CameraInitializer] CinemachineBrain set to CUT blend"); } // Invalidate all FreeLook camera states CinemachineFreeLook[] freeLooks = FindObjectsOfType(); foreach (var fl in freeLooks) { fl.m_YAxis.Value = 0.5f; fl.PreviousStateIsValid = false; // Set orbit heights fl.m_Orbits = new CinemachineFreeLook.Orbit[] { new CinemachineFreeLook.Orbit(4.5f, 5f), // TopRig new CinemachineFreeLook.Orbit(2.5f, 6f), // MiddleRig new CinemachineFreeLook.Orbit(0.5f, 5f), // BottomRig }; // CRITICAL: Clear default input axis names so Cinemachine doesn't fight // with CinemachineCameraInput. Without this, Cinemachine reads Input.GetAxis("Mouse X") // which returns 0 and snaps Y axis to bottom rig. fl.m_XAxis.m_InputAxisName = ""; fl.m_YAxis.m_InputAxisName = ""; if (debugLogging) Debug.Log($"[CameraInitializer] FreeLook '{fl.name}': YAxis=0.5, axes cleared, state invalidated"); } } private IEnumerator Start() { // ===== PHASE 2: Wait for player character, then snap camera ===== float elapsed = 0f; Transform playerTransform = null; while (elapsed < maxSpawnWaitTime) { // Try to find the player character playerTransform = FindPlayerCharacter(); if (playerTransform != null) { if (debugLogging) Debug.Log($"[CameraInitializer] Phase 2: Found player '{playerTransform.name}' at {playerTransform.position} after {elapsed:F1}s"); break; } yield return new WaitForSeconds(spawnCheckInterval); elapsed += spawnCheckInterval; } if (playerTransform == null) { Debug.LogWarning("[CameraInitializer] Phase 2: No player found within timeout — camera may be incorrect"); yield break; } // Force camera to the correct position relative to the character SnapCameraToCharacter(playerTransform); // Wait a frame for Cinemachine to process the snap yield return null; yield return null; // Restore smooth blending for character switching CinemachineBrain brain = GetComponent(); if (brain != null) { brain.m_DefaultBlend = new CinemachineBlendDefinition( CinemachineBlendDefinition.Style.EaseInOut, 1f ); if (debugLogging) Debug.Log("[CameraInitializer] Phase 2: Blend restored to EaseInOut"); } } /// /// Force-snap the camera to the correct orbit position relative to the character. /// private void SnapCameraToCharacter(Transform character) { CinemachineFreeLook[] freeLooks = FindObjectsOfType(); foreach (var fl in freeLooks) { if (!fl.isActiveAndEnabled) continue; // Ensure target is set if (fl.Follow == null) { fl.Follow = character; fl.LookAt = character; } // Reset to middle rig fl.m_YAxis.Value = 0.5f; fl.PreviousStateIsValid = false; // Force camera transform to the correct position // MiddleRig: Height 2.5, Radius 6 — camera should be behind and above Vector3 targetPos = character.position; Vector3 cameraPos = targetPos + new Vector3(0f, 2.5f, -6f); // Set the Main Camera transform directly transform.position = cameraPos; transform.LookAt(targetPos + Vector3.up * 1.5f); if (debugLogging) { Debug.Log($"[CameraInitializer] Camera snapped to {cameraPos} (character at {targetPos})"); Debug.Log($"[CameraInitializer] FreeLook Follow={fl.Follow?.name}, LookAt={fl.LookAt?.name}"); } } } /// /// Find the player-controlled character in the scene. /// private Transform FindPlayerCharacter() { // Try CameraManager first if (CameraManager.Instance != null) { var target = CameraManager.Instance.GetCurrentTarget(); if (target != null) return target.transform; } // Try finding by tag GameObject player = GameObject.FindGameObjectWithTag("Player"); if (player != null) return player.transform; // Try TeamMember with IsPlayerControlled TeamMember[] teamMembers = FindObjectsOfType(); foreach (var tm in teamMembers) { if (tm.IsPlayerControlled) return tm.transform; } return null; } }