Files
WhaleAdventure_VR/Assets/02_Scripts/Cave/FallingStalactite.cs
2026-06-24 14:40:39 +09:00

275 lines
7.3 KiB
C#

using System.Collections;
using UnityEngine;
public class FallingStalactite : MonoBehaviour
{
[Header("References")]
[SerializeField] private Rigidbody rb;
[SerializeField] private Collider damageCollider;
[SerializeField] private DamageObstacle damageObstacle;
[Header("Fall Settings")]
[SerializeField] private float fallDelay = 0.0f;
[Tooltip("떨어질 때 아래 방향으로 추가 속도를 줍니다.")]
[SerializeField] private float initialDownVelocity = 0f;
[Tooltip("떨어질 때 약간 회전시키고 싶으면 값을 넣습니다.")]
[SerializeField] private Vector3 initialAngularVelocity = new Vector3(0f, 0f, 0f);
[Header("Damage")]
[SerializeField] private int damage = 10;
[Tooltip("떨어지기 전에는 데미지를 끄고, 떨어질 때 켭니다.")]
[SerializeField] private bool damageOnlyWhileFalling = true;
[Header("Sound")]
[SerializeField] private AudioSource audioSource;
[Tooltip("종유석이 떨어지기 시작할 때 재생할 소리입니다.")]
[SerializeField] private AudioClip fallSound;
[Range(0f, 1f)]
[SerializeField] private float fallSoundVolume = 1f;
[Tooltip("리셋 후 다시 떨어질 때도 소리를 재생할지 여부입니다.")]
[SerializeField] private bool playSoundEveryFall = true;
[Tooltip("Play through the shared 2D SFX pool when available so the fall cue is always audible.")]
[SerializeField] private bool playThroughSoundManager = true;
[Tooltip("Force the fallback AudioSource to 2D playback.")]
[SerializeField] private bool playFallSoundAs2D = true;
[Header("Reset Option")]
[SerializeField] private bool resetAfterFall = true;
[Tooltip("떨어진 뒤 몇 초 후 원래 위치로 돌아갈지 설정합니다.")]
[SerializeField] private float resetDelay = 4.0f;
[Tooltip("리셋할 때 종유석을 다시 숨기지 않고 원위치에 고정합니다.")]
[SerializeField] private bool readyAgainAfterReset = true;
[Header("Debug")]
[SerializeField] private bool showDebugLog = true;
private Vector3 startPosition;
private Quaternion startRotation;
private bool hasFallen;
private bool hasPlayedSound;
private Coroutine fallRoutine;
public bool HasFallen => hasFallen;
private void Awake()
{
ResolveReferences();
startPosition = transform.position;
startRotation = transform.rotation;
PrepareStalactite();
}
private void ResolveReferences()
{
if (rb == null)
rb = GetComponent<Rigidbody>();
if (damageCollider == null)
damageCollider = GetComponent<Collider>();
if (damageObstacle == null)
damageObstacle = GetComponent<DamageObstacle>();
if (audioSource == null)
audioSource = GetComponent<AudioSource>();
}
private void PrepareStalactite()
{
if (rb != null)
{
rb.useGravity = false;
rb.isKinematic = true;
rb.linearVelocity = Vector3.zero;
rb.angularVelocity = Vector3.zero;
}
if (damageObstacle != null)
{
damageObstacle.SetDamage(damage);
damageObstacle.SetCanDamage(!damageOnlyWhileFalling);
}
if (damageCollider != null)
{
damageCollider.enabled = true;
}
if (audioSource != null)
{
audioSource.playOnAwake = false;
audioSource.mute = false;
if (playFallSoundAs2D)
audioSource.spatialBlend = 0f;
}
PreloadFallSound();
hasFallen = false;
if (playSoundEveryFall)
hasPlayedSound = false;
}
public void TriggerFall()
{
if (hasFallen)
return;
if (fallRoutine != null)
StopCoroutine(fallRoutine);
fallRoutine = StartCoroutine(FallRoutine());
}
private IEnumerator FallRoutine()
{
hasFallen = true;
if (fallDelay > 0f)
yield return new WaitForSeconds(fallDelay);
PlayFallSound();
if (damageObstacle != null)
{
damageObstacle.SetDamage(damage);
damageObstacle.SetCanDamage(true);
}
if (rb != null)
{
rb.isKinematic = false;
rb.useGravity = true;
if (initialDownVelocity > 0f)
{
rb.linearVelocity = Vector3.down * initialDownVelocity;
}
rb.angularVelocity = initialAngularVelocity;
}
if (showDebugLog)
Debug.Log($"[FallingStalactite] {name} 낙하 시작. 데미지 {damage}", this);
if (resetAfterFall)
{
yield return new WaitForSeconds(resetDelay);
ResetStalactite();
}
fallRoutine = null;
}
private AudioClip GetFallClip()
{
if (fallSound != null)
return fallSound;
if (audioSource != null)
return audioSource.clip;
return null;
}
private void PreloadFallSound()
{
AudioClip clipToLoad = GetFallClip();
if (clipToLoad != null && clipToLoad.loadState == AudioDataLoadState.Unloaded)
clipToLoad.LoadAudioData();
}
private void PlayFallSound()
{
if (hasPlayedSound && !playSoundEveryFall)
return;
AudioClip clipToPlay = GetFallClip();
if (clipToPlay == null)
{
if (showDebugLog)
Debug.LogWarning($"[FallingStalactite] {name} Fall Sound가 연결되지 않았습니다.", this);
return;
}
if (clipToPlay.loadState == AudioDataLoadState.Unloaded)
clipToPlay.LoadAudioData();
if (playThroughSoundManager && SoundManager.Instance != null)
{
SoundManager.Instance.PlaySFX(clipToPlay, fallSoundVolume);
}
else if (audioSource != null)
{
audioSource.enabled = true;
audioSource.mute = false;
audioSource.playOnAwake = false;
if (audioSource.volume <= 0f)
audioSource.volume = 1f;
if (playFallSoundAs2D)
audioSource.spatialBlend = 0f;
audioSource.PlayOneShot(clipToPlay, fallSoundVolume);
}
else
{
AudioSource.PlayClipAtPoint(clipToPlay, transform.position, fallSoundVolume);
}
hasPlayedSound = true;
if (showDebugLog)
Debug.Log($"[FallingStalactite] {name} 낙하 사운드 재생: {clipToPlay.name}", this);
}
public void ResetStalactite()
{
if (rb != null)
{
rb.linearVelocity = Vector3.zero;
rb.angularVelocity = Vector3.zero;
rb.useGravity = false;
rb.isKinematic = true;
}
transform.position = startPosition;
transform.rotation = startRotation;
if (damageObstacle != null)
{
damageObstacle.SetDamage(damage);
damageObstacle.SetCanDamage(!damageOnlyWhileFalling);
}
if (readyAgainAfterReset)
{
hasFallen = false;
}
if (playSoundEveryFall)
{
hasPlayedSound = false;
}
if (showDebugLog)
Debug.Log($"[FallingStalactite] {name} 원위치 리셋", this);
}
}