Save active Scene Unity 3D - unity3d

I am trying to save modification made when the scene is loaded (new object instantiated ..) and I would like to load them after.
My script is :
public void SaveGame()
{
PlayerPrefs.SetInt("Level", SceneManager.GetActiveScene().buildIndex);
PlayerPrefs.Save();
print("Game saved!");
Debug.Log("Game normally saved " + scene.name);
}
public void LoadGame()
{
SceneManager.LoadScene(PlayerPrefs.GetInt("Level"));
print("Game loaded!");
Debug.Log("Game normally Loaded ");
}
The problem is when I save the game , it saves the Scene which is in my asset but I want to save this scene without any modifications.
I think that I should get the scene active but with modifications so I should modify this part of code:
PlayerPrefs.SetInt("Level", SceneManager.GetActiveScene().buildIndex);
So is there any way to do this ?

Related

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 is Scene.isLoaded determined?

I cannot find anything in docs that breaks down the scene loading process and how the Scene.isLoaded property is determined.
I need to mock large scene loading and cannot find a way to delay a scene's loading. It would be useful to understand the flow especially when needing to load external assets in the scene and only mark the scene as loaded via code.
You should have a look at SceneManager.LoadSceneAsync and especially allowSceneActivation which allows you to do exactly that: Delay a scenes loading and e.g. display a loading screen meanwhile.
Example from the docs:
// This script lets you load a Scene asynchronously.
// It uses an asyncOperation to calculate the progress and outputs
// the current progress to Text (could also be used to make progress bars).
// Attach this script to a GameObject
// Create a Button (Create>UI>Button) and a Text GameObject (Create>UI>Text)
// and attach them both to the Inspector of your GameObject
//In Play Mode, press your Button to load the Scene, and the Text
// changes depending on progress. Press the space key to activate the Scene.
//Note: The progress may look like it goes straight to 100% if your Scene doesn’t have a lot to load.
using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class AsyncOperationProgressExample : MonoBehaviour
{
public Text m_Text;
public Button m_Button;
void Start()
{
//Call the LoadButton() function when the user clicks this Button
m_Button.onClick.AddListener(LoadButton);
}
void LoadButton()
{
//Start loading the Scene asynchronously and output the progress bar
StartCoroutine(LoadScene());
}
IEnumerator LoadScene()
{
yield return null;
//Begin to load the Scene you specify
AsyncOperation asyncOperation = SceneManager.LoadSceneAsync("Scene3");
//Don't let the Scene activate until you allow it to
asyncOperation.allowSceneActivation = false;
Debug.Log("Pro :" + asyncOperation.progress);
//When the load is still in progress, output the Text and progress bar
while (!asyncOperation.isDone)
{
//Output the current progress
m_Text.text = "Loading progress: " + (asyncOperation.progress * 100) + "%";
// Check if the load has finished
if (asyncOperation.progress >= 0.9f)
{
//Change the Text to show the Scene is ready
m_Text.text = "Press the space bar to continue";
//Wait to you press the space key to activate the Scene
if (Input.GetKeyDown(KeyCode.Space))
//Activate the Scene
asyncOperation.allowSceneActivation = true;
}
yield return null;
}
}
}
Also checkout sceneLoaded: as stated in the docs
Add a delegate to this to get notifications when a Scene has loaded.
This happens after the scene has been loaded and afaik after the Awake calls finished. You can easily test this by using a script like
public class Test : MonoBehaviour
{
private void Awake()
{
Debug.Log("Awake");
SceneManager.sceneLoaded += OnSceneLoaded;
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
Debug.Log("OnSceneLoaded");
}
}
You will see both the
Awake
OnSceneLoaded
debugs in the console meaning that Awake was called before OnSceneLoaded.
→ At this moment I would also expect the Scene.isLoaded to be set to true.

Keeping variable from changing on another scene load

I'm new to unity and i'm trying to load scenes based on items collected.
Problem is that the counter is not counting my acquired items.
I'm using OnTriggerEnter2D() to trigger the event; Below is the snippet:
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.CompareTag("Player"))
{
collectionNumber += 1;
Destroy(gameObject);
if (collectionNumber == 1)
{
collision.gameObject.transform.Find("Acquired_Items").GetChild(0).gameObject.SetActive(true);
qiCollector.gameObject.transform.GetChild(0).gameObject.SetActive(true);
}
else if (collectionNumber == 2)
{
collision.gameObject.transform.Find("Acquired_Items").GetChild(1).gameObject.SetActive(true);
qiCollector.gameObject.transform.GetChild(1).gameObject.SetActive(true);
}
else if (collectionNumber == 3)
{
collision.gameObject.transform.Find("Acquired_Items").GetChild(2).gameObject.SetActive(true);
qiCollector.gameObject.transform.GetChild(2).gameObject.SetActive(true);
}
else
{
Debug.LogWarning("All Items Collected !!");
}
cN.text = "Collection Number " + collectionNumber.ToString();
}
}
Whenever a new scene is loaded this script is loaded because it is on my quest item. And for every scene there is a quest item. So what I want to do is basically keep track of my collectionNumber, but it resets to 0.
Any help is much appreciated :)
First method:
Don't allow your object to be destroyed on scene load
https://docs.unity3d.com/ScriptReference/Object.DontDestroyOnLoad.html
public static void DontDestroyOnLoad(Object target);
Above code will prevent destroying your GameObject and its components from getting destroyed when loading a new scene, thus your script values
Second Method:
Write out your only value into a player pref
https://docs.unity3d.com/ScriptReference/PlayerPrefs.html
// How to save the value
PlayerPrefs.SetInt("CollectionNumber", collectionNumber);
// How to get that value
collectionNumber = PlayerPrefs.GetInt("CollectionNumber", 0);
Third method:
Implement a saving mechanism:
In your case i would not suggest this

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.

Issue with playing background audio for different scenes in unity3d

In my game, I have two scene.
What I want to achieve is if user navigates from one scene to another, background audio specific to each should be played from start(audio length=0)
But all my efforts are in vain.
I tried using 'Pause' Method of audioSound
I tried
create a new game object and assign this scene background score to it and play
destroy gameObject created for another scene if there was any
But it doesn't give the result that I want.
I searched for finding how to play audioClip from start and stop other audioClip playing but didn't find any.
I know I'm not supposed to ask for code on stack overflow but if anyone has achieved this or has some pseudo code request you to provide it
I'm not sure I understand your question properly, since it seems the simplest scenario for background music.
If you really want a change of audioclip in every scene, let's say Scene A must play Clip A and Scene B must play Clip B, and both clips should be played as soon as a scene is loaded, you just need to create a game object in both scenes with an Audio Source component, with the Play On Awake flag active, and then just assign the appropriate clip for the scene (i.e.: assign Clip A in the Audio Clip field of the Audio Source component of Scene A game object, and do the same with Clip B for Scene B).
That's pretty much it.
If you looking at the detail code then you can try this code.
First : Make a script "SoundFxScript.cs" // You can modified as you want
Insert this code :
public class SoundFxScript : MonoBehaviour {
//Background Music
public AudioSource Scene1_Sound;
public AudioSource Scene2_Sound;
// Use this for initialization
void Start () {
PlayBackgroundMusic ();
}
// Update is called once per frame
void Update () {
}
public void PlayBackgroundMusic() {
if (UnityEngine.SceneManagement.SceneManager.GetActiveScene ().name == "Scene1") {
Scene1_SoundPlay();
} else if (UnityEngine.SceneManagement.SceneManager.GetActiveScene ().name == "Scene2") {
Scene2_SoundPlay();
}
}
public void Scene1_SoundPlay() {
Scene1_Sound.Play ();
Scene2_Sound.Stop ();
}
public void Scene2_SoundPlay() {
Scene1_Sound.Stop ();
Scene2_Sound.Play ();
}
// Step Fifth
public void LoadTheScene (string Scenename) {
UnityEngine.SceneManagement.SceneManager.LoadScene (Scenename);
sf.PlayBackgroundMusic ();
}
}
Second : Make Gameobject name = "SoundMusic" at the first scene and add component script SoundFxScript.cs. In gameobject "SoundMusic" you can add you background music for scene1 and scene2.
Third : Make a singleton file Singleton.cs
Insert this code :
public class Singleton : MonoBehaviour {
static Singleton instance = null;
void Start ()
{
if (instance != null)
{
Destroy(gameObject);
return;
}
DontDestroyOnLoad(gameObject);
instance = this;
}
}
Fourth : at gameobject "SoundMusic" add component script "Singleton.cs"
Fifth : How To Call In Another Scene (Load Scene). This method is inside SoundFxScript.cs
Example You have a method to call a load scene. Try this method :
Call it with : LoadTheScene("Scene2") // Call scene2
In here you can call your SoundFxscript.cs Component from any script you have.
Example :
SoundFxScript sf;
sf = GameObject.Find ("SoundMusic").GetComponent<SoundFxScript> ();
And you can use method LoadTheScene to load a new scene and the background music will RePlay again according to the what Scene is it.
That's All.