chore: Assets 디렉토리 구조 정리 및 네이밍 컨벤션 적용
- Assets/_Game/ 하위로 게임 에셋 통합 - External/ 패키지 벤더별 분류 (Synty, Animations, UI) - 에셋 네이밍 컨벤션 확립 및 적용 (Data_Skill_, Data_SkillEffect_, Prefab_, Anim_, Model_, BT_ 등) - pre-commit hook으로 네이밍 컨벤션 자동 검사 추가 - RESTRUCTURE_CHECKLIST.md 작성 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
162
Assets/_Game/Scripts/Player/PlayerCamera.cs
Normal file
162
Assets/_Game/Scripts/Player/PlayerCamera.cs
Normal file
@@ -0,0 +1,162 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Colosseum.Player
|
||||
{
|
||||
/// <summary>
|
||||
/// 3인칭 카메라 컨트롤러
|
||||
/// </summary>
|
||||
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;
|
||||
if (cameraInstance == null)
|
||||
{
|
||||
var cameraObject = new GameObject("PlayerCamera");
|
||||
cameraInstance = cameraObject.AddComponent<Camera>();
|
||||
cameraObject.tag = "MainCamera";
|
||||
}
|
||||
|
||||
// 초기 각도
|
||||
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();
|
||||
UpdateCameraPosition();
|
||||
}
|
||||
|
||||
private void HandleRotation()
|
||||
{
|
||||
if (inputActions == null) return;
|
||||
|
||||
// Input Actions에서 Look 입력 받기
|
||||
Vector2 lookInput = inputActions.Player.Look.ReadValue<Vector2>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user