using System.Collections.Generic; using UnityEngine; /// /// 플레이어의 전투 행동을 슬라이딩 윈도우(최근 N초) 기반으로 추적합니다. /// 보스 AI가 이 데이터를 읽어 카운터 패턴 발동 여부를 판단합니다. /// public class PlayerBehaviorTracker : MonoBehaviour { public static PlayerBehaviorTracker Instance { get; private set; } [Header("슬라이딩 윈도우 설정")] [Tooltip("행동 추적 윈도우 크기(초)")] [SerializeField] private float windowDuration = 10f; // ── 내부 기록용 타임스탬프 리스트 ── private List dodgeTimestamps = new List(); private List aimStartTimes = new List(); private List aimEndTimes = new List(); private List pierceShotTimestamps = new List(); private List totalShotTimestamps = new List(); // ── 현재 조준 상태 ── private bool isAiming = false; private float currentAimStartTime; // ── 외부에서 읽는 프로퍼티 ── public int DodgeCount => CountInWindow(dodgeTimestamps); public float AimHoldTime => CalculateAimHoldTime(); public float PierceRatio => CalculatePierceRatio(); public int TotalShotsInWindow => CountInWindow(totalShotTimestamps); private void Awake() { if (Instance != null && Instance != this) { Destroy(gameObject); return; } Instance = this; } // ═══════════════════════════════════════════ // 외부에서 호출하는 이벤트 기록 메서드 // ═══════════════════════════════════════════ /// 플레이어가 회피(대시/구르기)를 수행했을 때 호출 public void RecordDodge() { dodgeTimestamps.Add(Time.time); } /// 플레이어가 조준을 시작했을 때 호출 public void RecordAimStart() { if (!isAiming) { isAiming = true; currentAimStartTime = Time.time; } } /// 플레이어가 조준을 해제했을 때 호출 public void RecordAimEnd() { if (isAiming) { isAiming = false; aimStartTimes.Add(currentAimStartTime); aimEndTimes.Add(Time.time); } } /// 화살 발사 시 호출. isPierce=true면 관통 화살 public void RecordShot(bool isPierce) { totalShotTimestamps.Add(Time.time); if (isPierce) { pierceShotTimestamps.Add(Time.time); } } // ═══════════════════════════════════════════ // 런 리셋 (새 런 시작 시 호출) // ═══════════════════════════════════════════ /// 새 런 시작 시 모든 행동 기록을 초기화 public void ResetForNewRun() { dodgeTimestamps.Clear(); aimStartTimes.Clear(); aimEndTimes.Clear(); pierceShotTimestamps.Clear(); totalShotTimestamps.Clear(); isAiming = false; } // ═══════════════════════════════════════════ // 내부 계산 // ═══════════════════════════════════════════ private int CountInWindow(List timestamps) { float cutoff = Time.time - windowDuration; // 오래된 항목 제거 timestamps.RemoveAll(t => t < cutoff); return timestamps.Count; } private float CalculateAimHoldTime() { float cutoff = Time.time - windowDuration; float total = 0f; // 완료된 조준 세션 for (int i = aimStartTimes.Count - 1; i >= 0; i--) { if (aimEndTimes[i] < cutoff) { aimStartTimes.RemoveAt(i); aimEndTimes.RemoveAt(i); continue; } float start = Mathf.Max(aimStartTimes[i], cutoff); total += aimEndTimes[i] - start; } // 현재 조준 중인 시간도 포함 if (isAiming) { float start = Mathf.Max(currentAimStartTime, cutoff); total += Time.time - start; } return total; } private float CalculatePierceRatio() { int totalShots = CountInWindow(totalShotTimestamps); if (totalShots == 0) return 0f; int pierceShots = CountInWindow(pierceShotTimestamps); return (float)pierceShots / totalShots; } }