using UnityEngine; // 유니티 엔진의 기본 기능을 불러올거에요 -> UnityEngine을
public class PlayerInput : MonoBehaviour // 클래스를 선언할거에요 -> MonoBehaviour를 상속받는 PlayerInput을
{
// 다른 스크립트에게 명령을 내려야 하니, 미리 참조 시킴
[SerializeField] private PlayerHealth health; // 변수를 선언할거에요 -> 죽었는지 확인할 health를
[SerializeField] private PlayerMovement movement; // 변수를 선언할거에요 -> 이동 명령을 내릴 movement를
[SerializeField] private PlayerAim aim; // 변수를 선언할거에요 -> 회전 명령을 내릴 aim을
[SerializeField] private PlayerInteraction interaction; // 변수를 선언할거에요 -> 상호작용 명령을 내릴 interaction을
[SerializeField] private PlayerAttack attack; // 변수를 선언할거에요 -> 공격 명령을 내릴 attack을
[SerializeField] private PlayerStatsUI statsUI; // 변수를 선언할거에요 -> UI 토글을 위한 statsUI를
// ── 입력 잠금 (보스 연출, 컷씬 등에서 사용) ──────────────────
private bool _isInputLocked = false; // 변수를 선언할거에요 -> 입력 잠금 여부를 (true면 모든 입력 차단)
///
/// 플레이어 입력을 잠가요. 보스 연출, 컷씬 등에서 호출하세요.
/// 잠금 중에는 이동, 공격, 상호작용 등 모든 입력이 무시돼요.
///
public void LockInput() // 함수를 선언할거에요 -> 입력을 잠그는 (외부에서 호출 가능)
{
_isInputLocked = true; // 설정할거에요 -> 입력 잠금 켜기
// 잠금 즉시 이동 입력을 0으로 초기화 (관성으로 미끄러지는 것 방지)
if (movement != null) movement.SetMoveInput(Vector3.zero, false); // 실행할거에요 -> 이동 정지를
Debug.Log("[PlayerInput] 입력 잠금 ON"); // 로그를 찍을거에요
}
///
/// 플레이어 입력 잠금을 해제해요. 연출 종료 후 호출하세요.
///
public void UnlockInput() // 함수를 선언할거에요 -> 입력 잠금을 해제하는 (외부에서 호출 가능)
{
_isInputLocked = false; // 설정할거에요 -> 입력 잠금 끄기
Debug.Log("[PlayerInput] 입력 잠금 OFF"); // 로그를 찍을거에요
}
/// 현재 입력이 잠겨있는지 확인 (읽기 전용)
public bool IsInputLocked => _isInputLocked; // 프로퍼티를 선언할거에요 -> 잠금 상태 조회용
private void Update() // 함수를 실행할거에요 -> 매 프레임마다 Update를
{
// 0. 입력 잠금 체크
// 보스 연출, 컷씬 등에서 입력이 잠겨있으면 모든 입력을 차단해요
if (_isInputLocked) return; // 조건이 맞으면 중단할거에요 -> 입력이 잠겨있다면
// 1. 사망 체크
// 죽었는데 키보드 눌린다고 시체가 움직이면 안 되니까 아예 입력을 차단(return)함
if (health != null && health.IsDead) return; // 조건이 맞으면 중단할거에요 -> 플레이어가 죽었다면
// 2. UI 토글 (C키)
// 스탯 창을 껐다 켰다 함
if (Input.GetKeyDown(KeyCode.C) && statsUI != null) statsUI.ToggleWindow(); // 조건이 맞으면 실행할거에요 -> C키를 눌렀다면 스탯창 토글을
// 3. 이동 입력 감지
// GetAxisRaw를 쓴 이유: 0에서 1로 부드럽게 변하는 게 아니라,
// 키를 누르면 즉시 1, 떼면 0이 되어서 빠릿빠릿한 조작감을 줌
float h = Input.GetAxisRaw("Horizontal"); // 값을 가져올거에요 -> 좌우 입력값(-1, 0, 1)을 h에
float v = Input.GetAxisRaw("Vertical"); // 값을 가져올거에요 -> 상하 입력값(-1, 0, 1)을 v에
bool sprint = Input.GetKey(KeyCode.LeftShift); // 값을 가져올거에요 -> 쉬프트 키 입력 여부를 sprint에
// 4. 이동 명령 하달 (★ 카메라 방향 기준 이동)
if (movement != null) // 조건이 맞으면 실행할거에요 -> 이동 스크립트가 있다면
{
// 카메라의 앞/오른쪽 방향을 기준으로 이동 방향을 계산해요
// 이렇게 하면 W키가 항상 "화면 기준 앞"으로 이동합니다
Vector3 inputDir = new Vector3(h, 0, v); // 만들거에요 -> 원시 입력 벡터를
Vector3 cameraRelativeDir = ConvertToCameraRelative(inputDir); // 변환할거에요 -> 카메라 기준 방향으로
movement.SetMoveInput(cameraRelativeDir, sprint); // 실행할거에요 -> 카메라 기준 이동 입력 전달을
// ⭐ [추가] 스페이스바 누르면 대시 명령!
if (Input.GetKeyDown(KeyCode.Space)) movement.AttemptDash(); // 조건이 맞으면 실행할거에요 -> 스페이스바 누르면 대시 시도를
}
// 5. 마우스 회전 명령
// 캐릭터가 마우스 커서를 바라보게 합니다.
if (aim != null) aim.RotateTowardsMouse(); // 조건이 맞으면 실행할거에요 -> 마우스 방향 회전 함수를
// 6. 상호작용 (F키)
// 바닥에 떨어진 무기를 줍기
if (Input.GetKeyDown(KeyCode.F) && interaction != null) interaction.TryInteract(); // 조건이 맞으면 실행할거에요 -> F키 누르면 상호작용 시도를
// 7. 공격 입력 (좌클릭/우클릭)
if (attack != null) // 조건이 맞으면 실행할거에요 -> 공격 스크립트가 있다면
{
// 우클릭 꾹: 차징(모으기) 시작
if (Input.GetMouseButtonDown(1)) attack.StartCharging(); // 조건이 맞으면 실행할거에요 -> 우클릭 누르면 차징 시작을
// 우클릭 뗌: 차징된 단계로 부채꼴 발사! (기존: 취소 → 변경: 발사)
if (Input.GetMouseButtonUp(1)) attack.ReleaseAttack(); // 조건이 맞으면 실행할거에요 -> 우클릭 떼면 차징 발사를
// 좌클릭: 차징 중이 아닐 때만 일반 공격
if (Input.GetMouseButtonDown(0)) // 조건이 맞으면 실행할거에요 -> 좌클릭을 눌렀다면
{
if (!attack.IsCharging) // 조건이 맞으면 실행할거에요 -> 차징 중이 아니라면
attack.PerformNormalAttack(); // 실행할거에요 -> 일반 공격을
// 차징 중 좌클릭은 무시 (우클릭 떼기로만 발사)
}
}
}
// ─────────────────────────────────────────────────────────────
// 카메라 기준 이동 방향 변환
// ─────────────────────────────────────────────────────────────
///
/// WASD 입력을 카메라가 바라보는 방향 기준으로 변환합니다.
/// W = 카메라 앞, A = 카메라 왼쪽, S = 카메라 뒤, D = 카메라 오른쪽
/// Y축(높이)은 무시하고 수평 방향만 사용합니다.
///
private Vector3 ConvertToCameraRelative(Vector3 rawInput) // 함수를 정의할거에요 -> 카메라 기준 방향 변환을
{
if (rawInput.sqrMagnitude < 0.001f) return Vector3.zero; // 방어할거에요 -> 입력이 없으면 제로 벡터 반환
Camera cam = Camera.main; // 가져올거에요 -> 메인 카메라를
if (cam == null) return rawInput.normalized; // 방어할거에요 -> 카메라 없으면 월드 좌표 그대로 사용
// 카메라의 앞/오른쪽 방향에서 Y 성분을 제거 (수평면만 사용)
Vector3 camForward = cam.transform.forward; // 가져올거에요 -> 카메라의 앞 방향을
camForward.y = 0f; // 제거할거에요 -> 높이 성분을 (수평면에 투영)
camForward.Normalize(); // 정규화할거에요 -> 단위 벡터로
Vector3 camRight = cam.transform.right; // 가져올거에요 -> 카메라의 오른쪽 방향을
camRight.y = 0f; // 제거할거에요 -> 높이 성분을 (수평면에 투영)
camRight.Normalize(); // 정규화할거에요 -> 단위 벡터로
// 입력 × 카메라 방향 = 최종 이동 방향
// W(v=1) → 카메라 앞, D(h=1) → 카메라 오른쪽
Vector3 result = camForward * rawInput.z + camRight * rawInput.x; // 계산할거에요 -> 카메라 기준 최종 방향을
return result.normalized; // 반환할거에요 -> 정규화된 방향을 (대각선 속도 보정)
}
}