The question is answered, right answer was accepted
How to stop a sound playing once another starts? (C#)
I have a prefab for an object (let's call it chord object) that plays a looping musical chord sound as the player passes through it (on collision). When the player collides with another of these objects, a new chord is played. What I'd like to know how to do is check if a chord loop is already playing and if so, stop that sound playing so that only one chord object (the most recent chord object the player has collided with) plays a sound at a time. I'm using C# and my script attached to the chord object looks like this (it updates the attached audio source component). Currently chords will play back on top of each other in a hideous cacophony, because there's nothing telling them to turn off:
void OnTriggerEnter(Collider other) { //Debug.Log (x);
if (other.gameObject.tag == "collisionTank") {//If tank collides with this
Debug.Log ("Play chord");
chordSource.Play ();
}
}
Answer by Le-Pampelmuse · Nov 14, 2015 at 09:08 PM
I suggest having an AudioSource reference stored somewhere called currentAudio
that is accessible from each of the audioSources' scripts.
Like an emtpy GameObject that has a script attached where a public AudioSource currentAudio
exists.
Then you add a reference to that script into the prefab's script and write something like:
public AudioManager otherScript;
void OnTriggerEnter(Collider other)
{
if(other.transform.tag == "collisionTank")
{
if(otherScript.currentAudio != null) // If we already have a chord playing..
{
otherScript.currentAudio.Stop(); // stop the currently playing Chord and..
chordSource.Play(); // start the new one, that THIS object has attached..
otherScript.currentAudio = chordSource; // tell the logic that THIS is playing!
}
else
{ //If this is the FIRST collider we entered..
chordSource.Play(); // ..start the chord, that THIS object has attached..
otherScript.currentAudio = chordSource; // ..assign the audiosource for later!
}
}
}
And in the AudioManager
script you would have:
public class AudioManager : MonoBehaviour{
public AudioSource currentAudio;
void Start()
{}
}
I hope this is what you were looking for, it just stops the current audio and immediately starts the next one. You could also fade out quickly, altough this might also sound horrible for a split second since they are chords.
PS: Since you are using Prefabs, I would recommend calling the AudioManager's GameObject "AudioLogic" or something thats easy to identify and at the start of the prefab add:
public AudioManager otherScript; //this doesnt need to be assigned by you anymore
void Start()
{
otherScript = GameObject.Find("AudioLogic").getComponent<AudioManager>();
}
This automatically finds the AudioLogic GameObject and gets the AudioManager script from it, so you don't have to assign it for 100 Prefabs by hand. Also when using Prefabs, it would not work to save references they have in the inspector.
Hey, thanks! I'm yet to try this out but reading through it, it does seem like an appropriate solution in theory. There are a few concepts that are new to me so I'll need to actually try implementing this before I can fully get my head round it.
Firstly, is "if(otherScript.currentAudio != null)" another way of saying if(is not playing)? What makes something == null? I get null reference errors a lot, even when things are working properly, so it'd be good to know more about this.
Secondly, am I able to use GameObject.Find() in any script for any object in my project to save me the trouble of dragging and dropping object references into scripts where I've created an unspecified public GameObject?
Hopefully I've understood these thingS correctly.
Checking if something is or is not "null" means the following code should only start to execute when the object ( here the AudioSource "currentAudio") is assigned to "something" if you don't add this check and you try to execute line 9 in my example:
otherScript.currentAudio.Stop(); this would give you a "nullreference error" because it "refers" to an object that is not assigned at the time the line is called, it is "null", it doesn't exist for the script.
Generally speaking, being null is the same as if you didn't drag an object into a Inspector slot. The Value is there, but "nothing" is assigned to it.
Here a simple walkthrough what happens in the script:
Scene starts
We have triggers in the scene but the player has not yet touched any of them, so there can be no
currentAudio
that is playingAs soon as we touch any trigger, we only make it play the audio, we don't need to stop any other audio because currentAudio tells us that there "is no" AudioSource, it is
null
at the $$anonymous$$oment.
We additionally assign the new, the first audio to thatcurrentAudio
value. Now currentAudio holds a reference to the new audio that is playing, it is now not "null" anymore. You can watch this happen in the Inspector. It means we can access the audio later on, when we touch the next trigger.Now we touch the next trigger, and because we have a currentAudio, because it isn't null, we need to
.Stop()
that from playing before we make the new one.Play().
In the same scope where we said we play the new audio, we need to also change the currentAudio to match that new audio, so we can .Stop() it the next time we touch a new trigger. This code will work without any other further action for as long as the scene is running and for every trigger you might have that has the right tag.
=> http://docs.unity3d.com/ScriptReference/GameObject.Find.html Yes, you definitely are able to do that, and you definitely should NOT do that :) GameObject.Find is a very demanding process, you should only use it where you can't assign object in the Inspector before the game starts. For example when you Instantiate a prefab, you can't assign them by hand because the game is already running.
That's what I was talking about in my comment. When adding a Prefab into the scene, it does not save the references the original object had. So the Audio$$anonymous$$anager would always be a Null Reference Exception when you don't use GameObject.Find in the ChordObject.
I'm not going to explain Nullreference Exceptions since there is enough Documentation about that and this forum is only for questions that can't be answered by tutorials or documentation. You might want to read the FAQ about that, so you can find a solution easier the next time you have a problem. http://docs.unity3d.com/$$anonymous$$anual/NullReferenceException.html http://answers.unity3d.com/page/faq.html
Hope this could help you.
Hey - thanks a lot. This is probably one of the most helpful forum replies I've ever had. I eventually got the scripts working, although there are a couple of things about how it works that are still making my head spin. I'll get it eventually =]
If you're still there, you mentioned being able to do fade outs on sounds ins$$anonymous$$d of just stopping them? I'm getting pops and clicks just now so that may not be such a bad idea. There will be other musical layers (notes, arpeggios, ambient sounds etc) that should mask the fades anyway.
No problem, I treat people and their questions the same way I want them to treat me. ;)
Yes there are some ways to fade audio volumes, but since that is a very common asked question I suggest you search the forum because:
Your question has been answered (please accept the answer, so it is obvious to people that this question is answered before they click on it).
Additionally you may close the question, this makes it easier for moderators because they don't have to do it then ;)How to fade audio would be another question, but please don't ask "how to fade audio" in a new question ;)
This is a classic example:
Original question: How to stop audio when other audio starts.
New question as a result: How to fade audio out.
You should google "unity how to fade out audio" for that. I give you a hint, you don't need yield or anything fancy:
Define a condition when to fade out ( most likely a bool)
Decrease the volume over time using
Time.deltaTime
When it is 0 stop decreasing it, because it can't be decreased anymore.
For example:
using UnityEngine;
using System.Collections;
public class FadeAudio : $$anonymous$$onoBehaviour {
public AudioSource audio;
public bool FadeNow;
public float Time$$anonymous$$ultiplier = 5f; //How fast we should fade out.
void Update()
{
if(FadeNow)
{
if(audio.volume > 0)
{
//The slower we want to fade, the smaller Time$$anonymous$$ultiplier would be.
audio.volume -= Time.deltaTime * Time$$anonymous$$ultiplier;
}
else
{
/*If it is down to 0, we don't have to let the script decrement it anymore.
Otherwise it would still try to decrement it, which is useless*/
audio.volume = 0f;
}
}
else
{
audio.volume = audio.volume; // $$anonymous$$ake sure it doesnt change, just in case.
}
}
}
If you still have any questions, you will find the answers via Google ;) Hope I could help again.