chunk 1: core gameplay scripts scenes runtime assets
This commit is contained in:
188
Assets/Scripts/Camera/CameraInitializer.cs
Normal file
188
Assets/Scripts/Camera/CameraInitializer.cs
Normal file
@@ -0,0 +1,188 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user