256 lines
20 KiB
C#
256 lines
20 KiB
C#
|
|
using UnityEngine; // 유니티 기능을 불러올거에요 -> UnityEngine을
|
||
|
|
|
||
|
|
// ============================================================
|
||
|
|
// NorcielBoss.SFX.cs — 사운드·이펙트 유틸리티 (partial class)
|
||
|
|
//
|
||
|
|
// [역할]
|
||
|
|
// ① 패턴별 AudioClip SerializeField 선언 (Inspector에서 클립 연결)
|
||
|
|
// ② ParticleSystem SerializeField 선언 (Inspector에서 파티클 연결)
|
||
|
|
// ③ PlayBossSFX / PlayBossVFX 헬퍼 메서드 (null 안전)
|
||
|
|
// ④ OnFootstep — Unity Animation Event 수신 (발소리 랜덤 재생)
|
||
|
|
//
|
||
|
|
// [Inspector 연결 방법]
|
||
|
|
// 각 슬롯에 AudioClip / ParticleSystem 을 드래그해서 연결하세요.
|
||
|
|
// 연결하지 않으면 null 체크로 오류 없이 무시됩니다.
|
||
|
|
//
|
||
|
|
// [ParticleSystem 배치 팁]
|
||
|
|
// - 보스 오브젝트의 자식으로 ParticleSystem 을 만들어두세요.
|
||
|
|
// - 트레일(vfx_Dash_Trail 등)은 보스 발 위치 자식에 배치하면 자연스러워요.
|
||
|
|
// - 임팩트(vfx_Smash_Impact 등)는 위치가 코드로 이동되므로 아무 자식이나 연결해도 됩니다.
|
||
|
|
// ============================================================
|
||
|
|
|
||
|
|
public partial class NorcielBoss : MonsterClass // 부분 클래스를 선언할거에요 -> NorcielBoss의 사운드·이펙트 부분으로
|
||
|
|
{
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
// 사운드 — 상태 / 발소리
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
[Header("=== 보스 사운드 — 상태 ===")]
|
||
|
|
[Tooltip("Phase 전환 포효 사운드 (Roar 애니와 함께 재생)")]
|
||
|
|
[SerializeField] private AudioClip sfx_Roar; // 변수를 선언할거에요 -> 포효 사운드 클립을
|
||
|
|
[Tooltip("포효 볼륨 (0~1)")]
|
||
|
|
[SerializeField][Range(0f, 1f)] private float sfx_Roar_Vol = 1f; // 변수를 선언할거에요 -> 포효 볼륨을
|
||
|
|
|
||
|
|
[Tooltip("숨돌리기(BossRest) 진입 시 숨 고르는 사운드")]
|
||
|
|
[SerializeField] private AudioClip sfx_Rest; // 변수를 선언할거에요 -> 숨돌리기 사운드 클립을
|
||
|
|
[Tooltip("숨돌리기 볼륨 (0~1)")]
|
||
|
|
[SerializeField][Range(0f, 1f)] private float sfx_Rest_Vol = 0.6f; // 변수를 선언할거에요 -> 숨돌리기 볼륨을
|
||
|
|
|
||
|
|
[Tooltip("걷기/달리기 발소리 (여러 개 등록 시 랜덤 재생)\n" +
|
||
|
|
"Monster_Walk / Monster_Run 클립에 Animation Event 'OnFootstep' 을 등록해주세요.")]
|
||
|
|
[SerializeField] private AudioClip[] sfx_Footsteps; // 변수를 선언할거에요 -> 발소리 클립 배열을 (랜덤 선택)
|
||
|
|
[Tooltip("발소리 볼륨 (0~1)")]
|
||
|
|
[SerializeField][Range(0f, 1f)] private float sfx_Footstep_Vol = 0.5f; // 변수를 선언할거에요 -> 발소리 볼륨을
|
||
|
|
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
// 사운드 — 피격 / 사망
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
[Header("=== 보스 사운드 — 피격 / 사망 ===")]
|
||
|
|
[Tooltip("피격 사운드\n보스는 슈퍼아머라 MonsterClass의 hitSound가 재생 안 됩니다.\n여기에 등록하면 TakeDamage에서 별도 재생됩니다.")]
|
||
|
|
[SerializeField] private AudioClip sfx_Hit; // 변수를 선언할거에요 -> 피격 사운드 클립을
|
||
|
|
[Tooltip("피격 볼륨 (0~1)")]
|
||
|
|
[SerializeField][Range(0f, 1f)] private float sfx_Hit_Vol = 0.8f; // 변수를 선언할거에요 -> 피격 볼륨을
|
||
|
|
|
||
|
|
[Tooltip("사망 사운드 (기존 MonsterClass.deathSound와 별개로 추가 재생)")]
|
||
|
|
[SerializeField] private AudioClip sfx_Death; // 변수를 선언할거에요 -> 사망 사운드 클립을
|
||
|
|
[Tooltip("사망 볼륨 (0~1)")]
|
||
|
|
[SerializeField][Range(0f, 1f)] private float sfx_Death_Vol = 1f; // 변수를 선언할거에요 -> 사망 볼륨을
|
||
|
|
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
// 사운드 — 던지기 패턴
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
[Header("=== 보스 사운드 — 던지기 패턴 ===")]
|
||
|
|
[Tooltip("쇠공 들어올리는 예비 모션 사운드")]
|
||
|
|
[SerializeField] private AudioClip sfx_Throw_Windup; // 변수를 선언할거에요 -> 던지기 예비 사운드 클립을
|
||
|
|
[Tooltip("던지기 예비 볼륨 (0~1)")]
|
||
|
|
[SerializeField][Range(0f, 1f)] private float sfx_Throw_Windup_Vol = 0.8f; // 변수를 선언할거에요 -> 던지기 예비 볼륨을
|
||
|
|
|
||
|
|
[Tooltip("쇠공 발사 순간 사운드")]
|
||
|
|
[SerializeField] private AudioClip sfx_Throw_Release; // 변수를 선언할거에요 -> 발사 사운드 클립을
|
||
|
|
[Tooltip("발사 볼륨 (0~1)")]
|
||
|
|
[SerializeField][Range(0f, 1f)] private float sfx_Throw_Release_Vol = 1f; // 변수를 선언할거에요 -> 발사 볼륨을
|
||
|
|
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
// 사운드 — 내려찍기 패턴
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
[Header("=== 보스 사운드 — 내려찍기 패턴 ===")]
|
||
|
|
[Tooltip("내려찍기 팔 드는 예비 모션 사운드")]
|
||
|
|
[SerializeField] private AudioClip sfx_Smash_Windup; // 변수를 선언할거에요 -> 내려찍기 예비 사운드 클립을
|
||
|
|
[Tooltip("내려찍기 예비 볼륨 (0~1)")]
|
||
|
|
[SerializeField][Range(0f, 1f)] private float sfx_Smash_Windup_Vol = 0.7f; // 변수를 선언할거에요 -> 내려찍기 예비 볼륨을
|
||
|
|
|
||
|
|
[Tooltip("내려찍기 임팩트 사운드 (쾅! 소리)")]
|
||
|
|
[SerializeField] private AudioClip sfx_Smash_Impact; // 변수를 선언할거에요 -> 내려찍기 임팩트 사운드 클립을
|
||
|
|
[Tooltip("내려찍기 임팩트 볼륨 (0~1)")]
|
||
|
|
[SerializeField][Range(0f, 1f)] private float sfx_Smash_Impact_Vol = 1f; // 변수를 선언할거에요 -> 내려찍기 임팩트 볼륨을
|
||
|
|
|
||
|
|
[Tooltip("Phase2 충격파 사운드")]
|
||
|
|
[SerializeField] private AudioClip sfx_Smash_Shockwave; // 변수를 선언할거에요 -> 충격파 사운드 클립을
|
||
|
|
[Tooltip("충격파 볼륨 (0~1)")]
|
||
|
|
[SerializeField][Range(0f, 1f)] private float sfx_Smash_Shockwave_Vol = 0.9f; // 변수를 선언할거에요 -> 충격파 볼륨을
|
||
|
|
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
// 사운드 — 휩쓸기 패턴
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
[Header("=== 보스 사운드 — 휩쓸기 패턴 ===")]
|
||
|
|
[Tooltip("팔 휩쓸기 스윙 사운드 (휘익! 소리)")]
|
||
|
|
[SerializeField] private AudioClip sfx_Sweep_Swing; // 변수를 선언할거에요 -> 휩쓸기 스윙 사운드 클립을
|
||
|
|
[Tooltip("휩쓸기 스윙 볼륨 (0~1)")]
|
||
|
|
[SerializeField][Range(0f, 1f)] private float sfx_Sweep_Swing_Vol = 0.8f; // 변수를 선언할거에요 -> 휩쓸기 스윙 볼륨을
|
||
|
|
|
||
|
|
[Tooltip("휩쓸기 히트 사운드 (충격음)")]
|
||
|
|
[SerializeField] private AudioClip sfx_Sweep_Impact; // 변수를 선언할거에요 -> 휩쓸기 히트 사운드 클립을
|
||
|
|
[Tooltip("휩쓸기 히트 볼륨 (0~1)")]
|
||
|
|
[SerializeField][Range(0f, 1f)] private float sfx_Sweep_Impact_Vol = 1f; // 변수를 선언할거에요 -> 휩쓸기 히트 볼륨을
|
||
|
|
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
// 사운드 — 대시 패턴
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
[Header("=== 보스 사운드 — 대시 패턴 ===")]
|
||
|
|
[Tooltip("대시 준비 사운드 (으르렁/숨들이쉬기 등)")]
|
||
|
|
[SerializeField] private AudioClip sfx_Dash_Windup; // 변수를 선언할거에요 -> 대시 준비 사운드 클립을
|
||
|
|
[Tooltip("대시 준비 볼륨 (0~1)")]
|
||
|
|
[SerializeField][Range(0f, 1f)] private float sfx_Dash_Windup_Vol = 0.8f; // 변수를 선언할거에요 -> 대시 준비 볼륨을
|
||
|
|
|
||
|
|
[Tooltip("대시 돌진 중 이동 사운드 (발구르기/바람소리 등)")]
|
||
|
|
[SerializeField] private AudioClip sfx_Dash_Move; // 변수를 선언할거에요 -> 돌진 이동 사운드 클립을
|
||
|
|
[Tooltip("돌진 이동 볼륨 (0~1)")]
|
||
|
|
[SerializeField][Range(0f, 1f)] private float sfx_Dash_Move_Vol = 0.7f; // 변수를 선언할거에요 -> 돌진 이동 볼륨을
|
||
|
|
|
||
|
|
[Tooltip("대시 플레이어 충돌 임팩트 사운드")]
|
||
|
|
[SerializeField] private AudioClip sfx_Dash_Impact; // 변수를 선언할거에요 -> 대시 충돌 사운드 클립을
|
||
|
|
[Tooltip("대시 충돌 볼륨 (0~1)")]
|
||
|
|
[SerializeField][Range(0f, 1f)] private float sfx_Dash_Impact_Vol = 1f; // 변수를 선언할거에요 -> 대시 충돌 볼륨을
|
||
|
|
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
// 이펙트 — 포효
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
[Header("=== 보스 이펙트 — 포효 ===")]
|
||
|
|
[Tooltip("Phase 전환 포효 파티클 (보스 주변 폭발/빛 파티클 등)\n보스 중심 위치에서 재생됩니다.")]
|
||
|
|
[SerializeField] private ParticleSystem vfx_Roar; // 변수를 선언할거에요 -> 포효 파티클을
|
||
|
|
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
// 이펙트 — 던지기 패턴
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
[Header("=== 보스 이펙트 — 던지기 ===")]
|
||
|
|
[Tooltip("쇠공 발사 순간 파티클 (발사 화염/바람 잔상 등)\n발사 시 ironBall 위치로 자동 이동됩니다.")]
|
||
|
|
[SerializeField] private ParticleSystem vfx_Throw_Release; // 변수를 선언할거에요 -> 발사 이펙트 파티클을
|
||
|
|
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
// 이펙트 — 내려찍기 패턴
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
[Header("=== 보스 이펙트 — 내려찍기 ===")]
|
||
|
|
[Tooltip("내려찍기 임팩트 파티클 (땅 충격파/흙먼지 등)\n판정 중심 위치(보스 앞)로 자동 이동됩니다.")]
|
||
|
|
[SerializeField] private ParticleSystem vfx_Smash_Impact; // 변수를 선언할거에요 -> 내려찍기 임팩트 파티클을
|
||
|
|
|
||
|
|
[Tooltip("Phase2 충격파 파티클 (원형 파동 등)\n보스 중심 위치에서 재생됩니다.")]
|
||
|
|
[SerializeField] private ParticleSystem vfx_Smash_Shockwave; // 변수를 선언할거에요 -> 충격파 파티클을
|
||
|
|
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
// 이펙트 — 휩쓸기 패턴
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
[Header("=== 보스 이펙트 — 휩쓸기 ===")]
|
||
|
|
[Tooltip("휩쓸기 팔 트레일 파티클 (바람/불꽃 잔상 등)\n임팩트 타이밍에 재생됩니다.")]
|
||
|
|
[SerializeField] private ParticleSystem vfx_Sweep_Trail; // 변수를 선언할거에요 -> 휩쓸기 트레일 파티클을
|
||
|
|
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
// 이펙트 — 대시 패턴
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
[Header("=== 보스 이펙트 — 대시 ===")]
|
||
|
|
[Tooltip("대시 이동 중 트레일 파티클 (먼지/발자국 등)\n돌진 시작 시 Play → 도착·종료 시 Stop됩니다.\n루프 파티클로 설정해주세요.")]
|
||
|
|
[SerializeField] private ParticleSystem vfx_Dash_Trail; // 변수를 선언할거에요 -> 돌진 트레일 파티클을 (루프)
|
||
|
|
|
||
|
|
[Tooltip("대시 충돌 임팩트 파티클 (폭발/충격파 등)\n플레이어 위치로 자동 이동됩니다.")]
|
||
|
|
[SerializeField] private ParticleSystem vfx_Dash_Impact; // 변수를 선언할거에요 -> 대시 충돌 이펙트 파티클을
|
||
|
|
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
// 이펙트 — 피격
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
[Header("=== 보스 이펙트 — 피격 ===")]
|
||
|
|
[Tooltip("피격 파티클 (피 튀김/불꽃 등)\n기존 visualFX.Flash()와 함께 재생됩니다.\n비워두면 Flash만 재생됩니다.")]
|
||
|
|
[SerializeField] private ParticleSystem vfx_Hit; // 변수를 선언할거에요 -> 피격 파티클을
|
||
|
|
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
// 헬퍼 — 사운드 재생
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
/// <summary>보스 오브젝트의 AudioSource로 효과음 재생 (3D 위치음)</summary>
|
||
|
|
private void PlayBossSFX(AudioClip clip, float volume = 1f) // 함수를 선언할거에요 -> 보스 위치에서 효과음을 재생하는
|
||
|
|
{
|
||
|
|
if (clip == null || audioSource == null) return; // 중단할거에요 -> 클립 또는 AudioSource 없으면
|
||
|
|
audioSource.PlayOneShot(clip, volume); // 재생할거에요 -> 효과음을 (3D 위치 기반)
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>특정 월드 좌표에서 효과음 재생 (임팩트처럼 위치가 다를 때 사용)</summary>
|
||
|
|
private void PlayBossSFXAt(AudioClip clip, Vector3 worldPos, float volume = 1f) // 함수를 선언할거에요 -> 지정 위치에서 효과음을 재생하는
|
||
|
|
{
|
||
|
|
if (clip == null) return; // 중단할거에요 -> 클립 없으면
|
||
|
|
if (SoundManager.Instance != null) // 조건이 맞으면 실행할거에요 -> SoundManager 있으면
|
||
|
|
SoundManager.Instance.PlaySFXAtPosition(clip, worldPos, volume); // 재생할거에요 -> 해당 위치에서 (3D 공간음)
|
||
|
|
else
|
||
|
|
AudioSource.PlayClipAtPoint(clip, worldPos, volume); // 폴백할거에요 -> Unity 기본 방식으로
|
||
|
|
}
|
||
|
|
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
// 헬퍼 — 파티클 재생 / 정지
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
/// <summary>ParticleSystem을 현재 배치된 위치에서 재생 (자식 파티클 포함)</summary>
|
||
|
|
private void PlayBossVFX(ParticleSystem vfx) // 함수를 선언할거에요 -> 파티클을 현재 위치에서 재생하는
|
||
|
|
{
|
||
|
|
if (vfx == null) return; // 중단할거에요 -> 없으면
|
||
|
|
vfx.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear); // 초기화할거에요 -> 이전 잔상을 제거하고
|
||
|
|
vfx.Play(true); // 재생할거에요 -> 자식 파티클 포함해서
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>ParticleSystem을 지정 월드 좌표로 이동한 뒤 재생 (임팩트 위치 이동)</summary>
|
||
|
|
private void PlayBossVFXAt(ParticleSystem vfx, Vector3 worldPos) // 함수를 선언할거에요 -> 파티클을 지정 위치에서 재생하는
|
||
|
|
{
|
||
|
|
if (vfx == null) return; // 중단할거에요 -> 없으면
|
||
|
|
|
||
|
|
// [C-1 수정] 자식 오브젝트는 부모 transform이 매 프레임 월드 위치를 덮어써서
|
||
|
|
// vfx.transform.position = worldPos 가 한 프레임 뒤 부모 기준으로 리셋됨.
|
||
|
|
// → SetParent(null) 로 부모에서 분리한 뒤 이동 → 월드 좌표가 그대로 유지됨
|
||
|
|
vfx.transform.SetParent(null); // 분리할거에요 -> 부모에서 (월드 좌표 이동이 덮어써지지 않게)
|
||
|
|
vfx.transform.position = worldPos; // 이동할거에요 -> 지정 위치로
|
||
|
|
vfx.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear); // 초기화할거에요 -> 이전 잔상을 제거하고
|
||
|
|
vfx.Play(true); // 재생할거에요 -> 자식 파티클 포함해서
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>ParticleSystem 발생 중단 (루프 트레일 종료에 사용)</summary>
|
||
|
|
private void StopBossVFX(ParticleSystem vfx) // 함수를 선언할거에요 -> 파티클을 정지하는 (기존 파티클은 자연 소멸)
|
||
|
|
{
|
||
|
|
if (vfx == null) return; // 중단할거에요 -> 없으면
|
||
|
|
vfx.Stop(true, ParticleSystemStopBehavior.StopEmitting); // 정지할거에요 -> 새 파티클 발생을 (기존 것은 자연 소멸)
|
||
|
|
}
|
||
|
|
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
// Animation Event 수신
|
||
|
|
// ══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Animation Event — 걷기/달리기 클립의 발이 땅에 닿는 프레임에서 호출
|
||
|
|
///
|
||
|
|
/// [등록 방법]
|
||
|
|
/// Unity Animation 창에서 Monster_Walk / Monster_Run 클립을 열고
|
||
|
|
/// 발이 땅에 닿는 프레임에 Function: "OnFootstep" 이벤트를 추가하세요.
|
||
|
|
/// </summary>
|
||
|
|
public void OnFootstep() // 함수를 선언할거에요 -> 발소리 이벤트를 수신하는 (Unity Animation Event)
|
||
|
|
{
|
||
|
|
if (sfx_Footsteps == null || sfx_Footsteps.Length == 0) return; // 중단할거에요 -> 발소리 클립 없으면
|
||
|
|
AudioClip step = sfx_Footsteps[Random.Range(0, sfx_Footsteps.Length)]; // 선택할거에요 -> 랜덤 발소리를
|
||
|
|
PlayBossSFX(step, sfx_Footstep_Vol); // 재생할거에요 -> 발소리를 (보스 위치에서 3D음)
|
||
|
|
}
|
||
|
|
}
|