Projext/Assets/Scripts/Enemy/BossAI/BossCounterFeedback.cs

190 lines
6.6 KiB
C#
Raw Normal View History

2026-02-10 15:29:22 +00:00
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
/// <summary>
/// 보스 카운터 시스템의 연출/UI를 담당합니다.
/// 카운터 발동 시 보스 대사, 화면 효과 등을 처리합니다.
///
/// "...또 그 수법이냐" 같은 대사로 "보스가 기억한다"는 서사 전달.
/// </summary>
public class BossCounterFeedback : MonoBehaviour
{
[Header("참조")]
[SerializeField] private BossCounterSystem counterSystem;
[Header("UI 요소 (선택)")]
[SerializeField] private TextMeshProUGUI bossDialogueText;
[SerializeField] private CanvasGroup dialogueCanvasGroup;
[SerializeField] private float dialogueDisplayDuration = 3f;
[SerializeField] private float dialogueFadeDuration = 0.5f;
[Header("카운터별 보스 대사")]
[SerializeField] private string[] dodgeCounterDialogues = new string[]
{
"...또 도망치려는 건가.",
"네 발은 이미 읽었다.",
"아무리 피해도 소용없어."
};
[SerializeField] private string[] aimCounterDialogues = new string[]
{
"그렇게 오래 노려봐야...",
"느린 조준은 빈틈이지.",
"시간을 줄 생각은 없다."
};
[SerializeField] private string[] pierceCounterDialogues = new string[]
{
"그 화살은 더 이상 통하지 않아.",
"관통? 이번엔 막아주지.",
"같은 수를 반복하다니."
};
[SerializeField] private string[] habitChangeDialogues = new string[]
{
"...다른 수를 쓰는 건가.",
"흥, 조금은 배운 모양이군.",
"이번엔 다르군... 재밌어."
};
// ── 잠금 해제 여부에 따른 첫 발동 대사 ──
[Header("첫 잠금 해제 시 대사 (서사 강조)")]
[SerializeField] private string[] firstUnlockDialogues = new string[]
{
"...기억했다. 네 버릇을.",
"이제 알겠어. 네 전투 방식이.",
"한 번이면 충분해. 패턴을 읽었다."
};
private Coroutine dialogueCoroutine;
private void OnEnable()
{
if (counterSystem != null)
{
counterSystem.OnCounterActivated.AddListener(OnCounterActivated);
counterSystem.OnCounterDeactivated.AddListener(OnCounterDeactivated);
counterSystem.OnHabitChangeRewarded.AddListener(OnHabitChangeRewarded);
}
}
private void OnDisable()
{
if (counterSystem != null)
{
counterSystem.OnCounterActivated.RemoveListener(OnCounterActivated);
counterSystem.OnCounterDeactivated.RemoveListener(OnCounterDeactivated);
counterSystem.OnHabitChangeRewarded.RemoveListener(OnHabitChangeRewarded);
}
}
// ═══════════════════════════════════════════
// 이벤트 핸들러
// ═══════════════════════════════════════════
private void OnCounterActivated(CounterType type)
{
var persistence = BossCounterPersistence.Instance;
// 첫 잠금 해제인지 확인 (총 발동 횟수가 1이면 방금 처음 잠금 해제된 것)
bool isFirstUnlock = false;
if (persistence != null)
{
int activations = type switch
{
CounterType.Dodge => persistence.Data.dodgeCounterActivations,
CounterType.Aim => persistence.Data.aimCounterActivations,
CounterType.Pierce => persistence.Data.pierceCounterActivations,
_ => 0
};
isFirstUnlock = (activations <= 1);
}
// 대사 선택
string dialogue;
if (isFirstUnlock)
{
dialogue = firstUnlockDialogues[Random.Range(0, firstUnlockDialogues.Length)];
}
else
{
dialogue = type switch
{
CounterType.Dodge => dodgeCounterDialogues[Random.Range(0, dodgeCounterDialogues.Length)],
CounterType.Aim => aimCounterDialogues[Random.Range(0, aimCounterDialogues.Length)],
CounterType.Pierce => pierceCounterDialogues[Random.Range(0, pierceCounterDialogues.Length)],
_ => ""
};
}
ShowDialogue(dialogue);
// TODO: 여기에 추가 연출 넣기
// - 보스 전용 애니메이션 트리거 (예: animator.SetTrigger("CounterReady"))
// - 보스 주변 이펙트 (파티클, 오라 등)
// - 카메라 연출 (줌인, 슬로모션 등)
// - 사운드 효과
Debug.Log($"[BossCounterFeedback] {type} 카운터 연출 재생" +
(isFirstUnlock ? " (첫 잠금 해제!)" : ""));
}
private void OnCounterDeactivated(CounterType type)
{
// 카운터 해제 시 연출 (이펙트 종료 등)
Debug.Log($"[BossCounterFeedback] {type} 카운터 연출 종료");
}
private void OnHabitChangeRewarded()
{
string dialogue = habitChangeDialogues[Random.Range(0, habitChangeDialogues.Length)];
ShowDialogue(dialogue);
Debug.Log("[BossCounterFeedback] 습관 변경 보상 연출!");
}
// ═══════════════════════════════════════════
// 대사 표시
// ═══════════════════════════════════════════
private void ShowDialogue(string text)
{
if (bossDialogueText == null || dialogueCanvasGroup == null) return;
if (dialogueCoroutine != null)
StopCoroutine(dialogueCoroutine);
dialogueCoroutine = StartCoroutine(DialogueRoutine(text));
}
private IEnumerator DialogueRoutine(string text)
{
bossDialogueText.text = text;
dialogueCanvasGroup.alpha = 0f;
// 페이드 인
float t = 0f;
while (t < dialogueFadeDuration)
{
t += Time.deltaTime;
dialogueCanvasGroup.alpha = t / dialogueFadeDuration;
yield return null;
}
dialogueCanvasGroup.alpha = 1f;
// 유지
yield return new WaitForSeconds(dialogueDisplayDuration);
// 페이드 아웃
t = 0f;
while (t < dialogueFadeDuration)
{
t += Time.deltaTime;
dialogueCanvasGroup.alpha = 1f - (t / dialogueFadeDuration);
yield return null;
}
dialogueCanvasGroup.alpha = 0f;
}
}