189 lines
7.0 KiB
C#
189 lines
7.0 KiB
C#
using UnityEngine;
|
|
using Cinemachine;
|
|
using System.Collections;
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
[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<CinemachineBrain>();
|
|
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<CinemachineFreeLook>();
|
|
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<CinemachineBrain>();
|
|
if (brain != null)
|
|
{
|
|
brain.m_DefaultBlend = new CinemachineBlendDefinition(
|
|
CinemachineBlendDefinition.Style.EaseInOut, 1f
|
|
);
|
|
if (debugLogging)
|
|
Debug.Log("[CameraInitializer] Phase 2: Blend restored to EaseInOut");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Force-snap the camera to the correct orbit position relative to the character.
|
|
/// </summary>
|
|
private void SnapCameraToCharacter(Transform character)
|
|
{
|
|
CinemachineFreeLook[] freeLooks = FindObjectsOfType<CinemachineFreeLook>();
|
|
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}");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find the player-controlled character in the scene.
|
|
/// </summary>
|
|
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<TeamMember>();
|
|
foreach (var tm in teamMembers)
|
|
{
|
|
if (tm.IsPlayerControlled) return tm.transform;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|