Unity, I have problem with DontDestroyOnLoad to keep tracking in different scence - unity3d

im new in unity and i have a problem
I am making a game that have 2 scence(Main Menu Scence and Game Scence), i put my music on Main Menu scence. I make a empty game object and i attach audio source there(music) , and i also attach script like this :
First script
public static KeepTheMusicOn Instance;
void Awake()
{
if (!Instance)
Instance = this;
else
Destroy(this.gameObject);
DontDestroyOnLoad(this.gameObject);
}
With that script i can keep music play in second scence wihtout restart the music, and in the main menu scence i have settings that have button to mute the music , the button will run my second script .
Second Script:
public AudioSource mainMusic;
public void Update()
{
DontDestroyOnLoad(mainMusic);
}
public void MusicOnOff()
{
if (mainMusic.isPlaying)
{
mainMusic.Pause();
}
else
{
mainMusic.UnPause();
}
}
My problem is when i start the game so im in my main menu scence i can mute the music with the button, but when i go to game scence and i back to menu, the button dont do anything.
So that is my problem, i hope anyone can help me. Sorry for my bad english.

Sounds like when switching scenes you destroy the button. When you them go back to the main menu you destroy the duplicate instance of your audio controller thing => references configured in the Button are lost.
In your case since you use a public Singleton anyway you could as well (ab)use it and put a component on the Button itself instead (thus the reference can not get lost) and do something like e.g.
[RequireComponent(typeof(Button))]
public class MusicButton : MonoBehaviour
{
[SerializeField] private Button button;
private void Awake()
{
if(!button) button = GetComponemt<Button>();
// dynamically add the callback
// it won't appear in the editor but get called in onClick
button.onClick.AddListener(OnClicked);
}
private void OnClicked()
{
KeepTheMusicOn.Instance.MusicOnOff();
}
}
If you prefer seeing it in the editor you can ofcourse as well rove it from Awake, make the OnClicked public and reference it in the button's onClick event manually.

Related

After pause menu score doesn't work anymore

does anyone know how to fix this? In my unity game when I go to pause menu and then continue playing, my scoreboard stops updating. I have two scoreboards, one in game and one in pause menu. The one in pause menu works well and updates but the one in game freezes after once visited in pause menu.
Here is my pausecodes and codes to add money (score):
public void PauseGame()
{
Time.timeScale = 0;
}
public void UnPauseGame()
{
Time.timeScale = 1;
}
}
if (collision.gameObject.tag == "Respawn") // When player lifts fish up
{
Destroy(this.gameObject);
// TODO: Player gets money (points) when this happens
textManager.instance.AddMoney();
Debug.Log("Add money");
}
public class textManager : MonoBehaviour
{
public static textManager instance;
public Text moneyText;
public int money;
private void Awake()
{
instance = this;
}
// Start is called before the first frame update
void Start()
{
Data data = SaveSystem.LoadData();
money = data.balance;
moneyText.text = "Balance: " + money.ToString() + " $";
}
public void AddMoney()
{
money = money + 10;
moneyText.text = "Balance: " + money.ToString() + " $";
SaveSystem.SaveData(this);
}
public int findMoney()
{
return money;
}
}
Please ask more info if needed.
I have tried to delete the one scoreboard in pause menu and after that the in-game pause menu started working right, but I would like to have still that other scoreboard too.
if (collision.gameObject.tag == "Respawn") // When player lifts fish up
{
// TODO: Player gets money (points) when this happens
textManager.instance.AddMoney();
Debug.Log("Add money");
Destroy(this.gameObject);
}
As I said this is the fix for what you posted as for the pause unpause problem you have to post the actual code where you do the pause unpause behavior so can people help you out my friend. As for what you posted what you've been doing is destroying the script just before excuting the call to textManager.instance.AddMoney(); and this will never run in the order you set in your code.
The scope of a static field is global, this means there can only be one.
Your textManager (side note: a classes first letter has to be upper case) is certainly attached to multiple GameObjects, once to the object displaying the score in the scene UI and once to a UI element in the pause menu.
As soon as you pause your game the first time, the TextManager attached to the object in the pause menu will run Awake() and the instance (please capitalise your properties as well) will be overriden and the reference to the ingame TextManager gets discarded.
The sloppy fix is re-initializing the ingame TextManager when you unpause the game, assigning it to be the Instance again. I'd not recommend doing that though.
The better solution is to implement an event on the player that gets triggered when the score changes and making the player instance a Singleton object since there will be only one player in all circumstances.
The UI elements displaying the score can then subscribe to this event in OnEnable() and unsubscribe in OnDisable() (do not forget to unsubscribe from events).
Addendum: You should not destroy your object before all code has been executed. Your code will still work because of how things are managed on the C++ layer of Unity, but it is definitely bad practice.

Problem with dontDestroyOnLoad in different scene

I am making a game that has two scenes (menu scene and game scene). In the menu scene, I create an empty game object just for my music, which includes (audio source (music), button to mute the music, and my script.
Here's the script:
public class Music : MonoBehaviour
{
public static Music Instance;
public AudioSource mainMusic;
public GameObject musicOffImage;
// Keep The Muic Playing In Diffrent Scene
void Awake()
{
if (!Instance)
Instance = this;
else
Destroy(this.gameObject);
DontDestroyOnLoad(this.gameObject);
}
// Method Mute Button
public void MusicOnOff()
{
if (mainMusic.isPlaying)
{
mainMusic.Pause();
musicOffImage.SetActive(true);
}
else
{
mainMusic.UnPause();
musicOffImage.SetActive(false);
}
}
}
With that script, I can play music in different scenes without reloading the music, and the button is working too, but the problem is when I go to the game scene and I back up to the menu scene, somehow the button didn't work. I think it's about the Destroy game object, but I am not sure how to fix it. Any help would mean a lot to me. Thanks.
I assume that everything the Music scripts needs is a child of it so that it is always fine.
However, after Destroy of the instance from the new scene, your buttons from the new scene loose the reference to the Music instance.
Since you have a Singleton there anyway you could as well (ab)use it and have this attached to your button itself
public MusicButton : MonoBehaviour
{
public void MusicOn()
{
Music.Instance.MusicOnOff();
}
}
And reference that instead in your button.
Also the image could e.g. register itself to the Music.Instance like e.g.
public MusicImage : MonoBehaviour
{
private void Start()
{
Music.Instance.musicOffImage = gameObject;
gameObject.SetActive(Music.Instance.mainMusic.isPlaying);
}
}
Alternative
In your question you said all objects are child's of an empty object, however the only object that gets DontDestroyOnLoad is the Music one. The others will get destroyed and reloaded so all these references might get lost as well. You might probably rather DontDestroyOnLoad the entire empty object and only hide/show the button in certain scenes.

How to control audio in another scene on/off in unity

I am making a game now, it's almost done. now I am trying to control the audio on and off by button or toggle button.
The problem is, I put my audio source gameobject in the splashscreen that is in the 1st scene. and I put the audio or music button in the Setting scene which is inside the 3rd scene. I already make the c# script to control the audio but when I've tried to insert the AudioSource, but it can't since it's from a different scene. I've tried to put the AudioSource in the same scene but the audio didn't start except I go to settings scene first.
Here is the script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Music : MonoBehaviour
{
static Music instance = null;
public AudioSource Backsound;
private void Awake()
{
if (instance != null)
{
Destroy(gameObject);
}
else
{
instance = this;
GameObject.DontDestroyOnLoad(gameObject);
}
}
public void backsoundOnOff()
{
AudioSource bgsound = Backsound.GetComponent<AudioSource>();
if (bgsound.mute = true){
bgsound.mute = false;
}
else {
bgsound.mute = true;
}
}
}
You have already solved half the problem by using GameObject.DontDestroyOnLoad
The object does indeed exist in both scenes. Now you just need to fetch it.
In the first scene where the created the object, Change the tag of the object. Instead of using one of the exiting tags, create a new tag for it called something such as "MenuMusic". Make sure you assign it after creating it, unity does not assign it automatically
Now, in the 3rd scene, in the game object that needs to access it, create a private field "_music"
in your Start function, add
void Start() {
_music = GameObject.FindGameObjectsWithTag("MenuMusic");
}
You will now have the same instance of Music from scene 1
I would highly recommend referencing the sound script that you have into some sort of game manager. Usually how i work is i have one generic script that controls a multitude of options that i usually call the GameManager. This sets player controls, visual options and sound. From here you can simply set bool whether the player wants the music on and off. If this option wants to change you can reference the GameManager at any point in any script.
//Game Manager code
public void SoundControl(bool soundOff)
{
If(soundOff == true)
{
//Sound Off Control
}else
{
//Sound on Control
}
}
//Reference to game Manager
GameManager manager;
public void TurnOffSound()
{
//Turn sound off through manager
manager =
GameObject.FindGameObjectWithTag("Manager").GetComponent<GameManager>
().SoundControl(true);
}
I find this to be the easiest way to control any options through one script that you can reference anywhere.

How to prevent mouse clicks from passing through GUI controls and playing animation in Unity3D [duplicate]

This question already has answers here:
Detect mouse clicked on GUI
(3 answers)
Closed 4 years ago.
Description
Hi Guys need your help I faced problems with mouse clicks which pass through UI panel in Unity, that is, I have created pause menu and when I click Resume button, the game gets unpaused and the player plays Attack animation which is undesirable.What I want is when I click Resume button, Attack animation should not be played. The same problem if I just click on panel not necessarily a button and the more I click on UI panel the more Attack animation is played after I exit pause menu. Moreover, I have searched for solutions to this issue and was suggeted to use event system and event triggers but since my knowledge of Unity is at beginner level I could not properly implement it. Please guys help and sorry for my English if it is not clear)) Here is the code that I use:
The code:
using UnityEngine;
using UnityEngine.EventSystems;
public class PauseMenu : MonoBehaviour {
public static bool IsPaused = false;
public GameObject pauseMenuUI;
public GameObject Player;
private bool state;
private void Update() {
//When Escape button is clicked, the game has to freeze and pause menu has to pop up
if (Input.GetKeyDown(KeyCode.Escape)) {
if (IsPaused) {
Resume();
}
else {
Pause();
}
}
}
//Code for Resume button
public void Resume() {
//I was suggested to use event system but no result Attack animation still plays once I exit pause menu
if (EventSystem.current.IsPointerOverGameObject()) {
Player.GetComponent<Animator>().ResetTrigger("Attack");
}
pauseMenuUI.SetActive(false);
Time.timeScale = 1f;
IsPaused = false;
}
//this method is responsible for freezing the game and showing UI panel
private void Pause() {
pauseMenuUI.SetActive(true);
Time.timeScale = 0f;
IsPaused = true;
}
//The code for Quit button
public void QuitGame() {
Application.Quit();
}
}
Im not sure if i understood your problem, but it sounds like somewhere in your code you start an attack when the player does a left click.
Now your problem is that this code is also executed when the player clicks on a UI element, for example in this case the Resume button?
You tried to fix this problem, by resetting the attack trigger of the animator, i think it would be a better solution to prevent the attack from starting instead of trying to reset it later.
EventSystem.current.IsPointerOverGameObject() returns true if the mouse is over an UI element.
So you can use it to modify your code where you start your attack:
... add this check in your code where you want to start the attack
if(EventSystem.current.IsPointerOverGameObject() == false)
{
// add your code to start your attack
}
...
Now your attack will only start if you are not over a UI element

Loader during Unity IAP Callback

I want to put loader in between dialog boxes come up for the purchase. What is the way for this?
Because when game player press Buy button, he should require to wait for 5 to 10 second depends on internet speed and server response and this process happed 2 to 3 times because multiple dialogs come up within screen.
So in this case, may be player can leave the screen. I want to put the loader so that game player realise that some processing is running in background, he required to wait for some time.
At present I was following completely this code for Unity IAP setup.
Integrating Unity IAP In Your Game
I assume this is for mobile platform but even if its not still the following can be considered:
Simple solution is to create a full screen Image (UI/Panel) object in your UI to block clicks. I would use Animator component (with triggers) to display this panel in front of other UI when there is a background process running.
public class Loader : MonoBehaviour
{
public static Loader Instance;
Animator m_Animator;
public bool Loading {get; private set;}
void Awake()
{
Instance = this; // However make sure there is only one object containing this script in the scene all time.
}
void Start()
{
//This gets the Animator, which should be attached to the GameObject you are intending to animate.
m_Animator = gameObject.GetComponent<Animator>();
Loading = false;
}
public void Show()
{
Loading = true;
m_Animator.SetBool("Loading", Loading); // this will show the panel.
}
public void Hide()
{
Loading = false;
m_Animator.SetBool("Loading", Loading); // this will hide the panel.
}
}
Then in any script which manipulates UI:
public void BuyButtonClicked()
{
Loader.Instance.Show();
// process time taking stuff
Loader.Instance.Hide();
}
You can also create any kind of loading animation as child of panel object using simple images and animation tool inside Unity (for example rotating animation (use fidget spinner, its cool)).
And in case of Android where user have option to leave screen by pressing OS back button you can prevent going back by checking if any loading is in progress by following example:
// code for back button
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
BackButtonPressed();
}
}
void BackButtonPressed()
{
if(Loader.Instance.Loading)
return;
// use back button event. (For example to leave screen)
}
Hope this helps ;)