using UnityEngine; // 유니티 기능을 불러올거에요 -> UnityEngine을 // ============================================================ // NorcielBoss.Tracking.cs — 플레이어 행동 추적 (partial class) // // [역할] // 매 프레임 플레이어의 행동을 분석하여 적응형 AI에 데이터를 제공합니다. // - 정지 감지 (서있으면 공격적 패턴) // - 접근/도주 감지 (다가오면 카운터, 도망치면 갭클로저) // - 카이팅 감지 (거리 유지 + 피격 → 대시 돌진) // - 피격 카운터 감쇠 (시간이 지나면 카운터 감소) // - 뒤잡기 감지 → counterSystem에 위임 // // [설계] // partial class로 NorcielBoss의 private 필드에 직접 접근. // Update()에서 TrackPlayerBehavior() 한 줄로 호출됩니다. // ============================================================ public partial class NorcielBoss : MonsterClass // 부분 클래스를 선언할거에요 -> NorcielBoss의 플레이어 추적 부분으로 { // ══════════════════════════════════════════════════════════ // 플레이어 행동 추적 런타임 변수 // ══════════════════════════════════════════════════════════ private Vector3 _prevTargetPos; // 변수를 선언할거에요 -> 이전 프레임 플레이어 위치를 (행동 추적 기준점) private float _playerStillTimer = 0f; // 변수를 선언할거에요 -> 플레이어 정지 누적 시간을 (멈춰있으면 공격적으로) private float _playerApproachTimer = 0f; // 변수를 선언할거에요 -> 플레이어 접근 누적 시간을 (다가오면 카운터 기회) private float _playerFleeTimer = 0f; // 변수를 선언할거에요 -> 플레이어 도주 누적 시간을 (도망치면 갭클로저) private float _kitingTimer = 0f; // 변수를 선언할거에요 -> 카이팅 감지 누적 시간을 (거리 유지 + 활 쏘기) private int _recentHitCount = 0; // 변수를 선언할거에요 -> 최근 피격 횟수를 (짧은 시간 내 여러 번 맞으면 카이팅) private float _recentHitDecay = 0f; // 변수를 선언할거에요 -> 피격 카운터 감쇠 타이머를 // ══════════════════════════════════════════════════════════ // 매 프레임 플레이어 행동 추적 (Update에서 호출) // ══════════════════════════════════════════════════════════ /// 플레이어의 정지/접근/도주/카이팅 행동을 매 프레임 분석 private void TrackPlayerBehavior() // 함수를 선언할거에요 -> 플레이어 행동을 추적하는 { if (_target == null) return; // 중단할거에요 -> 타겟 없으면 Vector3 curPos = _target.position; // 가져올거에요 -> 현재 플레이어 위치를 Vector3 delta = curPos - _prevTargetPos; // 계산할거에요 -> 이전 프레임 대비 이동량을 float speed = delta.magnitude / Mathf.Max(Time.deltaTime, 0.001f); // 계산할거에요 -> 플레이어 이동 속도를 (m/s) // ── 정지 감지 ─────────────────────────────────── if (speed < playerStillThreshold) // 조건이 맞으면 실행할거에요 -> 거의 안 움직이면 _playerStillTimer += Time.deltaTime; // 누적할거에요 -> 정지 시간을 else // 아니면 (움직이고 있으면) _playerStillTimer = 0f; // 초기화할거에요 -> 움직이면 리셋 // ── 접근 / 도주 감지 ──────────────────────────── float prevDist = Vector3.Distance(transform.position, _prevTargetPos); // 계산할거에요 -> 이전 프레임 거리를 float curDist = Vector3.Distance(transform.position, curPos); // 계산할거에요 -> 현재 프레임 거리를 float distDelta = prevDist - curDist; // 계산할거에요 -> 거리 변화를 (양수 = 접근, 음수 = 도주) if (distDelta > 0.01f) // 조건이 맞으면 실행할거에요 -> 플레이어가 다가오고 있으면 { _playerApproachTimer += Time.deltaTime; // 누적할거에요 -> 접근 시간을 _playerFleeTimer = 0f; // 초기화할거에요 -> 도주 타이머를 } else if (distDelta < -0.01f) // 조건이 맞으면 실행할거에요 -> 도망가고 있으면 { _playerFleeTimer += Time.deltaTime; // 누적할거에요 -> 도주 시간을 _playerApproachTimer = 0f; // 초기화할거에요 -> 접근 타이머를 } else // 거리 변화 거의 없으면 (옆으로 이동 등) { _playerApproachTimer = Mathf.Max(_playerApproachTimer - Time.deltaTime, 0f); // 감소할거에요 -> 천천히 소멸 _playerFleeTimer = Mathf.Max(_playerFleeTimer - Time.deltaTime, 0f); // 감소할거에요 -> 천천히 소멸 } _prevTargetPos = curPos; // 갱신할거에요 -> 다음 프레임 비교용 위치를 // ── 카이팅 감지 ───────────────────────────────── // 조건: 플레이어가 meleeRange 밖에 있으면서 + 최근에 피격당한 적이 있으면 // → "거리 유지하며 원거리 공격하는 중" = 카이팅 float curDist2 = Vector3.Distance(transform.position, curPos); // 계산할거에요 -> 현재 거리를 if (curDist2 > meleeRange && _recentHitCount >= 1) // 조건이 맞으면 실행할거에요 -> 원거리 + 최근 피격이면 _kitingTimer += Time.deltaTime; // 누적할거에요 -> 카이팅 시간을 else // 아니면 _kitingTimer = Mathf.Max(_kitingTimer - Time.deltaTime * 2f, 0f); // 감소할거에요 -> 빠르게 소멸 // ── 피격 카운터 감쇠 ──────────────────────────── // 3초 동안 안 맞으면 피격 카운터 1씩 감소 _recentHitDecay += Time.deltaTime; // 누적할거에요 -> 감쇠 타이머를 if (_recentHitDecay >= 3f) // 조건이 맞으면 실행할거에요 -> 3초 경과하면 { _recentHitDecay = 0f; // 초기화할거에요 -> 타이머를 _recentHitCount = Mathf.Max(_recentHitCount - 1, 0); // 감소시킬거에요 -> 피격 카운터를 (최소 0) } // ── 뒤잡기 감지 → counterSystem에 위임 ────────────── if (_target != null && counterSystem != null) // 조건이 맞으면 실행할거에요 -> 플레이어와 카운터 시스템 있으면 { counterSystem.UpdateBehindTimer(transform.forward, transform.position, _target.position); // 업데이트할거에요 -> 뒤잡기 타이머를 (카운터 시스템에서 관리) } } }