Hello. I’m building a system of trigger with a script that have 2 behaviours:
- OnTriggerEnter: the audio-clip (music) plays, and the volume increase from 0 to 1 in 2 seconds.
- OnTriggerExit: the audio-clip decrease the volume from 1 to 0 in 2 seconds, then pause.
Everything works just fine, until i “break the system” by doing 2 things
- I exit the trigger before the volume arrived to 1 (right after entering)
- I enter the trigger before the volume arrived to 0 (right after exiting)
I’m sure this is because the Coroutine is broken on midle of operation. What happend is that the volume stay stuck at the level it was when i entered / exited the trigger while the coroutine is playing the precedent fade effect.
Here is the code:
using UnityEngine;
using System.Collections;
public class VolumeFadeTrigger : MonoBehaviour
{
public AudioSource source;
public AudioClip clip;
public void Awake()
{
source = GetComponent<AudioSource> ();
}
public void OnTriggerEnter(Collider other)
{
StartCoroutine ("FadeIn");
}
public void OnTriggerExit(Collider other)
{
StartCoroutine ("FadeOut");
}
IEnumerator FadeOut()
{
while (source.volume > 0.01f)
{
source.volume -= Time.deltaTime / 2.0f;
yield return null;
}
source.volume = 0;
source.Pause();
}
IEnumerator FadeIn()
{
source.Play ();
while (source.volume < 0.99f)
{
source.volume += Time.deltaTime / 2.0f;
yield return null;
}
source.volume = 1;
}
}
Any idea how i can fix this ?
(if the volume is actually fading in, and i exit the trigger before it ends, it will just take it from there and fade out naturally.)
The problem is that your two coroutines work with the same variable, in your case with source.volume. While one coroutine decrements the value, the other increments the value. So you never reach any of the desired values.
There are many ways how you can solve this. The most easiest way is to use a local variable instead
for(float v = source.volume; v > 0f; v -=Time.deltaTime / 2.0f)
{
source.volume = v;
yield return null;
}
If you design your two coroutines like this each will be ensured to finish as they work with a local variable and no other code can change the local variable. Though if two coroutines run at the same time the actual volume value would bounce between the current values of those coroutines.
A better way would be to simply stop the opposite coroutine when you start a new one. Since you use the string version of StartCoroutine you can simply use StopCoroutine(“FadeOut”) when you start FadeIn and the other way round.
When not using the string version you can store the current “Coroutine” object in a variable. It can be used to stop the old coroutine. This is probably the best solution:
Coroutine current = null;
public void OnTriggerEnter(Collider other)
{
if (current != null)
StopCoroutine(current);
current = StartCoroutine (FadeIn());
}
public void OnTriggerExit(Collider other)
{
if (current != null)
StopCoroutine(current);
current = StartCoroutine (FadeOut());
}
IEnumerator FadeOut()
{
for (float v = source.volume; v > 0f; v -= Time.deltaTime / 2.0f)
{
source.volume = v;
yield return null;
}
source.volume = 0;
source.Pause();
current = null;
}
IEnumerator FadeIn()
{
source.Play ();
for (float v = source.volume; v < 1f; v += Time.deltaTime / 2.0f)
{
source.volume = v;
yield return null;
}
source.volume = 1f;
current = null;
}
I still didn’t find any way to resolve the issue, and see my question already lost under hundreds of other questions, so i’m really sorry to post again for up.