185 lines
11 KiB
C#
185 lines
11 KiB
C#
using System.Collections; // 코루틴 기능을 사용할거에요 -> System.Collections를
|
|
using UnityEngine; // 유니티 엔진의 기본 기능을 불러올거에요 -> UnityEngine을
|
|
using UnityEngine.UI; // UI 기능을 사용할거에요 -> UnityEngine.UI를
|
|
using TMPro; // 텍스트메쉬프로 기능을 사용할거에요 -> TMPro를
|
|
|
|
/// <summary>
|
|
/// 보스 카운터 시스템의 연출/UI를 담당합니다.
|
|
/// </summary>
|
|
public class BossCounterFeedback : MonoBehaviour // 클래스를 선언할거에요 -> MonoBehaviour를 상속받는 BossCounterFeedback을
|
|
{
|
|
[Header("참조")] // 인스펙터 창에 제목을 표시할거에요 -> 참조 를
|
|
[SerializeField] private BossCounterSystem counterSystem; // 변수를 선언할거에요 -> 카운터 시스템 스크립트를 연결할 counterSystem을
|
|
|
|
[Header("UI 요소 (선택)")] // 인스펙터 창에 제목을 표시할거에요 -> UI 요소 (선택) 을
|
|
[SerializeField] private TextMeshProUGUI bossDialogueText; // 변수를 선언할거에요 -> 보스 대사를 표시할 텍스트 UI를
|
|
[SerializeField] private CanvasGroup dialogueCanvasGroup; // 변수를 선언할거에요 -> 대사창의 투명도를 조절할 캔버스 그룹을
|
|
[SerializeField] private float dialogueDisplayDuration = 3f; // 변수를 선언할거에요 -> 대사 표시 시간(3초)을 dialogueDisplayDuration에
|
|
[SerializeField] private float dialogueFadeDuration = 0.5f; // 변수를 선언할거에요 -> 대사 페이드 시간(0.5초)을 dialogueFadeDuration에
|
|
|
|
[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; // 변수를 선언할거에요 -> 실행 중인 대사 코루틴을 저장할 dialogueCoroutine을
|
|
|
|
private void OnEnable() // 함수를 실행할거에요 -> 오브젝트 활성화 시 호출되는 OnEnable을
|
|
{
|
|
if (counterSystem != null) // 조건이 맞으면 실행할거에요 -> 카운터 시스템이 연결되어 있다면
|
|
{
|
|
counterSystem.OnCounterActivated.AddListener(OnCounterActivated); // 구독할거에요 -> 카운터 활성 이벤트에 OnCounterActivated를
|
|
counterSystem.OnCounterDeactivated.AddListener(OnCounterDeactivated); // 구독할거에요 -> 카운터 비활성 이벤트에 OnCounterDeactivated를
|
|
counterSystem.OnHabitChangeRewarded.AddListener(OnHabitChangeRewarded); // 구독할거에요 -> 보상 이벤트에 OnHabitChangeRewarded를
|
|
}
|
|
}
|
|
|
|
private void OnDisable() // 함수를 실행할거에요 -> 오브젝트 비활성화 시 호출되는 OnDisable을
|
|
{
|
|
if (counterSystem != null) // 조건이 맞으면 실행할거에요 -> 카운터 시스템이 연결되어 있다면
|
|
{
|
|
counterSystem.OnCounterActivated.RemoveListener(OnCounterActivated); // 구독 해제할거에요 -> 카운터 활성 이벤트를
|
|
counterSystem.OnCounterDeactivated.RemoveListener(OnCounterDeactivated); // 구독 해제할거에요 -> 카운터 비활성 이벤트를
|
|
counterSystem.OnHabitChangeRewarded.RemoveListener(OnHabitChangeRewarded); // 구독 해제할거에요 -> 보상 이벤트를
|
|
}
|
|
}
|
|
|
|
// ═══════════════════════════════════════════
|
|
// 이벤트 핸들러
|
|
// ═══════════════════════════════════════════
|
|
|
|
private void OnCounterActivated(CounterType type) // 함수를 선언할거에요 -> 카운터 활성 시 호출될 OnCounterActivated를
|
|
{
|
|
var persistence = BossCounterPersistence.Instance; // 변수를 가져올거에요 -> 영구 저장소 인스턴스를 persistence에
|
|
|
|
// 첫 잠금 해제인지 확인 (총 발동 횟수가 1이면 방금 처음 잠금 해제된 것)
|
|
bool isFirstUnlock = false; // 변수를 초기화할거에요 -> 첫 해금 여부를 거짓(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); // 판단할거에요 -> 발동 횟수가 1 이하라면 첫 해금이라고
|
|
}
|
|
|
|
// 대사 선택
|
|
string dialogue; // 변수를 선언할거에요 -> 출력할 대사를 담을 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); // 함수를 실행할거에요 -> 선택된 대사를 화면에 띄우는 ShowDialogue를
|
|
|
|
Debug.Log($"[BossCounterFeedback] {type} 카운터 연출 재생" + (isFirstUnlock ? " (첫 잠금 해제!)" : "")); // 로그를 출력할거에요 -> 연출 재생 알림을
|
|
}
|
|
|
|
private void OnCounterDeactivated(CounterType type) // 함수를 선언할거에요 -> 카운터 종료 시 호출될 OnCounterDeactivated를
|
|
{
|
|
// 카운터 해제 시 연출 (이펙트 종료 등)
|
|
Debug.Log($"[BossCounterFeedback] {type} 카운터 연출 종료"); // 로그를 출력할거에요 -> 연출 종료 알림을
|
|
}
|
|
|
|
private void OnHabitChangeRewarded() // 함수를 선언할거에요 -> 보상 지급 시 호출될 OnHabitChangeRewarded를
|
|
{
|
|
string dialogue = habitChangeDialogues[Random.Range(0, habitChangeDialogues.Length)]; // 선택할거에요 -> 보상 대사 중 하나를 랜덤으로
|
|
ShowDialogue(dialogue); // 함수를 실행할거에요 -> 대사를 띄우는 ShowDialogue를
|
|
|
|
Debug.Log("[BossCounterFeedback] 습관 변경 보상 연출!"); // 로그를 출력할거에요 -> 보상 연출 알림을
|
|
}
|
|
|
|
// ═══════════════════════════════════════════
|
|
// 대사 표시
|
|
// ═══════════════════════════════════════════
|
|
|
|
private void ShowDialogue(string text) // 함수를 선언할거에요 -> 텍스트를 UI에 표시하는 ShowDialogue를
|
|
{
|
|
if (bossDialogueText == null || dialogueCanvasGroup == null) return; // 조건이 맞으면 중단할거에요 -> UI 요소가 연결되지 않았다면
|
|
|
|
if (dialogueCoroutine != null) // 조건이 맞으면 실행할거에요 -> 이미 실행 중인 대사가 있다면
|
|
StopCoroutine(dialogueCoroutine); // 중단할거에요 -> 기존 코루틴을
|
|
|
|
dialogueCoroutine = StartCoroutine(DialogueRoutine(text)); // 코루틴을 시작할거에요 -> 새 대사를 출력하는 DialogueRoutine을
|
|
}
|
|
|
|
private IEnumerator DialogueRoutine(string text) // 코루틴 함수를 선언할거에요 -> 페이드 효과와 함께 대사를 출력하는 DialogueRoutine을
|
|
{
|
|
bossDialogueText.text = text; // 값을 설정할거에요 -> UI 텍스트 내용을 전달받은 text로
|
|
dialogueCanvasGroup.alpha = 0f; // 값을 설정할거에요 -> 투명도를 0(안 보임)으로
|
|
|
|
// 페이드 인
|
|
float t = 0f; // 변수를 초기화할거에요 -> 경과 시간을 0으로
|
|
while (t < dialogueFadeDuration) // 반복할거에요 -> 경과 시간이 페이드 시간보다 작을 동안
|
|
{
|
|
t += Time.deltaTime; // 값을 더할거에요 -> 경과 시간에 프레임 시간을
|
|
dialogueCanvasGroup.alpha = t / dialogueFadeDuration; // 값을 조절할거에요 -> 투명도를 서서히 1로
|
|
yield return null; // 대기할거에요 -> 다음 프레임까지
|
|
}
|
|
dialogueCanvasGroup.alpha = 1f; // 값을 확정할거에요 -> 투명도를 완전 불투명(1)으로
|
|
|
|
// 유지
|
|
yield return new WaitForSeconds(dialogueDisplayDuration); // 기다릴거에요 -> 대사 유지 시간만큼
|
|
|
|
// 페이드 아웃
|
|
t = 0f; // 변수를 초기화할거에요 -> 경과 시간을 다시 0으로
|
|
while (t < dialogueFadeDuration) // 반복할거에요 -> 경과 시간이 페이드 시간보다 작을 동안
|
|
{
|
|
t += Time.deltaTime; // 값을 더할거에요 -> 경과 시간에 프레임 시간을
|
|
dialogueCanvasGroup.alpha = 1f - (t / dialogueFadeDuration); // 값을 조절할거에요 -> 투명도를 서서히 0으로
|
|
yield return null; // 대기할거에요 -> 다음 프레임까지
|
|
}
|
|
dialogueCanvasGroup.alpha = 0f; // 값을 확정할거에요 -> 투명도를 완전 투명(0)으로
|
|
}
|
|
} |