- Home /
Change music between scenes
I have two music clips, one for the menu and one for the in game level, I would like those music clips to be played on specific scenes, so when the game starts and the menu is displayed, I want one song to play and then when you change to a level scene, it turns off the menu music and turns on the level music and then, when you change from the level scene to the menu scene, the music changes again. I have made a script, for this but the problem is that it works in the unity editor, but when I build the game for iPhone the music won't start when you change from level scene to menu scene.
My script:
static var TurnOffMenuMusic : boolean = false;
static var pauseUnPauseBut : boolean = false;
var menuMusic : AudioSource;
function Start () {
if (TurnOffMenuMusic == false) {
audio.Play();
} else if (TurnOffMenuMusic == true) {
audio.Pause();
}
}
function Update () {
if (TurnOffMenuMusic == false) {
DontDestroyOnLoad(transform.gameObject);
} else if (TurnOffMenuMusic == true) {
Destroy(this.gameObject);
}
}
And then to activate the boolean I have this script.
var targetScript : MenuMusicOnandOff;
function Awake() {
targetScript.TurnOffMenuMusic = true;
}
And then I have a singleton to make sure the music object won't duplicate.
private static var instance:MyUnitySingleton;
public static function GetInstance(): MyUnitySingleton {
return instance;
}
function Awake() {
if (instance != null && instance != this) {
Destroy(this.gameObject);
return;
} else {
instance = this;
}
DontDestroyOnLoad(this.gameObject);
}
if you want to have music change upon entering a level, I'd change your approach a bit. (i'd combine the scripts too calling it "music Player" or something.. just to make things a bit easier to work with).
then every time you call Aplication.LoadLevel, you want to access your $$anonymous$$usic-player Singleton then call a function to load your new music for that particular level for example this code
Application.LoadLevel("Level1"); //I call my level change
$$anonymous$$usicPlayer.instance.Change$$anonymous$$usic("Level1"); //now change my music
that way you could have many levels each with ts own set of music. just make sure you call that function EVERY time you change scenes
I appreciate your answer, but I think what I am looking for, is more like a way to stop and start the audio source, when a button is clicked.
stop and start the AudioSource? when you change music then call Play() on the audio source this stop the old music and start playing the new music. is that what you are after?
whether if its on a button click is irrelevant... the button click just runs the event(in this case a change of music)... it isn't part of it thus doesn't really matter... from what i can gather.. one of your menu's buttons calls a scene change right? well that's when you change the music
sorry if i misunderstood you
I think fornoreason1000 proposed a good solution. I'm going to propose a snippet based on his idea to make things perfectly clear. But I also noticed several coding style points that seem very ugly to me in your code. I'd make some suggestions if you allow me:
You write "if (booleanVariable == true) {bla bla ...} else if (booleanVariable == false) {bla bla ...}". A boolean variable is always either true or false by definition, so you could write a more concise and readable code by simply putting "if (booleanVariable == true) {bla bla ...} else {bla bla ...}". This is the convention in use everywhere afaik.
DontDestroyOnLoad whould not be put in Update() but in Awake or Start if your script must survive "forever". Calling it once is sufficient.
Having code in Update running each loop to check the value of a boolean field (which is a form of so-called active waiting) is bad practice for at least two reasons. First it consumes CPU resources (well, not that much here, but if you do it in multiple places or with costly operations it will slow down your program). Second it makes your code more complicated and error-prone, with lots of stuff in Update. A better way to do it is to have a method in your music player class which is in charge of changing the music, and to call this method il lieu of setting a boolean field.
Answer by graslany · Nov 26, 2014 at 08:35 AM
This is a possible solution to the original problem following on Fornoreason1000's suggestion in the original post comments. You can use a music player script designed along these lines:
using UnityEngine;
using System.Collections;
/// <summary>
/// This script is in charge of playing music in the game
/// </summary>
public class MusicPlayer : MonoBehaviour
{
/// <summary>
/// The clip to play in a menu.
/// This field is private because it's not designed to be directly
/// modified by other scripts, and tagged with [SerializeField] so that
/// you can still modify it using the Inspector and so that Unity
/// saves its value.
/// </summary>
[SerializeField]
private AudioClip menuMusic;
/// <summary>
/// The clip to play outside menus.
/// </summary>
[SerializeField]
private AudioClip levelMusic;
[SerializeField]
/// <summary>
/// The component that plays the music
/// </summary>
private AudioSource source;
/// <summary>
/// This class follows the singleton pattern and this is its instance
/// </summary>
static private MusicPlayer instance;
/// <summary>
/// Awake is not public because other scripts have no reason to call it directly,
/// only the Unity runtime does (and it can call protected and private methods).
/// It is protected virtual so that possible subclasses may perform more specific
/// tasks in their own Awake and still call this base method (It's like constructors
/// in object-oriented languages but compatible with Unity's component-based stuff.
/// </summary>
protected virtual void Awake() {
// Singleton enforcement
if (instance == null) {
// Register as singleton if first
instance = this;
DontDestroyOnLoad(this);
} else {
// Self-destruct if another instance exists
Destroy(this);
return;
}
}
protected virtual void Start() {
// If the game starts in a menu scene, play the appropriate music
PlayMenuMusic();
}
/// <summary>
/// Plays the music designed for the menus
/// This method is static so that it can be called from anywhere in the code.
/// </summary>
static public void PlayMenuMusic ()
{
if (instance != null) {
if (instance.source != null) {
instance.source.Stop();
instance.source.clip = instance.menuMusic;
instance.source.Play();
}
} else {
Debug.LogError("Unavailable MusicPlayer component");
}
}
/// <summary>
/// Plays the music designed for outside menus
/// This method is static so that it can be called from anywhere in the code.
/// </summary>
static public void PlayGameMusic ()
{
if (instance != null) {
if (instance.source != null) {
instance.source.Stop();
instance.source.clip = instance.levelMusic;
instance.source.Play();
}
} else {
Debug.LogError("Unavailable MusicPlayer component");
}
}
}
Then wherever you need to change music in a script (in a button click callback, an Awake method or whatever), instead of setting a boolean value that will be checked in Update, you simply put
MusicPlayer.PlayMenuMusic();
or
MusicPlayer.PlayLevelMusic();
I use a similar architecture in my current project. To change the music, we have another script called GameControle that stays alive between scenes (using DontDestroyOnLoad). In its OnLevelWasLoaded callback it calls the music player to change its current clip (and it does also other stuff that needs to be done when the level changes). It's better in my opinion than having a script for each scene in charge of changing the music, because everything related to music changes on level loading is kept in one place and therefore easier to maintain.
Thank you very much for your answer, it is much appreciated.
@graslany How would you change the game music to one that holds more music than just the one, so:
private AudioClip[] level$$anonymous$$usic;
I tried because your script worked better than $$anonymous$$e and I wanted to take your idea and adapt it but i'm fairly new to unity. I know it's also quite a while since the last post but worth a shot in asking.
You could use:
private AudioClip[] level$$anonymous$$usic;
Then edit the function PlayGame$$anonymous$$usic():
static public void PlayGame$$anonymous$$usic (int clipCount)
instance.source.clip = instance.level$$anonymous$$usic[clipCount];
Now you call the function and hand over an int value of the clip you want to use just like this:
$$anonymous$$usicPlayer.PlayGame$$anonymous$$usic(0); //Play clip 1