148 lines
4.9 KiB
C#
148 lines
4.9 KiB
C#
|
|
using System.Collections.Generic;
|
||
|
|
using UnityEngine;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 플레이어의 전투 행동을 슬라이딩 윈도우(최근 N초) 기반으로 추적합니다.
|
||
|
|
/// 보스 AI가 이 데이터를 읽어 카운터 패턴 발동 여부를 판단합니다.
|
||
|
|
/// </summary>
|
||
|
|
public class PlayerBehaviorTracker : MonoBehaviour
|
||
|
|
{
|
||
|
|
public static PlayerBehaviorTracker Instance { get; private set; }
|
||
|
|
|
||
|
|
[Header("슬라이딩 윈도우 설정")]
|
||
|
|
[Tooltip("행동 추적 윈도우 크기(초)")]
|
||
|
|
[SerializeField] private float windowDuration = 10f;
|
||
|
|
|
||
|
|
// ── 내부 기록용 타임스탬프 리스트 ──
|
||
|
|
private List<float> dodgeTimestamps = new List<float>();
|
||
|
|
private List<float> aimStartTimes = new List<float>();
|
||
|
|
private List<float> aimEndTimes = new List<float>();
|
||
|
|
private List<float> pierceShotTimestamps = new List<float>();
|
||
|
|
private List<float> totalShotTimestamps = new List<float>();
|
||
|
|
|
||
|
|
// ── 현재 조준 상태 ──
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════
|
||
|
|
// 외부에서 호출하는 이벤트 기록 메서드
|
||
|
|
// ═══════════════════════════════════════════
|
||
|
|
|
||
|
|
/// <summary>플레이어가 회피(대시/구르기)를 수행했을 때 호출</summary>
|
||
|
|
public void RecordDodge()
|
||
|
|
{
|
||
|
|
dodgeTimestamps.Add(Time.time);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>플레이어가 조준을 시작했을 때 호출</summary>
|
||
|
|
public void RecordAimStart()
|
||
|
|
{
|
||
|
|
if (!isAiming)
|
||
|
|
{
|
||
|
|
isAiming = true;
|
||
|
|
currentAimStartTime = Time.time;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>플레이어가 조준을 해제했을 때 호출</summary>
|
||
|
|
public void RecordAimEnd()
|
||
|
|
{
|
||
|
|
if (isAiming)
|
||
|
|
{
|
||
|
|
isAiming = false;
|
||
|
|
aimStartTimes.Add(currentAimStartTime);
|
||
|
|
aimEndTimes.Add(Time.time);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>화살 발사 시 호출. isPierce=true면 관통 화살</summary>
|
||
|
|
public void RecordShot(bool isPierce)
|
||
|
|
{
|
||
|
|
totalShotTimestamps.Add(Time.time);
|
||
|
|
if (isPierce)
|
||
|
|
{
|
||
|
|
pierceShotTimestamps.Add(Time.time);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════
|
||
|
|
// 런 리셋 (새 런 시작 시 호출)
|
||
|
|
// ═══════════════════════════════════════════
|
||
|
|
|
||
|
|
/// <summary>새 런 시작 시 모든 행동 기록을 초기화</summary>
|
||
|
|
public void ResetForNewRun()
|
||
|
|
{
|
||
|
|
dodgeTimestamps.Clear();
|
||
|
|
aimStartTimes.Clear();
|
||
|
|
aimEndTimes.Clear();
|
||
|
|
pierceShotTimestamps.Clear();
|
||
|
|
totalShotTimestamps.Clear();
|
||
|
|
isAiming = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════
|
||
|
|
// 내부 계산
|
||
|
|
// ═══════════════════════════════════════════
|
||
|
|
|
||
|
|
private int CountInWindow(List<float> 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;
|
||
|
|
}
|
||
|
|
}
|