Projext/Assets/02_Scripts/Enemy/BossAI/BossPhaseManager.cs
hydrozen e989d20668 카툰 쉐이더 추가 + 중복 스크립트 수정 + 전체 업데이트
- ToonPostProcess.shader: 횃불 고딕 스타일 후처리 쉐이더 (Built-in RP)
- ToonCameraEffect.cs: 카메라 자동 부착 후처리 스크립트
- 중복 UI 스크립트 제거 (MenuIntroController, ToggleCustom)
- 씬, 프리팹, 애니메이션 등 전체 업데이트

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 12:31:16 +09:00

239 lines
18 KiB
C#

using UnityEngine; // 유니티 기능을 불러올거에요 -> UnityEngine을
using System.Collections; // 코루틴 기능을 불러올거에요 -> IEnumerator를
/// <summary>
/// 보스 페이즈 관리 컴포넌트 — HP 기반 페이즈 전환 + 스탯 배율 제공
/// NorcielBoss에서 분리되어 페이즈 전환 로직과 배율 계산만 전담합니다.
/// </summary>
public class BossPhaseManager : MonoBehaviour
{
// ══════════════════════════════════════════════════════════
// Phase2 설정
// ══════════════════════════════════════════════════════════
[Header("=== Phase2 ===")]
[Tooltip("HP 이 비율 이하면 Phase2 진입 (0.5 = 50%)")] // 설명할거에요 -> Phase2 HP 비율 임계값을
[SerializeField][Range(0f, 1f)] private float phase2HpRatio = 0.5f; // 변수를 선언할거에요 -> Phase2 전환 HP 비율을
[Tooltip("Phase2 공격 간격 배율 (0.65 = 35% 단축)")] // 설명할거에요 -> Phase2 간격 배율 설명을
[SerializeField] private float phase2IntervalMult = 0.65f; // 변수를 선언할거에요 -> Phase2 간격 배율을
[Tooltip("Phase2 데미지 배율")] // 설명할거에요 -> Phase2 데미지 배율 설명을
[SerializeField] private float phase2DamageMult = 1.4f; // 변수를 선언할거에요 -> Phase2 데미지 배율을
[Tooltip("Phase2 이동 속도 배율")] // 설명할거에요 -> Phase2 이동 속도 배율 설명을
[SerializeField] private float phase2SpeedMult = 1.3f; // 변수를 선언할거에요 -> Phase2 이동 속도 배율을
// ══════════════════════════════════════════════════════════
// Phase3 (광폭화) 설정 — HP 25% 이하
// ══════════════════════════════════════════════════════════
[Header("=== Phase3 (광폭화) ===")]
[Tooltip("HP 이 비율 이하면 Phase3 진입 (0.25 = 25%)")] // 설명할거에요 -> Phase3 HP 비율 임계값을
[SerializeField][Range(0f, 1f)] private float phase3HpRatio = 0.25f; // 변수를 선언할거에요 -> Phase3 전환 HP 비율을
[Tooltip("Phase3 patternInterval 배율 (0.45 = 55% 단축)\nPhase2 배율 위에 추가로 곱해져요")] // 설명할거에요 -> Phase3 간격 배율 설명을
[SerializeField] private float phase3IntervalMult = 0.45f; // 변수를 선언할거에요 -> Phase3 간격 배율을
[Tooltip("Phase3 데미지 배율 (1.8 = 80% 증가)")] // 설명할거에요 -> Phase3 데미지 배율 설명을
[SerializeField] private float phase3DamageMult = 1.8f; // 변수를 선언할거에요 -> Phase3 데미지 배율을
[Tooltip("Phase3 이동 속도 배율 (Phase2 위에 추가)")] // 설명할거에요 -> Phase3 이동 속도 배율 설명을
[SerializeField] private float phase3SpeedMult = 1.5f; // 변수를 선언할거에요 -> Phase3 이동 속도 배율을
[Tooltip("Phase3 원거리에서 대시+스매시 콤보 확률 (0.7 = 70%)")] // 설명할거에요 -> Phase3 콤보 대시 확률 설명을
[SerializeField][Range(0f, 1f)] private float phase3DashSmashChance = 0.7f; // 변수를 선언할거에요 -> Phase3 콤보 대시 확률을
// ══════════════════════════════════════════════════════════
// 콤보 체인 설정 — 패턴 연속 발동
// ══════════════════════════════════════════════════════════
[Header("=== 콤보 체인 (패턴 연계) ===")]
[Tooltip("Phase1 콤보 확률 (0 = 콤보 안 나옴)")] // 설명할거에요 -> Phase1 콤보 확률 설명을
[SerializeField][Range(0f, 1f)] private float comboChance = 0f; // 변수를 선언할거에요 -> Phase1 콤보 확률을 (기본 0 = 없음)
[Tooltip("Phase2 콤보 확률 (0.35 = 35%)")] // 설명할거에요 -> Phase2 콤보 확률 설명을
[SerializeField][Range(0f, 1f)] private float phase2ComboChance = 0.35f; // 변수를 선언할거에요 -> Phase2 콤보 확률을
[Tooltip("Phase3 콤보 확률 (0.55 = 55%)")] // 설명할거에요 -> Phase3 콤보 확률 설명을
[SerializeField][Range(0f, 1f)] private float phase3ComboChance = 0.55f; // 변수를 선언할거에요 -> Phase3 콤보 확률을
[Tooltip("최대 연속 콤보 횟수 (이 수에 도달하면 강제 경직)\n추천: 2~4")] // 설명할거에요 -> 최대 콤보 횟수 설명을
[SerializeField][Range(1, 6)] private int maxComboCount = 3; // 변수를 선언할거에요 -> 최대 연속 콤보 횟수를
[Tooltip("콤보 간 짧은 숨 고르기 시간 (초)\n추천: 0.15~0.3")] // 설명할거에요 -> 콤보 간 딜레이 설명을
[SerializeField] private float comboDelay = 0.2f; // 변수를 선언할거에요 -> 콤보 간 딜레이를
// ══════════════════════════════════════════════════════════
// 경직/쿨타임 설정 — Phase별 경직 배율
// ══════════════════════════════════════════════════════════
[Header("=== 경직/쿨타임 ===")]
[Tooltip("기본 경직 시간 (초)")] // 설명할거에요 -> 기본 경직 시간 설명을
[SerializeField] private float recoverTime = 0.5f; // 변수를 선언할거에요 -> 기본 경직 시간을
[Tooltip("Phase2 경직 배율 (0.6 = 40% 단축)")] // 설명할거에요 -> Phase2 경직 배율 설명을
[SerializeField] private float phase2RecoverMult = 0.6f; // 변수를 선언할거에요 -> Phase2 경직 배율을
[Tooltip("Phase3 경직 배율 (0.35 = 65% 단축)")] // 설명할거에요 -> Phase3 경직 배율 설명을
[SerializeField] private float phase3RecoverMult = 0.35f; // 변수를 선언할거에요 -> Phase3 경직 배율을
// ══════════════════════════════════════════════════════════
// 런타임 상태 변수
// ══════════════════════════════════════════════════════════
private bool _isPhase2 = false; // 변수를 선언할거에요 -> 현재 Phase2 상태 여부를
private bool _phase2Done = false; // 변수를 선언할거에요 -> Phase2 이미 발동 여부를 (중복 방지)
private bool _isPhase3 = false; // 변수를 선언할거에요 -> 현재 Phase3 상태 여부를
private bool _phase3Done = false; // 변수를 선언할거에요 -> Phase3 이미 발동 여부를 (중복 방지)
// ══════════════════════════════════════════════════════════
// 공개 이벤트 및 게터
// ══════════════════════════════════════════════════════════
/// <summary>페이즈 변경 이벤트 (int = 페이즈 번호 2 또는 3)</summary> // 설명할거에요 -> 페이즈 변경 이벤트를
public event System.Action<int> OnPhaseChanged; // 이벤트를 선언할거에요 -> 페이즈 변경 콜백을
/// <summary>현재 Phase2 상태 여부</summary> // 설명할거에요 -> Phase2 상태 읽기 전용 속성을
public bool IsPhase2 => _isPhase2; // 속성을 선언할거에요 -> Phase2 상태를 (읽기전용)
/// <summary>현재 Phase3 상태 여부</summary> // 설명할거에요 -> Phase3 상태 읽기 전용 속성을
public bool IsPhase3 => _isPhase3; // 속성을 선언할거에요 -> Phase3 상태를 (읽기전용)
/// <summary>최대 콤보 횟수</summary> // 설명할거에요 -> 최대 콤보 횟수 속성을
public int MaxComboCount => maxComboCount; // 속성을 선언할거에요 -> 최대 콤보 횟수를 (읽기전용)
/// <summary>콤보 간 딜레이</summary> // 설명할거에요 -> 콤보 간 딜레이 속성을
public float ComboDelay => comboDelay; // 속성을 선언할거에요 -> 콤보 간 딜레이를 (읽기전용)
/// <summary>Phase3에서의 대시+스매시 콤보 확률</summary> // 설명할거에요 -> Phase3 대시스매시 확률 속성을
public float Phase3DashSmashChance => phase3DashSmashChance; // 속성을 선언할거에요 -> Phase3 대시스매시 확률을 (읽기전용)
// ══════════════════════════════════════════════════════════
// 배율 계산 메서드들 — 현재 페이즈에 따라 올바른 값 반환
// ══════════════════════════════════════════════════════════
/// <summary>현재 페이즈에 적용할 공격 간격 배율을 반환해요</summary> // 설명할거에요 -> 간격 배율 게터 함수를
public float GetIntervalMult() // 함수를 선언할거에요 -> 페이즈별 간격 배율을 반환하는
{
// 조건이 맞으면 실행할거에요 -> Phase3 중이면
if (_isPhase3) return phase3IntervalMult; // 반환할거에요 -> Phase3 간격 배율을
// 조건이 맞으면 실행할거에요 -> Phase2 중이면
if (_isPhase2) return phase2IntervalMult; // 반환할거에요 -> Phase2 간격 배율을
// 반환할거에요 -> 기본값 1배수를
return 1f; // 반환할거에요 -> 배율 1 (원본)
}
/// <summary>현재 페이즈에 적용할 데미지 배율을 반환해요</summary> // 설명할거에요 -> 데미지 배율 게터 함수를
public float GetDamageMult() // 함수를 선언할거에요 -> 페이즈별 데미지 배율을 반환하는
{
// 조건이 맞으면 실행할거에요 -> Phase3 중이면
if (_isPhase3) return phase3DamageMult; // 반환할거에요 -> Phase3 데미지 배율을
// 조건이 맞으면 실행할거에요 -> Phase2 중이면
if (_isPhase2) return phase2DamageMult; // 반환할거에요 -> Phase2 데미지 배율을
// 반환할거에요 -> 기본값 1배수를
return 1f; // 반환할거에요 -> 배율 1 (원본)
}
/// <summary>현재 페이즈에 적용할 이동 속도 배율을 반환해요</summary> // 설명할거에요 -> 이동 속도 배율 게터 함수를
public float GetSpeedMult() // 함수를 선언할거에요 -> 페이즈별 이동 속도 배율을 반환하는
{
// 조건이 맞으면 실행할거에요 -> Phase3 중이면
if (_isPhase3) return phase3SpeedMult; // 반환할거에요 -> Phase3 이동 속도 배율을
// 조건이 맞으면 실행할거에요 -> Phase2 중이면
if (_isPhase2) return phase2SpeedMult; // 반환할거에요 -> Phase2 이동 속도 배율을
// 반환할거에요 -> 기본값 1배수를
return 1f; // 반환할거에요 -> 배율 1 (원본)
}
/// <summary>현재 페이즈에 적용할 경직 배율을 반환해요</summary> // 설명할거에요 -> 경직 배율 게터 함수를
public float GetRecoverMult() // 함수를 선언할거에요 -> 페이즈별 경직 배율을 반환하는
{
// 조건이 맞으면 실행할거에요 -> Phase3 중이면
if (_isPhase3) return phase3RecoverMult; // 반환할거에요 -> Phase3 경직 배율을
// 조건이 맞으면 실행할거에요 -> Phase2 중이면
if (_isPhase2) return phase2RecoverMult; // 반환할거에요 -> Phase2 경직 배율을
// 반환할거에요 -> 기본값 1배수를
return 1f; // 반환할거에요 -> 배율 1 (원본)
}
/// <summary>기본 경직 시간을 반환해요</summary> // 설명할거에요 -> 기본 경직 시간 게터 함수를
public float GetRecoverTime() => recoverTime; // 함수를 선언할거에요 -> 기본 경직 시간을 반환하는
/// <summary>현재 페이즈에 적용할 콤보 확률을 반환해요</summary> // 설명할거에요 -> 콤보 확률 게터 함수를
public float GetComboChance() // 함수를 선언할거에요 -> 페이즈별 콤보 확률을 반환하는
{
// 조건이 맞으면 실행할거에요 -> Phase3 중이면
if (_isPhase3) return phase3ComboChance; // 반환할거에요 -> Phase3 콤보 확률을
// 조건이 맞으면 실행할거에요 -> Phase2 중이면
if (_isPhase2) return phase2ComboChance; // 반환할거에요 -> Phase2 콤보 확률을
// 반환할거에요 -> 기본값 콤보 확률을
return comboChance; // 반환할거에요 -> Phase1 콤보 확률을 (기본 0)
}
/// <summary>현재 페이즈에서 대시+스매시 콤보 확률을 반환해요 (기본값 포함)</summary> // 설명할거에요 -> 대시스매시 확률 계산 함수를
public float GetDashSmashChance(float baseDashSmashChance) // 함수를 선언할거에요 -> 기본 대시스매시 확률을 받아서 페이즈별로 조정하는
{
// 조건이 맞으면 실행할거에요 -> Phase3 중이면
if (_isPhase3) return phase3DashSmashChance; // 반환할거에요 -> Phase3 고정값을
// 조건이 맞으면 실행할거에요 -> Phase2 중이면
if (_isPhase2) return Mathf.Clamp01(baseDashSmashChance * 1.5f); // 반환할거에요 -> 기본값의 1.5배 (상한선 1)을
// 반환할거에요 -> 기본값을
return baseDashSmashChance; // 반환할거에요 -> Phase1 기본값을
}
// ══════════════════════════════════════════════════════════
// 페이즈 전환 로직
// ══════════════════════════════════════════════════════════
/// <summary>매 프레임 호출. HP 비율 확인 후 페이즈 전환 이벤트를 발생시켜요</summary> // 설명할거에요 -> HP 기반 페이즈 체크 함수를
public void CheckPhases(float currentHp, float maxHp) // 함수를 선언할거에요 -> 현재/최대 HP로 페이즈를 확인하는
{
// 조건이 맞으면 실행할거에요 -> maxHP 유효하지 않으면
if (maxHp <= 0f) return; // 중단할거에요 -> 계산을 중지하고
float ratio = currentHp / maxHp; // 계산할거에요 -> HP 비율을 (0.0 ~ 1.0)
// ─────────────── Phase2 체크 ─────────────────
// 조건이 맞으면 실행할거에요 -> Phase2가 아직 발동 안 됐고 HP 기준 이하면
if (!_phase2Done && ratio <= phase2HpRatio) // 조건을 확인할거에요 -> Phase2 전환 조건을
{
_phase2Done = true; // 설정할거에요 -> Phase2 이미 발동으로
OnPhaseChanged?.Invoke(2); // 호출할거에요 -> Phase2 변경 이벤트를 (리스너들이 처리)
}
// ─────────────── Phase3 체크 ─────────────────
// 조건이 맞으면 실행할거에요 -> Phase3이 아직 발동 안 됐고, Phase2 진입했고, HP 기준 이하면
if (!_phase3Done && _isPhase2 && ratio <= phase3HpRatio) // 조건을 확인할거에요 -> Phase3 전환 조건을
{
_phase3Done = true; // 설정할거에요 -> Phase3 이미 발동으로
OnPhaseChanged?.Invoke(3); // 호출할거에요 -> Phase3 변경 이벤트를 (리스너들이 처리)
}
}
// ══════════════════════════════════════════════════════════
// 페이즈 상태 설정 메서드
// ══════════════════════════════════════════════════════════
/// <summary>Phase2 진입을 확정해요 (보스가 포효 연출 완료 후 호출)</summary> // 설명할거에요 -> Phase2 확정 함수를
public void ConfirmPhase2() // 함수를 선언할거에요 -> Phase2 진입을 확정하는
{
_isPhase2 = true; // 설정할거에요 -> Phase2 활성 상태로
}
/// <summary>Phase3 진입을 확정해요 (보스가 포효 연출 완료 후 호출)</summary> // 설명할거에요 -> Phase3 확정 함수를
public void ConfirmPhase3() // 함수를 선언할거에요 -> Phase3 진입을 확정하는
{
_isPhase3 = true; // 설정할거에요 -> Phase3 활성 상태로
}
/// <summary>전투 초기화 (보스 전투 시작 시 호출)</summary> // 설명할거에요 -> 전투 초기화 함수를
public void InitializeBattle() // 함수를 선언할거에요 -> 모든 페이즈 상태를 초기화하는
{
_isPhase2 = false; // 초기화할거에요 -> Phase2 비활성 상태로
_phase2Done = false; // 초기화할거에요 -> Phase2 발동 플래그를 (재발동 허용)
_isPhase3 = false; // 초기화할거에요 -> Phase3 비활성 상태로
_phase3Done = false; // 초기화할거에요 -> Phase3 발동 플래그를 (재발동 허용)
}
}