using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.SceneManagement; namespace Colosseum.Player { /// /// 3인칭 카메라 컨트롤러 /// public class PlayerCamera : MonoBehaviour { [Header("Camera Settings")] [SerializeField] private float distance = 5f; [SerializeField] private float height = 2f; [SerializeField] private float rotationSpeed = 2f; [SerializeField] private float minPitch = -30f; [SerializeField] private float maxPitch = 60f; private Transform target; private float yaw; private float pitch; private Camera cameraInstance; private InputSystem_Actions inputActions; private bool isSpectating = false; public Transform Target => target; public bool IsSpectating => isSpectating; private void OnEnable() { SceneManager.sceneLoaded += OnSceneLoaded; } private void OnDisable() { SceneManager.sceneLoaded -= OnSceneLoaded; } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { // 씬 로드 시 카메라 참조 갱신 RefreshCamera(); SnapToTarget(); Debug.Log($"[PlayerCamera] Scene loaded, camera refreshed"); } public void Initialize(Transform playerTransform, InputSystem_Actions actions) { target = playerTransform; inputActions = actions; isSpectating = false; // 기존 메인 카메라 사용 또는 새로 생성 cameraInstance = Camera.main; Debug.Log($"[PlayerCamera] Initialize: target={playerTransform?.name}, Camera.main={cameraInstance?.name ?? "NULL"}"); if (cameraInstance == null) { var cameraObject = new GameObject("PlayerCamera"); cameraInstance = cameraObject.AddComponent(); cameraObject.tag = "MainCamera"; } // 초기 각도 if (target != null) { yaw = target.eulerAngles.y; } pitch = 20f; // 카메라 위치를 즉시 타겟 위치로 초기화 SnapToTarget(); } /// /// 관전 대상 변경 /// public void SetTarget(Transform newTarget) { if (newTarget == null) return; target = newTarget; isSpectating = true; // 부드러운 전환을 위해 현재 카메라 위치에서 새 타겟으로 yaw = target.eulerAngles.y; Debug.Log($"[PlayerCamera] Now spectating: {target.name}"); } /// /// 원래 플레이어로 복귀 /// public void ResetToPlayer(Transform playerTransform) { target = playerTransform; isSpectating = false; } /// /// 카메라 위치를 타겟 위치로 즉시 이동 (부드러운 전환 없이) /// public void SnapToTarget() { if (target == null || cameraInstance == null) return; Quaternion rotation = Quaternion.Euler(pitch, yaw, 0f); Vector3 offset = rotation * new Vector3(0f, 0f, -distance); offset.y += height; cameraInstance.transform.position = target.position + offset; cameraInstance.transform.LookAt(target.position + Vector3.up * height * 0.5f); } /// /// 카메라 참조 갱신 (씬 전환 후 호출) /// public void RefreshCamera() { // 씬 전환 시 항상 새 카메라 참조 획득 cameraInstance = Camera.main; } private void LateUpdate() { // 카메라 참조가 없으면 갱신 시도 if (cameraInstance == null) { RefreshCamera(); } if (target == null || cameraInstance == null) return; HandleRotation(); UpdateCameraPosition(); } private void HandleRotation() { if (inputActions == null) return; // Input Actions에서 Look 입력 받기 Vector2 lookInput = inputActions.Player.Look.ReadValue(); float mouseX = lookInput.x * rotationSpeed * 0.1f; float mouseY = lookInput.y * rotationSpeed * 0.1f; yaw += mouseX; pitch -= mouseY; pitch = Mathf.Clamp(pitch, minPitch, maxPitch); } private void UpdateCameraPosition() { // 구면 좌표로 카메라 위치 계산 Quaternion rotation = Quaternion.Euler(pitch, yaw, 0f); Vector3 offset = rotation * new Vector3(0f, 0f, -distance); offset.y += height; cameraInstance.transform.position = target.position + offset; cameraInstance.transform.LookAt(target.position + Vector3.up * height * 0.5f); } } }