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; // 반환할거에요 -> 정규화된 방향을 (대각선 속도 보정) } }