feat: 플레이어 관전 시스템 추가

- PlayerSpectator: 사망한 플레이어가 살아있는 플레이어 관찰
- PlayerCamera: SetTarget/ResetToPlayer/SnapToTarget 메서드 추가
- PlayerMovement: OnEnable/OnDisable에서 입력 액션 관리
- Tab 키로 관전 대상 전환 기능

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-03-14 15:08:18 +09:00
parent 62306a34a2
commit 0a1aeea825
4 changed files with 338 additions and 23 deletions

View File

@@ -1,5 +1,6 @@
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;
namespace Colosseum.Player
{
@@ -20,11 +21,35 @@ namespace Colosseum.Player
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;
@@ -36,12 +61,73 @@ namespace Colosseum.Player
}
// 초기 각도
yaw = target.eulerAngles.y;
if (target != null)
{
yaw = target.eulerAngles.y;
}
pitch = 20f;
// 카메라 위치를 즉시 타겟 위치로 초기화
SnapToTarget();
}
/// <summary>
/// 관전 대상 변경
/// </summary>
public void SetTarget(Transform newTarget)
{
if (newTarget == null) return;
target = newTarget;
isSpectating = true;
// 부드러운 전환을 위해 현재 카메라 위치에서 새 타겟으로
yaw = target.eulerAngles.y;
Debug.Log($"[PlayerCamera] Now spectating: {target.name}");
}
/// <summary>
/// 원래 플레이어로 복귀
/// </summary>
public void ResetToPlayer(Transform playerTransform)
{
target = playerTransform;
isSpectating = false;
}
/// <summary>
/// 카메라 위치를 타겟 위치로 즉시 이동 (부드러운 전환 없이)
/// </summary>
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);
}
/// <summary>
/// 카메라 참조 갱신 (씬 전환 후 호출)
/// </summary>
public void RefreshCamera()
{
// 씬 전환 시 항상 새 카메라 참조 획득
cameraInstance = Camera.main;
}
private void LateUpdate()
{
// 카메라 참조가 없으면 갱신 시도
if (cameraInstance == null)
{
RefreshCamera();
}
if (target == null || cameraInstance == null) return;
HandleRotation();