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>()
Related
I'm currently developing developing a 2d top-down RPG game in Unity.
I am trying to implement a healing system where you heal some points when you buy a coffee. This is my healthManager:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HealthManager : MonoBehaviour
{
public int maxHealth = 10;
public int currentHealth;
public HealthBarScript healthBar;
void Start()
{
currentHealth = maxHealth;
healthBar.SetMaxHealth(maxHealth);
}
void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
TakeDamage(1);
}
}
public void TakeDamage(int damage)
{
currentHealth -= damage;
healthBar.SetHealth(currentHealth);
if(currentHealth <= 0)
{
currentHealth = 0;
}
}
public void heal(int heal)
{
currentHealth += heal;
healthBar.SetHealth(currentHealth);
if(currentHealth >= maxHealth)
{
currentHealth = maxHealth;
}
}
}
And this is the script to buy coffee from a game object (sth. like a healing fountain):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class KaffeeautomatDialog : MonoBehaviour
{
public GameObject dialogBox;
public Text dialogText;
public string dialog;
public bool playerInRange;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if(Input.GetKeyDown(KeyCode.E) && playerInRange)
{
if(dialogBox.activeInHierarchy)
{
dialogBox.SetActive(false);
}
else
{
dialogBox.SetActive(true);
dialogText.text = dialog;
}
}
}
public void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
Debug.Log("Player entered");
playerInRange = true;
var healthManager = other.GetComponent<HealthManager>();
if(Input.GetKeyDown(KeyCode.J) && playerInRange)
{
if(healthManager != null)
{
healthManager.heal(5);
Debug.Log("You healed 5 Points!");
}
}
}
}
public void OnTriggerExit2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
Debug.Log("Player left");
playerInRange = false;
dialogBox.SetActive(false);
}
}
}
The Dialog Box shows up just fine and the Text is displayed. When i am staning in front of the healing fountain, i can activate and deactivate the dialog box by pressing "e".
But when i press "j", i don't heal and the console.log won't appear in Unity.
Did i mess up the Component import?
Checking for an Input it should always happen inside the Update() method and not inside the OnTriggerEnter2D() because the OnTriggerEnter2D() method it will only executed everytime something gets inside the trigger which might happen only onces. On the other hand Update() it will be executed every single frame and check for an Input.
What you can do is the following, inside your OnTriggerEnter2D() method you need to have a global boolean variable that indicates that the player has entered the trigger and inside your Update() method when you check for the Input key "J" you need to check if the above boolean flag is true, if it is true then continue with the heal process. Also you need to make the flag false when the player exit the trigger.
From what I see you already have such flag playerInRange, you can write the following code:
public HealthManager healthManager;
void Update()
{
if(playerInRange)
{
if(Input.GetKeyDown(KeyCode.E))
{
if(dialogBox.activeInHierarchy)
{
dialogBox.SetActive(false);
}
else
{
dialogBox.SetActive(true);
dialogText.text = dialog;
}
}
if(Input.GetKeyDown(KeyCode.E))
{
if(healthManager != null)
{
healthManager.heal(5);
Debug.Log("You healed 5 Points!");
}
}
}
}
public void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
Debug.Log("Player entered");
playerInRange = true;
healthManager = other.GetComponent<HealthManager>();
}
}
Remove the Input check from your OnTriggerEnter2D() method.
I press escape, in the debug it says that Pause(), Resum(), Pause() completed. And during the pause, Resum(), Pause(), Resum(), Pause() increase. During the dialogue, I press escape more to turn off the dialogue and still pause. How can I make sure that only one action is performed, and not several different ones? RETURN NOT WORKING
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class PauseMenu : MonoBehaviour
{
public static bool GameIsPaused;
public DialogWindow dialogWindow;
public GameObject pauseMenuUI;
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
if (GameIsPaused)
{
Debug.Log("resume");
Resume();
}
if (DialogWindow.IsDialog)
{
Debug.Log("dialog");
dialogWindow.Close();
}
else
{
Debug.Log("pause");
Pause();
}
}
}
public void Resume()
{
Cursor.lockState = CursorLockMode.Locked;
pauseMenuUI.SetActive(false);
Time.timeScale = 1f;
GameIsPaused = false;
}
public void Pause()
{
Cursor.lockState = CursorLockMode.None;
pauseMenuUI.SetActive(true);
Time.timeScale = 0f;
GameIsPaused = true;
}
public void ToMainMenu(int sceneNumber)
{
SceneManager.LoadScene(sceneNumber);
}
}
Perhaps you could use else if instead of just else, that would eliminate any chance of the different if statements to trigger simultaneously.
if (Input.GetKeyDown(KeyCode.Escape))
{
if (GameIsPaused)
{
Debug.Log("resume");
Resume();
}
else if (DialogWindow.IsDialog)
{
Debug.Log("dialog");
dialogWindow.Close();
}
else
{
Debug.Log("pause");
Pause();
}
}
So i tryied to add the select button on controller to my script so i can access it with a controller but I don't under stand how because I'm new to coding and if you can explain that would be great.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PauseMenu : MonoBehaviour{
public static bool GameIsPaused = false;
public GameObject pauseMenuUI;
private bool pauseEnabled;
void Update() {
if (Input.GetKeyDown(KeyCode.Escape))
{
if (GameIsPaused)
{
Resume();
}else
{
Pause();
}
}
}
void Resume()
{
pauseMenuUI.SetActive(false);
Time.timeScale = 1f;
GameIsPaused = false;
}
void Pause()
{
pauseMenuUI.SetActive(true);
Time.timeScale = 0f;
GameIsPaused = true;
}
}
First you have to know which button ID's of gamepad/ joystick. I prefer using this package for getting the button ID's of any new gamepad/ joystick. And then by getting the ID's you have to modify the Input Manager if required and call the function, like for example
if(Input.GetKey(KeyCode.Joystick1Button10)){
// do something
}
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;
}
}
i wonne make a game where the Gravity changes form 1enter image description here to -1enter image description here when i touch one Button and back when i touch the other button Button. In the Beginning it works but then it just stops working
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Button : MonoBehaviour
{
private Rigidbody2D rb;
private bool moveUp;
private bool MoveDown;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody2D>();
moveUp = false;
MoveDown = false;
}
public void PionterDownRight(){
moveUp = true;
}
public void PionterUpRight(){
moveUp=false;
}
public void PionterDownLeft(){
MoveDown= true;
}
public void PionterUpLeft(){
MoveDown = false;
}
// Update is called once per frame
void Update()
{
if(moveUp){
rb.gravityScale = -1;
}
if (MoveDown){
rb.gravityScale = 1;
}
}
}
I recommend having only one bool variable:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Button : MonoBehaviour
{
private Rigidbody2D rb;
private bool moveDown = true;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
public void PionterDownRight()
{
moveDown = false;
}
public void PionterUpRight()
{
moveDown = true;
}
public void PionterDownLeft()
{
moveDown = true;
}
public void PionterUpLeft()
{
moveDown = false;
}
// Update is called once per frame
void Update()
{
if (moveDown == true)
{
rb.gravityScale = 1;
}
else
{
rb.gravityScale = -1;
}
}
}
Graviyscale affects how much gravity will effect the rigidbody, I'm assuming its not working because it plateaus at 0 = no effect. You might have to take a different approach, something like
rb.gravityScale = -1;
replace with
rb.AddForce(new Vector2(0, 9.81f * 2));