Merge branch 'main' of https://www.nakjungit.site/sharedacc520k/WhaleAdventure_VR
This commit is contained in:
@@ -38,6 +38,36 @@ public class ClamOpenClose : MonoBehaviour
|
||||
[Tooltip("쾅 하고 닫힌 뒤 원래 닫힌 위치로 돌아오는 시간입니다.")]
|
||||
[SerializeField] private float snapCloseOvershootDuration = 0.02f;
|
||||
|
||||
[Header("Sound")]
|
||||
[SerializeField] private AudioSource audioSource;
|
||||
|
||||
[Tooltip("조개가 닫히며 입이 부딪힐 때 재생할 사운드입니다. 여러 개를 넣으면 랜덤 재생됩니다.")]
|
||||
[SerializeField] private AudioClip[] closeImpactSounds;
|
||||
|
||||
[Tooltip("조개가 닫히기 시작할 때 재생할 예고/마찰 사운드입니다. 필요 없으면 비워두세요.")]
|
||||
[SerializeField] private AudioClip closeStartSound;
|
||||
|
||||
[Range(0f, 1f)]
|
||||
[SerializeField] private float closeImpactVolume = 1f;
|
||||
|
||||
[Range(0f, 1f)]
|
||||
[SerializeField] private float closeStartVolume = 0.5f;
|
||||
|
||||
[Tooltip("닫힘 충격음의 최소 피치입니다.")]
|
||||
[SerializeField] private float minImpactPitch = 0.9f;
|
||||
|
||||
[Tooltip("닫힘 충격음의 최대 피치입니다.")]
|
||||
[SerializeField] private float maxImpactPitch = 1.1f;
|
||||
|
||||
[Tooltip("닫힘 충격음의 볼륨을 살짝 랜덤하게 줄지 여부입니다.")]
|
||||
[SerializeField] private bool randomizeImpactVolume = true;
|
||||
|
||||
[Tooltip("볼륨 랜덤 적용 시 최소 배율입니다.")]
|
||||
[SerializeField] private float minImpactVolumeMultiplier = 0.85f;
|
||||
|
||||
[Tooltip("볼륨 랜덤 적용 시 최대 배율입니다.")]
|
||||
[SerializeField] private float maxImpactVolumeMultiplier = 1.0f;
|
||||
|
||||
[Header("Start Option")]
|
||||
[SerializeField] private bool startAutomatically = true;
|
||||
[SerializeField] private bool startOpened = false;
|
||||
@@ -74,6 +104,17 @@ private void Awake()
|
||||
if (found != null) downShell = found;
|
||||
}
|
||||
|
||||
if (audioSource == null)
|
||||
{
|
||||
audioSource = GetComponent<AudioSource>();
|
||||
}
|
||||
|
||||
if (audioSource != null)
|
||||
{
|
||||
audioSource.playOnAwake = false;
|
||||
audioSource.loop = false;
|
||||
}
|
||||
|
||||
if (upShell == null)
|
||||
{
|
||||
Debug.LogWarning("[ClamOpenClose] UpShell이 연결되지 않았습니다.", this);
|
||||
@@ -145,6 +186,7 @@ private IEnumerator OpenCloseRoutine()
|
||||
yield return new WaitForSeconds(openWait);
|
||||
|
||||
isClosing = true;
|
||||
PlayCloseStartSound();
|
||||
onCloseStarted?.Invoke();
|
||||
|
||||
yield return MoveShells(
|
||||
@@ -158,6 +200,10 @@ private IEnumerator OpenCloseRoutine()
|
||||
|
||||
isOpen = false;
|
||||
|
||||
// 실제 입이 맞닿는 타이밍.
|
||||
// 랜덤으로 닫혀도 항상 닫힘 동작이 끝난 직후 실행됨.
|
||||
PlayCloseImpactSound();
|
||||
|
||||
if (useSnapClose)
|
||||
{
|
||||
yield return SnapCloseEffect();
|
||||
@@ -225,6 +271,47 @@ private IEnumerator SnapCloseEffect()
|
||||
downShell.localRotation = downClosedRot;
|
||||
}
|
||||
|
||||
private void PlayCloseStartSound()
|
||||
{
|
||||
if (audioSource == null || closeStartSound == null)
|
||||
return;
|
||||
|
||||
audioSource.pitch = 1f;
|
||||
audioSource.PlayOneShot(closeStartSound, closeStartVolume);
|
||||
}
|
||||
|
||||
private void PlayCloseImpactSound()
|
||||
{
|
||||
if (audioSource == null)
|
||||
return;
|
||||
|
||||
if (closeImpactSounds == null || closeImpactSounds.Length == 0)
|
||||
return;
|
||||
|
||||
AudioClip clip = closeImpactSounds[Random.Range(0, closeImpactSounds.Length)];
|
||||
|
||||
if (clip == null)
|
||||
return;
|
||||
|
||||
float originalPitch = audioSource.pitch;
|
||||
|
||||
audioSource.pitch = Random.Range(minImpactPitch, maxImpactPitch);
|
||||
|
||||
float volume = closeImpactVolume;
|
||||
|
||||
if (randomizeImpactVolume)
|
||||
{
|
||||
float volumeMultiplier = Random.Range(minImpactVolumeMultiplier, maxImpactVolumeMultiplier);
|
||||
volume *= volumeMultiplier;
|
||||
}
|
||||
|
||||
audioSource.PlayOneShot(clip, volume);
|
||||
|
||||
// PlayOneShot은 재생 직후 피치를 바꿔도 이미 재생 중인 소리에 영향이 적지만,
|
||||
// 다음 사운드 재생을 위해 기본 피치로 복구.
|
||||
audioSource.pitch = originalPitch;
|
||||
}
|
||||
|
||||
private void SetClosedImmediately()
|
||||
{
|
||||
if (upShell != null)
|
||||
|
||||
@@ -23,6 +23,24 @@ public class FallingStalactite : MonoBehaviour
|
||||
[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;
|
||||
|
||||
@@ -38,6 +56,7 @@ public class FallingStalactite : MonoBehaviour
|
||||
private Vector3 startPosition;
|
||||
private Quaternion startRotation;
|
||||
private bool hasFallen;
|
||||
private bool hasPlayedSound;
|
||||
private Coroutine fallRoutine;
|
||||
|
||||
public bool HasFallen => hasFallen;
|
||||
@@ -62,6 +81,9 @@ private void ResolveReferences()
|
||||
|
||||
if (damageObstacle == null)
|
||||
damageObstacle = GetComponent<DamageObstacle>();
|
||||
|
||||
if (audioSource == null)
|
||||
audioSource = GetComponent<AudioSource>();
|
||||
}
|
||||
|
||||
private void PrepareStalactite()
|
||||
@@ -85,7 +107,21 @@ private void PrepareStalactite()
|
||||
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()
|
||||
@@ -106,6 +142,8 @@ private IEnumerator FallRoutine()
|
||||
if (fallDelay > 0f)
|
||||
yield return new WaitForSeconds(fallDelay);
|
||||
|
||||
PlayFallSound();
|
||||
|
||||
if (damageObstacle != null)
|
||||
{
|
||||
damageObstacle.SetDamage(damage);
|
||||
@@ -137,6 +175,71 @@ private IEnumerator FallRoutine()
|
||||
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)
|
||||
@@ -161,6 +264,11 @@ public void ResetStalactite()
|
||||
hasFallen = false;
|
||||
}
|
||||
|
||||
if (playSoundEveryFall)
|
||||
{
|
||||
hasPlayedSound = false;
|
||||
}
|
||||
|
||||
if (showDebugLog)
|
||||
Debug.Log($"[FallingStalactite] {name} 원위치 리셋", this);
|
||||
}
|
||||
|
||||
158
Assets/02_Scripts/Cave/SteeringKeyGrabMusic.cs
Normal file
158
Assets/02_Scripts/Cave/SteeringKeyGrabMusic.cs
Normal file
@@ -0,0 +1,158 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.XR.Interaction.Toolkit;
|
||||
using UnityEngine.XR.Interaction.Toolkit.Interactables;
|
||||
|
||||
public class SteeringKeyGrabMusic : MonoBehaviour
|
||||
{
|
||||
[Header("References")]
|
||||
[SerializeField] private XRGrabInteractable grabInteractable;
|
||||
[SerializeField] private AudioSource audioSource;
|
||||
|
||||
[Header("Playback")]
|
||||
[SerializeField] private bool playOnGrab = true;
|
||||
[SerializeField] private bool stopOnRelease = true;
|
||||
|
||||
[Tooltip("키를 잡았을 때 음악을 처음부터 다시 재생할지 여부입니다.")]
|
||||
[SerializeField] private bool restartFromBeginningOnGrab = false;
|
||||
|
||||
[Header("Fade")]
|
||||
[SerializeField] private bool useFade = true;
|
||||
[SerializeField] private float fadeInDuration = 0.4f;
|
||||
[SerializeField] private float fadeOutDuration = 0.5f;
|
||||
|
||||
[Range(0f, 1f)]
|
||||
[SerializeField] private float targetVolume = 1f;
|
||||
|
||||
[Header("Debug")]
|
||||
[SerializeField] private bool showDebugLog = true;
|
||||
|
||||
private Coroutine fadeRoutine;
|
||||
private bool isGrabbed;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (grabInteractable == null)
|
||||
grabInteractable = GetComponentInChildren<XRGrabInteractable>(true);
|
||||
|
||||
if (audioSource == null)
|
||||
audioSource = GetComponent<AudioSource>();
|
||||
|
||||
if (audioSource != null)
|
||||
{
|
||||
audioSource.playOnAwake = false;
|
||||
audioSource.loop = true;
|
||||
|
||||
if (useFade)
|
||||
audioSource.volume = 0f;
|
||||
else
|
||||
audioSource.volume = targetVolume;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (grabInteractable != null)
|
||||
{
|
||||
grabInteractable.selectEntered.AddListener(OnKeyGrabbed);
|
||||
grabInteractable.selectExited.AddListener(OnKeyReleased);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (grabInteractable != null)
|
||||
{
|
||||
grabInteractable.selectEntered.RemoveListener(OnKeyGrabbed);
|
||||
grabInteractable.selectExited.RemoveListener(OnKeyReleased);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnKeyGrabbed(SelectEnterEventArgs args)
|
||||
{
|
||||
isGrabbed = true;
|
||||
|
||||
if (!playOnGrab)
|
||||
return;
|
||||
|
||||
if (audioSource == null)
|
||||
{
|
||||
if (showDebugLog)
|
||||
Debug.LogWarning("[SteeringKeyGrabMusic] AudioSource가 연결되지 않았습니다.", this);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (restartFromBeginningOnGrab)
|
||||
audioSource.time = 0f;
|
||||
|
||||
if (!audioSource.isPlaying)
|
||||
audioSource.Play();
|
||||
|
||||
if (useFade)
|
||||
StartFade(targetVolume, fadeInDuration);
|
||||
else
|
||||
audioSource.volume = targetVolume;
|
||||
|
||||
if (showDebugLog)
|
||||
Debug.Log("[SteeringKeyGrabMusic] 키 잡음. 음악 재생.", this);
|
||||
}
|
||||
|
||||
private void OnKeyReleased(SelectExitEventArgs args)
|
||||
{
|
||||
isGrabbed = false;
|
||||
|
||||
if (!stopOnRelease)
|
||||
return;
|
||||
|
||||
if (audioSource == null)
|
||||
return;
|
||||
|
||||
if (useFade)
|
||||
StartFade(0f, fadeOutDuration, stopAfterFade: true);
|
||||
else
|
||||
audioSource.Stop();
|
||||
|
||||
if (showDebugLog)
|
||||
Debug.Log("[SteeringKeyGrabMusic] 키 놓음. 음악 정지.", this);
|
||||
}
|
||||
|
||||
private void StartFade(float target, float duration, bool stopAfterFade = false)
|
||||
{
|
||||
if (fadeRoutine != null)
|
||||
StopCoroutine(fadeRoutine);
|
||||
|
||||
fadeRoutine = StartCoroutine(FadeRoutine(target, duration, stopAfterFade));
|
||||
}
|
||||
|
||||
private IEnumerator FadeRoutine(float target, float duration, bool stopAfterFade)
|
||||
{
|
||||
if (audioSource == null)
|
||||
yield break;
|
||||
|
||||
float startVolume = audioSource.volume;
|
||||
float timer = 0f;
|
||||
float safeDuration = Mathf.Max(0.01f, duration);
|
||||
|
||||
while (timer < safeDuration)
|
||||
{
|
||||
timer += Time.deltaTime;
|
||||
|
||||
float t = Mathf.Clamp01(timer / safeDuration);
|
||||
float smoothT = t * t * (3f - 2f * t);
|
||||
|
||||
audioSource.volume = Mathf.Lerp(startVolume, target, smoothT);
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
audioSource.volume = target;
|
||||
|
||||
if (stopAfterFade && !isGrabbed)
|
||||
{
|
||||
audioSource.Stop();
|
||||
}
|
||||
|
||||
fadeRoutine = null;
|
||||
}
|
||||
}
|
||||
2
Assets/02_Scripts/Cave/SteeringKeyGrabMusic.cs.meta
Normal file
2
Assets/02_Scripts/Cave/SteeringKeyGrabMusic.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3a3e3221faafc724c82b5b177b84072e
|
||||
@@ -66,8 +66,10 @@ public void Relocate(GameObject target, Transform destination)
|
||||
|
||||
if (target.TryGetComponent(out NavMeshAgent agent) && agent.isOnNavMesh)
|
||||
{
|
||||
Debug.Log($"엔드포지션 : {destination.position}");
|
||||
agent.Warp(destination.position);
|
||||
target.transform.rotation = destination.rotation;
|
||||
Debug.Log($"이동한포지션 : {target.transform.position}");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user