How is Scene.isLoaded determined? - unity3d

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.

Related

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.

Multi-scene launching of build, as is, from Unity, how?

Multiscene editing in Unity, blessed be it, permits the launching (via Editor Play mode) of the current scenes, in their current hierarchical state.
However, building and running the project doesn't recognise the current scene setup in the editor, and starts with whatever is set in the Build Settings.
Is there some way to make builds aware of the current editor state of Multi-scene editing hierarchy, and build and run that setup?
1. Getting the editor scenes into the build settings
First for collecting the settings you can use an editor script using
EditorSceneManager.GetSceneManagerSetup to receive the current setup of scenes in the editor
I assume you want only loaded Scenes so make a list of only scenes with isLoaded = true
EditorBuildSettings.scenes to add those scenes to the build settings
1.a. Update on MenuItem click
I made it an extra button in the menu since you might not want to have it always automatically.
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.SceneManagement;
public static class UpdateBuildSettigns
{
[MenuItem("Example/UpdateBuildSettings")]
public static void UpdateSettings()
{
// get current editor setup
SceneSetup[] editorScenes = EditorSceneManager.GetSceneManagerSetup();
// filter list e.g. get only scenes with isActive true
var activeEditorScenes = editorScenes.Where(scene => scene.isLoaded);
// set those scenes as the buildsettings
List<EditorBuildSettingsScene> editorBuildSettingsScenes = new List<EditorBuildSettingsScene>();
foreach (var sceneAsset in activeEditorScenes)
{
string scenePath = sceneAsset.path;
// ignore unsaved scenes
if (!string.IsNullOrEmpty(scenePath)) continue;
editorBuildSettingsScenes.Add(new EditorBuildSettingsScene(scenePath, true));
}
// Set the Build Settings window Scene list
EditorBuildSettings.scenes = editorBuildSettingsScenes.ToArray();
}
}
Updating on menu button
1.b. Update automaticly on (un)loading scenes
If you want it happening automatically you could also add the call as callback to EditorSceneManager.sceneOpened and EditorSceneManager.sceneClosed using InitializeOnLoad and a static constructor to get the callbacks added after recompile or opening the UnityEditor like
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;
[InitializeOnLoad]
public static class UpdateBuildSettigns
{
// ofcourse you still can also call it via menu item
[MenuItem("Example/UpdateBuildSettings")]
public static void UpdateSettings()
{
//...
}
static UpdateBuildSettigns()
{
// it is always save to remove callbacks even if they are not there
// makes sure they are always only added once
//
// this is a static constructor so actually there should be no
// callbacks yet ... but .. you never know ;)
EditorSceneManager.sceneOpened -= OnSceneLoaded;
EditorSceneManager.sceneClosed -= OnSceneUnloaded;
EditorSceneManager.sceneOpened += OnSceneLoaded;
EditorSceneManager.sceneClosed += OnSceneUnloaded;
}
private static void OnSceneUnloaded(Scene current)
{
UpdateSettings();
}
private static void OnSceneLoaded(Scene current, OpenSceneMode mode)
{
UpdateSettings();
}
}
Using automatic update
1.c. Enable/Disable automatic updates
If you want more control you can also add extra menu entries for enabling and disabling the automatic updates like
// flag to check if auto-updates are currently enabled
private static bool isEnabled;
// disable the "EnableAutoUpdate" button if already enabled
[MenuItem("Example/EnableAutoUpdate", true)]
private static bool CanEnable()
{
return !isEnabled;
}
// disable the "DisableAutoUpdate" button if already disabled
[MenuItem("Example/DisableAutoUpdate", true)]
private static bool CanDisable()
{
return isEnabled;
}
// add callbacks
[MenuItem("Example/EnableAutoUpdate")]
private static void EnableAutoUpdate()
{
// it is always save to remove callbacks even if they are not there
// makes sure they are always only added once
EditorSceneManager.sceneOpened -= OnSceneLoaded;
EditorSceneManager.sceneClosed -= OnSceneUnloaded;
EditorSceneManager.sceneOpened += OnSceneLoaded;
EditorSceneManager.sceneClosed += OnSceneUnloaded;
isEnabled = true;
}
// remove callbacks
[MenuItem("Example/DisableAutoUpdate")]
private static void DisableAutoUpdate()
{
EditorSceneManager.sceneOpened -= OnSceneLoaded;
EditorSceneManager.sceneClosed -= OnSceneUnloaded;
isEnabled = false;
}
Note since this uses the UnityEditor namespace you should either place this script in an Editor folder or use proper pre-processors like
#if UNITY_EDITOR
// above code here
#endif
2. Loading all scenes from the build settings
Than later when running the app in the first scene there should be a script responsible for loading all those scenes. Something like e.g.
// making it a component to make sure it is inside of one scene
public class SceneLoader : MonoBehaviour
{
private void Start()
{
var thisScene = SceneManager.GetActiveScene();
// load all scenes
for(int i = 0; i < SceneManager.sceneCountInBuildSettings; i++)
{
// skip if is current scene since we don't want it twice
if(thisScene.buildIndex == i) continue;
// Skip if scene is already loaded
if(SceneManager.GetSceneByBuildIndex(i).IsValid()) continue;
SceneManager.LoadScene(i, LoadSceneMode.Additive);
// or depending on your usecase
SceneManager.LoadSceneAsync(i, LoadSceneMode.Additive);
}
}
}
refs:
SceneManager.sceneCountInBuildSettings
Scene.buildIndex
SceneManager.GetSceneByBuildIndex
SceneManager.LoadScene
SceneManager.LoadSceneAsync
What I would do is attach some sort of a script to the launching scene in unity that would then trigger the loading of the rest of the required scenes after the game has started. That would require some fiddling to start properly (e.g detect the fact that the scenes are not already loaded before trying to load them).
I might extend the answer with a code snippet to achieve the result if you need it.
For now you could take a look at the docs here:
https://docs.unity3d.com/ScriptReference/SceneManagement.SceneManager.GetSceneByName.html https://docs.unity3d.com/ScriptReference/SceneManagement.SceneManager.LoadSceneAsync.html
The basic idea would be:
Get the necessary scenes using SceneManager.GetSceneByName and
filter out all the scenes that are already loaded.
For the scenes that are not loaded yet, call LoadSceneAsync and
attach some sort of
the coroutine to check the loading progress.
When all of the scenes are loaded, run a callback so that rest of
the game knows that the scenes are loaded and it is good to run the
rest of necessary actions which rely on those scenes being loaded.
If you want to preserve the current hierarchy (a set of scenes that are opened in the editor) when building, then it might be achievable with BuildPipeline:
https://docs.unity3d.com/Manual/BuildPlayerPipeline.html
There is a way to make a build with a programmatically-accessible list of scenes:
// Get filename.
string path = EditorUtility.SaveFolderPanel("Choose Location of Built Game", "", "");
string[] levels = new string[] {"Assets/Scene1.unity", "Assets/Scene2.unity"}; // You'd have to assemble this list yourself.
// Build player.
BuildPipeline.BuildPlayer(levels, path + "/BuiltGame.exe", BuildTarget.StandaloneWindows, BuildOptions.None);
(which you can determine based on the currently loaded scenes when running your build). This wouldn't be a standard (Cmd + b) build though, but pretty close.

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 ;)

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.

Create Loading To Wait Execution of Specific Code Until Finish Unity

I am making a game now. I have also make a save game and load game.
What i want is when i load the game (this mean code to load) i will show loading progress until code load execution is finish.
For example (LoadGame.cs):
using UnityEngine;
using System.Collections;
public class Load_Timer_Merchant : MonoBehaviour {
CloudSaver CloudSave;
// Use this for initialization
void Start () {
CloudSave = GameObject.FindGameObjectWithTag("CloudSave").GetComponent<CloudSaver>();
CloudSave.Load_Timer_Merchant ();
}
// Update is called once per frame
void Update () {
}
}
I want to make the progress loading of that file script LoadGame.cs
Is that possible to do it ?
Thanks
Dennnis
You have required Co-routine to do this task
For instance, You want to log successful text as image load from a url something like this
IEnumerator DoImageLoad()
{
WWW txTexture = new WWW(m_szTexturePath);
yield return txTexture;
renderer.material.mainTexture = txTexture.texture;
Debug.Log("Image Loaded. This message will show as image loaded");
}
Start(){
StartCoroutine("DoImageLoad");
}