154 lines
3.6 KiB
C#
154 lines
3.6 KiB
C#
using System.Collections;
|
|
using UnityEngine;
|
|
|
|
public class SpotlightFall : MonoBehaviour
|
|
{
|
|
[Header("떨어질 조명")]
|
|
[SerializeField] private Transform spotlight;
|
|
|
|
[Header("도착 위치")]
|
|
[SerializeField] private Transform fallEndPoint;
|
|
|
|
[Header("낙하 설정")]
|
|
[SerializeField] private float fallDuration = 1f;
|
|
|
|
[Header("충돌 사운드")]
|
|
[SerializeField] private AudioSource impactAudioSource;
|
|
[SerializeField] private AudioClip impactSound;
|
|
|
|
[Range(0f, 1f)]
|
|
[SerializeField] private float impactVolume = 1f;
|
|
|
|
[Tooltip("조명이 도착하기 몇 초 전에 소리를 재생할지")]
|
|
[SerializeField] private float impactSoundLeadTime = 0.12f;
|
|
|
|
private Vector3 startPosition;
|
|
private Quaternion startRotation;
|
|
|
|
private bool hasFallen;
|
|
private bool isFalling;
|
|
|
|
private void Awake()
|
|
{
|
|
if (spotlight != null)
|
|
{
|
|
startPosition = spotlight.position;
|
|
startRotation = spotlight.rotation;
|
|
}
|
|
}
|
|
|
|
public void DropSpotlight()
|
|
{
|
|
if (hasFallen || isFalling)
|
|
return;
|
|
|
|
if (spotlight == null)
|
|
{
|
|
Debug.LogWarning(
|
|
"[SpotlightFall] Spotlight가 연결되지 않았습니다."
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (fallEndPoint == null)
|
|
{
|
|
Debug.LogWarning(
|
|
"[SpotlightFall] Fall End Point가 연결되지 않았습니다."
|
|
);
|
|
return;
|
|
}
|
|
|
|
hasFallen = true;
|
|
StartCoroutine(FallRoutine());
|
|
}
|
|
|
|
private IEnumerator FallRoutine()
|
|
{
|
|
isFalling = true;
|
|
|
|
Vector3 fallStartPosition = spotlight.position;
|
|
Vector3 targetPosition = fallEndPoint.position;
|
|
|
|
float duration = Mathf.Max(0.01f, fallDuration);
|
|
float elapsed = 0f;
|
|
|
|
bool impactSoundPlayed = false;
|
|
|
|
while (elapsed < duration)
|
|
{
|
|
elapsed += Time.deltaTime;
|
|
|
|
float t = Mathf.Clamp01(elapsed / duration);
|
|
|
|
// 처음에는 느리고 아래로 갈수록 빠르게 낙하
|
|
float acceleratedT = t * t * t;
|
|
|
|
spotlight.position = Vector3.Lerp(
|
|
fallStartPosition,
|
|
targetPosition,
|
|
acceleratedT
|
|
);
|
|
|
|
// 완전히 도착하기 조금 전에 와장창 소리 재생
|
|
if (!impactSoundPlayed &&
|
|
duration - elapsed <= impactSoundLeadTime)
|
|
{
|
|
impactSoundPlayed = true;
|
|
PlayImpactSound();
|
|
}
|
|
|
|
yield return null;
|
|
}
|
|
|
|
// 마지막 위치 정확히 맞추기
|
|
spotlight.position = targetPosition;
|
|
|
|
// Lead Time이 너무 작아 소리가 실행되지 않았을 경우 대비
|
|
if (!impactSoundPlayed)
|
|
{
|
|
PlayImpactSound();
|
|
}
|
|
|
|
isFalling = false;
|
|
}
|
|
|
|
private void PlayImpactSound()
|
|
{
|
|
if (impactAudioSource == null || impactSound == null)
|
|
return;
|
|
|
|
impactAudioSource.PlayOneShot(
|
|
impactSound,
|
|
impactVolume
|
|
);
|
|
}
|
|
|
|
public void ResetFall()
|
|
{
|
|
StopAllCoroutines();
|
|
|
|
hasFallen = false;
|
|
isFalling = false;
|
|
|
|
if (impactAudioSource != null)
|
|
impactAudioSource.Stop();
|
|
|
|
if (spotlight != null)
|
|
{
|
|
spotlight.position = startPosition;
|
|
spotlight.rotation = startRotation;
|
|
}
|
|
}
|
|
|
|
[ContextMenu("Test Fall")]
|
|
private void TestFall()
|
|
{
|
|
DropSpotlight();
|
|
}
|
|
|
|
[ContextMenu("Reset Fall")]
|
|
private void TestResetFall()
|
|
{
|
|
ResetFall();
|
|
}
|
|
} |