How can I disable the audio for a specific scene? - c#

I'm using this line :
AudioListener.pause = true;
The problem is that I have two scenes loaded, I'm loading another scene with the
LoadSceneMode.Additive
Then the audio pausing is effecting both scenes.
This is my full code for pausing/resuming the game. Index 0 is the Main Menu scene and index 1 is the Game scene. I have two scenes the Main Menu and the Game.
When I hit the escape key to the main menu I want to pause the audio in the Game scene and then when hitting the escape ley again back to the game I want to resume the audio in the game scene.
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class BackToMainMenu : MonoBehaviour
{
public GameObject[] objsToDisable;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
if (Time.timeScale == 0)
{
DisableEnableUiTexts(true);
AudioListener.pause = false;
SceneManager.UnloadSceneAsync(0);
Cursor.visible = false;
Time.timeScale = 1;
}
else
{
Time.timeScale = 0;
MenuController.LoadSceneForSavedGame = false;
AudioListener.pause = true;
SceneManager.LoadScene(0, LoadSceneMode.Additive);
SceneManager.sceneLoaded += SceneManager_sceneLoaded;
Cursor.visible = true;
}
}
}
private void SceneManager_sceneLoaded(Scene arg0, LoadSceneMode arg1)
{
DisableEnableUiTexts(false);
}
private void DisableEnableUiTexts(bool enabled)
{
foreach (GameObject go in objsToDisable)
{
if (go.name == "Cameras")
{
foreach(Transform child in go.transform)
{
if(child.name == "Main Camera")
{
if (enabled == false)
{
child.GetComponent<Camera>().enabled = false;
}
else
{
child.GetComponent<Camera>().enabled = true;
}
}
}
}
else
{
go.SetActive(enabled);
}
}
}
}

As mentioned one possibility is to use AudioSource.mute but of course you'd have to use it on all sources of according scene.
When you already know the index you can do e.g.
var scene = SceneManager.GetSceneAtBuildIndex(1);
if(scene.isValid)
{
// Iterate through the scene Hierarchy and get all sources (also inactive/disabled ones)
var audioSources = new List<AudioSource>();
foreach (var root in scene.GetRootGameObjects())
{
audioSources.AddRange(root.GetComponentsInChildren<AudioSource>(true));
}
foreach(var source in audioSources)
{
source.mute = !enabled;
//Or
source.pause = !enabled;
}
}
Alternatively you could use the AudioMixer which I'd say is more convenient for this.
Here would simply but all the sources into a certain AudioMixerGroup and make the volume an exposed parameter.
Then you can simply silent that entire group by using SetFloat.
Btw be very careful with naming your method parameters like already existing fields/properties! Yes, they will be shadowed anyway but it only leads to confusion for you and other readers. There already is Behavior.enabled which MonoBehavior and thus your class derives from.

Can you try using audio_source_name.mute = true &/or audio_source_name.Stop()?
Example:
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
if (Time.timeScale == 0)
{
DisableEnableUiTexts(true);
// AudioListener.pause = true;
audio_source_name.mute = false; // <--------
SceneManager.UnloadSceneAsync(0);
Cursor.visible = false;
Time.timeScale = 1;
}
else
{
Time.timeScale = 0;
MenuController.LoadSceneForSavedGame = false;
// AudioListener.pause = true;
audio_source_name.mute = false; // <--------
SceneManager.LoadScene(0, LoadSceneMode.Additive);
SceneManager.sceneLoaded += SceneManager_sceneLoaded;
Cursor.visible = true;
}
}
}
You could also do it this way (Source: Unity - Scripting API: AudioSource.mute):
// Mutes-Unmutes the sound from this object each time the user presses space.
using UnityEngine;
using System.Collections;
public class ExampleClass : MonoBehaviour
{
AudioSource audioSource;
void Start()
{
audioSource = GetComponent<AudioSource>();
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
audioSource.mute = !audioSource.mute;
}
}

Related

change scene upon reaching specific score

I'm very new to unity and coding so I have some doubt here. I'm current creating a multiple choice quiz game. can someone help w the coding for my question. My question here is I need to change scene upon reaching certain point. for example, when my score hits 30 I want winner page to appear and when my score hits 0 I want you lose page to appear. how?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class AnswerButtons : MonoBehaviour {
public GameObject answerAbackBlue; // blue is waiting
public GameObject answerAbackGreen; // green is correct
public GameObject answerAbackRed; // red is wrong answer
public GameObject answerBbackBlue; // blue is waiting
public GameObject answerBbackGreen; // green is correct
public GameObject answerBbackRed; // red is wrong answer
public GameObject answerCbackBlue; // blue is waiting
public GameObject answerCbackGreen; // green is correct
public GameObject answerCbackRed; // red is wrong answer
public GameObject answerDbackBlue; // blue is waiting
public GameObject answerDbackGreen; // green is correct
public GameObject answerDbackRed; // red is wrong answer
public GameObject answerA;
public GameObject answerB;
public GameObject answerC;
public GameObject answerD;
public AudioSource CorrectFX;
public AudioSource WrongFX;
public GameObject currentScore;
public int scoreValue;
void Update() {
currentScore.GetComponent<Text>().text = "SCORE: " + scoreValue;
if (scoreValue > 300)
SceneManager.LoadScene("WinnerPage");
if (scoreValue < 50)
SceneManager.LoadScene("LoserPage");
}
public void AnswerA() {
if (QuestionGenarate.actualAnswer == "A") {
answerAbackGreen.SetActive(true);
answerAbackBlue.SetActive(false);
CorrectFX.Play();
scoreValue += 10;
} else {
answerAbackRed.SetActive(true);
answerAbackBlue.SetActive(false);
WrongFX.Play();
scoreValue -= 5;
}
answerA.GetComponent<Button>().enabled = false;
answerB.GetComponent<Button>().enabled = false;
answerC.GetComponent<Button>().enabled = false;
answerD.GetComponent<Button>().enabled = false;
StartCoroutine(NextQuestion());
}
public void AnswerB() {
if (QuestionGenarate.actualAnswer == "B") {
answerBbackGreen.SetActive(true);
answerBbackBlue.SetActive(false);
CorrectFX.Play();
scoreValue += 10;
} else {
answerBbackRed.SetActive(true);
answerBbackBlue.SetActive(false);
WrongFX.Play();
scoreValue -= 5;
}
answerA.GetComponent<Button>().enabled = false;
answerB.GetComponent<Button>().enabled = false;
answerC.GetComponent<Button>().enabled = false;
answerD.GetComponent<Button>().enabled = false;
StartCoroutine(NextQuestion());
}
public void AnswerC() {
if (QuestionGenarate.actualAnswer == "C") {
answerCbackGreen.SetActive(true);
answerCbackBlue.SetActive(false);
CorrectFX.Play();
scoreValue += 10;
} else {
answerCbackRed.SetActive(true);
answerCbackBlue.SetActive(false);
WrongFX.Play();
scoreValue -= 5;
}
answerA.GetComponent<Button>().enabled = false;
answerB.GetComponent<Button>().enabled = false;
answerC.GetComponent<Button>().enabled = false;
answerD.GetComponent<Button>().enabled = false;
StartCoroutine(NextQuestion());
}
public void AnswerD() {
if (QuestionGenarate.actualAnswer == "D") {
answerDbackGreen.SetActive(true);
answerDbackBlue.SetActive(false);
CorrectFX.Play();
scoreValue += 10;
} else {
answerDbackRed.SetActive(true);
answerDbackBlue.SetActive(false);
WrongFX.Play();
scoreValue -= 5;
}
answerA.GetComponent<Button>().enabled = false;
answerB.GetComponent<Button>().enabled = false;
answerC.GetComponent<Button>().enabled = false;
answerD.GetComponent<Button>().enabled = false;
StartCoroutine(NextQuestion());
}
IEnumerator NextQuestion() {
yield return new WaitForSeconds(2);
answerAbackGreen.SetActive(false);
answerBbackGreen.SetActive(false);
answerCbackGreen.SetActive(false);
answerDbackGreen.SetActive(false);
answerAbackRed.SetActive(false);
answerBbackRed.SetActive(false);
answerCbackRed.SetActive(false);
answerDbackRed.SetActive(false);
answerAbackBlue.SetActive(false);
answerBbackBlue.SetActive(false);
answerCbackBlue.SetActive(false);
answerDbackBlue.SetActive(false);
answerA.GetComponent<Button>().enabled = true;
answerB.GetComponent<Button>().enabled = true;
answerC.GetComponent<Button>().enabled = true;
answerD.GetComponent<Button>().enabled = true;
QuestionGenarate.displayingQuestion = false;
}
}
It is hard to answer because you didn't show us the exact code where you count scores. But I will try to help you as much as I can.
Enter your code where you count scores (where you have that variable that stores scores). Now, above the code, among other "using" statements add
using UnityEngine.SceneManagement;
So you just added a scene manager into your script to have control over scenes. Now inside your Update() function add
if (score<0)
{
SceneManager.LoadScene("LoserPage"); //player lost
}
if (score>29)
{
SceneManager.LoadScene("WinnerPage"); //player won
}
In the code above score is a variable that stores your scores. Change that to whatever you have that does it. LoserPage and WinnderPage are the names of the scenes that you have created. Change their names to whatever you have to make it work. Don't forget to add your scenes in Build settings in the correct order.

How can I check if a scene is loaded don't load it again?

Line number 53 in the else :
SceneManager.LoadScene(0, LoadSceneMode.Additive);
I want that if this scene 0 is already loaded with any other scene in my case there are two scenes only for now but if scene 0 the main menu is already loaded Additive then don't load it again when clicking the escape key.
This script sits in Game scene 1
The problem is if in the main menu I click for a new game and the game scene has loaded but before it removed unloaded the main menu scene and I click escape too fast it will load the main menu scene, again and again, all the time.
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.Experimental.GlobalIllumination;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class BackToMainMenu : MonoBehaviour
{
public GameObject[] objsToDisable;
public AudioMixer audioMixer;
public static bool gameSceneLoaded;
public GameObject fadeImage;
public Light lights;
private float volumeLinearToDecibel;
private void Awake()
{
gameSceneLoaded = true;
}
// Start is called before the first frame update
void Start()
{
GetGameMusicVolume();
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
if (Time.timeScale == 0)
{
lights.enabled = true;
DisableEnableUiTexts(true);
SceneManager.UnloadSceneAsync(0);
if (fadeImage != null)
fadeImage.SetActive(true);
GetGameMusicVolume();
Cursor.visible = false;
Time.timeScale = 1;
}
else
{
Time.timeScale = 0;
lights.enabled = false;
MenuController.LoadSceneForSavedGame = false;
SceneManager.LoadScene(0, LoadSceneMode.Additive);
SceneManager.sceneLoaded += SceneManager_sceneLoaded;
Cursor.visible = true;
}
}
}
private void SceneManager_sceneLoaded(Scene arg0, LoadSceneMode arg1)
{
fadeImage = GameObject.FindWithTag("Game Scene Fader");
if (fadeImage != null)
fadeImage.SetActive(false);
audioMixer.SetFloat("gamemusicvolume", Mathf.Log(0.0001f) * 20);
DisableEnableUiTexts(false);
var pauseResumeMainMenuMode = FindInActiveObjectByName("MenuDefaultButtons_Canvas_Pause_Resume");
var newFreshGameMainMenuMode = FindInActiveObjectByName("MenuDefaultButtons_Canvas_NewFreshGame_SaveGame_Not_Exist");
newFreshGameMainMenuMode.SetActive(false);
pauseResumeMainMenuMode.SetActive(true);
SceneManager.sceneLoaded -= SceneManager_sceneLoaded;
}
private void DisableEnableUiTexts(bool enabled)
{
foreach (GameObject go in objsToDisable)
{
if (go.name == "Cameras Control")
{
foreach (Transform child in go.transform)
{
if (child.name == "Main Camera")
{
if (enabled == false)
{
child.GetComponent<Camera>().enabled = false;
}
else
{
child.GetComponent<Camera>().enabled = true;
}
}
}
}
else
{
go.SetActive(enabled);
}
}
}
private float LinearToDecibel(float linear)
{
float dB;
if (linear != 0)
dB = 20.0f * Mathf.Log10(linear);
else
dB = -144.0f;
return dB;
}
private void GetGameMusicVolume()
{
volumeLinearToDecibel = LinearToDecibel(PlayerPrefs.GetFloat("mainmenumusicvolume") / 100f);
audioMixer.SetFloat("gamemusicvolume", volumeLinearToDecibel);
}
GameObject FindInActiveObjectByName(string name)
{
Transform[] objs = Resources.FindObjectsOfTypeAll<Transform>() as Transform[];
for (int i = 0; i < objs.Length; i++)
{
if (objs[i].hideFlags == HideFlags.None)
{
if (objs[i].name == name)
{
return objs[i].gameObject;
}
}
}
return null;
}
}
You can check if the menu object cached already exist open the menu object, if not load the menu scene. Simple as that.
The more general way would be using SceneManager.GetSceneByBuildIndex
This method will return a valid Scene if a Scene has been added to the build settings at the given build index AND the Scene is loaded. If it has not been loaded yet the SceneManager cannot return a valid Scene.
so simply check IsValid like
if(SceneManager.GetSceneByBuildIndex(0).IsValid())

Pause sounds and background music in Unity

I am making a Pause menu in Unity that appears when you press escape. But whenever I press escape, the background music still plays. I am getting an error in lines 32 and 46.
The error log is Assets\PauseMenu.cs(32,32): error CS0029: Cannot implicitly convert type 'UnityEngine.AudioSource' to 'UnityEngine.AudioSource[]'
Here is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PauseMenu : MonoBehaviour
{
public static bool GameIsPaused = false;
public GameObject pauseMenuUI;
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
if (GameIsPaused)
{
Resume();
}
else
{
Pause();
}
}
}
void Resume()
{
pauseMenuUI.SetActive(false);
Time.timeScale = 1f;
GameIsPaused = false;
AudioSource[] audios = FindObjectOfType<AudioSource>();
foreach (AudioSource a in audios)
{
a.Play();
}
}
void Pause()
{
pauseMenuUI.SetActive(true);
Time.timeScale = 0f;
GameIsPaused = true;
AudioSource[] audios = FindObjectOfType<AudioSource>();
foreach (AudioSource a in audios)
{
a.Pause();
}
}
}
FindObjectOfType<AudioSource>() return only one object to return an array of object use FindObjectsOfType<AudioSource>()

How can I find all the ui text components in the hierarchy and then to disable/enable them?

For now I created array of Text and then dragging in the editor one by one to the inspector to the array.
The problem is that some Text ui are childs or childs of childs and it's not easy to find them all one by one.
How can I loop over the hierarchy or find all the text ui and then to disable/enable them ?
I need it for my game pause/resume.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class BackToMainMenu : MonoBehaviour
{
public Text[] uiTexts;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
if (Time.timeScale == 0)
{
SceneManager.UnloadSceneAsync(0);
DisableEnableUiTexts(false);
Cursor.visible = false;
Time.timeScale = 1;
}
else
{
Time.timeScale = 0;
MenuController.LoadSceneForSavedGame = false;
DisableEnableUiTexts(true);
SceneManager.LoadScene(0, LoadSceneMode.Additive);
Cursor.visible = true;
}
}
}
private void DisableEnableUiTexts(bool uiTextEnabled)
{
if (uiTexts.Length > 0)
{
foreach (Text ui in uiTexts)
{
if (uiTextEnabled)
{
ui.GetComponent<Text>().enabled = false;
}
else
{
ui.GetComponent<Text>().enabled = true;
}
}
}
}
}
Update :
I tried this :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class BackToMainMenu : MonoBehaviour
{
[ContextMenuItem("Fetch", nameof(FetchAllTexts))]
public Text[] uiTexts;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
if (Time.timeScale == 0)
{
SceneManager.UnloadSceneAsync(0);
DisableEnableUiTexts(false);
Cursor.visible = false;
Time.timeScale = 1;
}
else
{
Time.timeScale = 0;
MenuController.LoadSceneForSavedGame = false;
DisableEnableUiTexts(true);
SceneManager.LoadScene(0, LoadSceneMode.Additive);
Cursor.visible = true;
}
}
}
private void FetchAllTexts()
{
var tmp = new List<Text>();
for (var i = 0; i < SceneManager.sceneCount; i++)
{
foreach (var root in SceneManager.GetSceneAt(i).GetRootGameObjects())
{
tmp.AddRange(root.GetComponentsInChildren<Text>(true));
}
}
Text[] texts = tmp.ToArray();
uiTexts = texts;
}
private void DisableEnableUiTexts(bool uiTextEnabled)
{
if (uiTexts.Length > 0)
{
foreach (Text ui in uiTexts)
{
ui.enabled = uiTextEnabled;
}
}
}
}
but I don't see the "Fetch" ContextMenuItem anywhere. Tried right click on the object in hierarchy where the script is attached to tried in the Assets tried in the editor menu/s it's not there.
As said either use FindObjectsOfType
Text[] texts = FindObjectsOfType<Text>();
will return all active and enabled instances of Text.
However to get also disabled/inactive ones you can use
var tmp = new List<Text>();
for(var i = 0; i < SceneManager.sceneCount; i++)
{
foreach(var root in SceneManager.GetSceneAt(i).GetRootGameObjects)
{
tmp.AddRange(root.GetComponentsInChildren<Text>(true));
}
}
Text[] texts = tmp.ToArray();
which iterates over all loaded scenes and fetches ALL instances also currently inactive or disabled ones.
See
SceneManager.sceneCount
SceneManager.GetSceneAt
Scene.GetRootGameObjects
GameObject.GetComponentsInChildren
Then you can e.g. fetch this once either in Start or even earlier via the Inspector itself so you don't have to do it at every app start using [ContextMenuItem].
public class BackToMainMenu : MonoBehaviour
{
[ContextMenuItem("Fetch", nameof(FetchAllTexts)]
public Text[] uiTexts;
private void FetchAllTexts()
{
uiTexts = // One of the methods shown above
}
...
}
Why is this a good thing? As said this way you don't have to do it every time in Start and thus delaying the app start until it is done.
Instead you store all the references already via the Inspector in a SerializeField so when your app starts, this component already "knows" these references and you don't have to get them first.
Simply right click on the uiTexts field in the Inspector and hit Fetch
Then in general you would simply write
private void DisableEnableUiTexts(bool uiTextEnabled)
{
foreach (Text ui in uiTexts)
{
ui.enabled = uiTextEnabled;
}
}

Everything breaks after using PlayOneShot

In a game I am working on, the player uses a cube to open doors. When the player interacts with the cube while near the door, the door slides open. I am now trying to make it so that while it slides open, it makes a sound as doors should but I kinda ran into some issues. When I tried to add in the sound for the door, it ignored the part where the door should open altogether.
Here is what I did:
I added an AudioSource to the Cube's child object, CORE because the Cube object already contains an AudioSource which will play at the same time as this sound and assigned it in my Cube's script...
public AudioSource Woosh;
void Start()
{
//Debug.Log("Script initialized!");
Hm = gameObject.GetComponent<AudioSource>();
anim = GameObject.Find("TheFirstDoor").GetComponent<Animator>();
//Door = GameObject.Find("TheFirstDoor").GetComponent<AudioSource>();
Woosh = GameObject.Find("CORE").GetComponent<AudioSource>();
}
Here's the interactive part of the script, it runs a check but for some reason, it is pretending CanBeKey is false...
public void OnActivate()
{
if(HasPlayed == false) //Have I played? Has the Time out passed?
{
HasPlayedFirstTime = true;
Hm.Play();
HasPlayed = true;
PlayTimeOut = 30.0f;
if(CanBeKey == true)
{
anim.Play("Door_Open");
//Door.Play();
StartCoroutine(PlayDaWoosh());
Debug.Log("OPENING!");
}
}
}
Now here is the IEnumerator part of the script, PlayDaWoosh()
IEnumerator PlayDaWoosh()
{
Woosh.PlayOneShot(Woosh.clip);
yield return new WaitForSeconds(Woosh.clip.length);
}
I am aware that the last code snippet is a bit messy but it was the best thing I can think of.
Here is the full script in case you are that curious.....
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CUBE_CORE : MonoBehaviour
{
public AudioSource Hm; // HM!!!
private bool HasPlayed = false;
public float PlayTimeOut = 0.0f;
public static bool CanBeKey = false;
public Animator anim;
public static bool HasPlayedFirstTime = false;
public static AudioSource Door;
public AudioSource Woosh;
// Start is called before the first frame update
void Start()
{
//Debug.Log("Script initialized!");
Hm = gameObject.GetComponent<AudioSource>();
anim = GameObject.Find("TheFirstDoor").GetComponent<Animator>();
//Door = GameObject.Find("TheFirstDoor").GetComponent<AudioSource>();
Woosh = GameObject.Find("CORE").GetComponent<AudioSource>();
}
// Update is called once per frame
void Update()
{
if(HasPlayed == true)
{
if (PlayTimeOut < 0.0f)
{
HasPlayed = false;
}
PlayTimeOut -= Time.smoothDeltaTime;
}
}
public void OnActivate()
{
if(HasPlayed == false) //Have I played? Has the Time out passed?
{
HasPlayedFirstTime = true;
Hm.Play();
HasPlayed = true;
PlayTimeOut = 30.0f;
if(CanBeKey == true)
{
anim.Play("Door_Open");
//Door.Play();
StartCoroutine(PlayDaWoosh());
Debug.Log("OPENING!");
}
}
}
private void OnTriggerEnter(Collider other)
{
if(other.gameObject.name == "SecondDoor")
{
anim = GameObject.Find("DoorSecond").GetComponent<Animator>();
}
if(other.gameObject.name == "ThirdDoor")
{
anim = GameObject.Find("TheThirdDoor").GetComponent<Animator>();
}
if(other.gameObject.name == "FourthDoor")
{
anim = GameObject.Find("TheFourthDoor").GetComponent<Animator>();
}
}
IEnumerator PlayDaWoosh()
{
Woosh.PlayOneShot(Woosh.clip);
yield return new WaitForSeconds(Woosh.clip.length);
}
}
Expected result: Door opening and sound playing
Actual result: Neither happening unless I remove anything that has to do with the Woosh
I apologize ahead of time if I wasn't specific enough or if the question was answered somewhere else. I am relatively new here.

Categories