Как сделать звук смерти после смерти объекта?
У меня есть объект Ship с событием Died и методом Die. Есть объект ShipAudio, который отлавливает различные события у Ship и проигрывает соответствующие звуки через AudioSource.
Метод Die просто деактивирует объект и инвоукает событие.
ShipAudio, отловив Died, должен проигрывать звук взрыва. Но он этого не делает, из-за того, что ShipAudio прикреплён к тому же объекту, что и Ship, поэтому SetActive скрывает их обоих.
Кто бы что сделал с этой ситуацией? Можно ShipAudio расположить на другом объекте, но что если громкость звука в игре меняется в зависимости от удалённости Ship? Тогда удобнее, чтобы Ship и ShipAudio находились на одном объекте.
Код:
public class Ship : MonoBehavior
{
public UnityEvent Died;
public void Die()
{
gameObject.SetActive(false);
Died?.Invoke();
}
}
public class ShipAudio : MonoBehaviour
{
[SerializeField] private Ship _ship;
[SerializeField] private AudioClip _explosionAudio;
private AudioSource _source;
private void OnEnable()
{
_ship.Died.AddListener(OnDied);
}
private void OnDisable()
{
_ship.Died.RemoveListener(OnDied);
}
private void OnDied()
{
_source.clip = _explosionAudio;
_source.Play();
}
}
Ответы (1 шт):
Если ShipAudio расположен на том же объекте, который выключает в момент наступления OnDie() - то проигрывание аудио будет сразу остановлено. Как вариант
Выключать объект после окончания проигрывания. Выключать в этом случае можно либо в методе (ниже в примере это будет корутина), вызывающем _source.Play(),
//public class Ship
public void Die()
{
Died?.Invoke();
}
//class ShipAudio
private void OnDied()
{
StartCoroutine(PlaySoundAndDoAction());
}
private IEnumerator PlaySoundAndDoAction()
{
_source.clip = _explosionAudio;
_source.Play();
yield return new WaitForSeconds(_explosionAudio.lenght);
gameObject.SetActive(false);
}
либо передавать Action в Action, и уже из класса Ship решать, что делать дальше, например так:
//public class Ship
Action<Action> Died;
public void Die()
{
Died?.Invoke(()=> OnDieSoundComplete());
}
private void OnDieSoundComplete()
{
gameObject.SetActive(false);
}
//class ShipAudio
private void OnDied(Action onComplete = null)
{
StartCoroutine(PlaySoundAndDoAction(onComplete));
}
private IEnumerator PlaySoundAndDoAction(Action onComplete = null)
{
_source.clip = _explosionAudio;
_source.Play();
yield return new WaitForSeconds(_explosionAudio.lenght);
if(onComplete !=null)
onComplete?.Invoke();
}
Еще, как вариант - можно скомбинировать костыль: ShipAudio повесить на объект пустышку, который будет дочерним Ship. Когда Ship погибает - то объекту с ShipAudio менять родителя на null. После проигрывания аудио - либо уничтожать объект пустышку, либо возращать его как дочерний объекту с Ship, если планируется его повторное использование (судя потому, что он в момент смерти SetActive(false), а не Destroy()).