Unity DontDestroyOnLoad

Hey!
So I have a GameSceneManager for loading scenes (fade in/out etc) which has DontDestroyOnLoad implemented.
The main menu creates this object and level GameManagers find it.
There are multiple buttons that call the GameSceneManager for level selection, which works fine, expect when I go back to the menu - the buttons have lost their refferneces…

What would be a proper way to fix this? Create a manager for the scene manager (buttons call it, which finds the SceneManager in the scene)?

1 Like

Hey @MustSeeMelons,

The way I did it in Glitch Garden months ago for example was that I created a FaderCanvasPrefab.

The script attached to it is a singleton to not get destroyed during fading in and out.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class FaderCanvasSingleton : MonoBehaviour {

	public static FaderCanvasSingleton instance;

	float fadeInTime = 0.12f;
	float fadedTime = 0.05f;
	float fadeOutTime = 0.12f;
	float fadeInSpeed;
	float fadeOutSpeed;
	Color fadeColor;

	void Awake() {
		// Need not to be destroyed for the fade in phase on the next level. Destroying at the end of the coroutine.
		MakeSingleton();
		fadeInSpeed = 1f / fadeInTime;
		fadeOutSpeed = 1f / fadeOutTime;
	}

	void MakeSingleton() {
		if (instance != null && instance != this) {
			Destroy(gameObject);
		} else {
			instance = this;
			DontDestroyOnLoad(gameObject);
		}
	}

	public void LoadLevel(string level, float _fadeInTime, float _fadedTime, float _fadeOutTime, Color _fadeColor) {
		fadeInTime = _fadeInTime;
		fadedTime = _fadedTime;
		fadeOutTime = _fadeOutTime;
		fadeColor = _fadeColor;
		StartCoroutine(LoadLevelCoroutine(level));
	}

	// Overload to load next level
	void LoadLevel() {
		StartCoroutine(LoadLevelCoroutine(null, SceneManager.GetActiveScene().buildIndex + 1));
	}

	public void LoadNextLevel(float _fadeInTime, float _fadedTime, float _fadeOutTime, Color _fadeColor) {
		fadeInTime = _fadeInTime;
		fadedTime = _fadedTime;
		fadeOutTime = _fadeOutTime;
		fadeColor = _fadeColor;
		LoadLevel();
	}

	IEnumerator LoadLevelCoroutine(string level, int levelIndex = 0, float fadeRate = 0.02f, float timer = 0f) {
		// TODO Review the logic with this timer thingy

		Image faderImage = gameObject.GetComponent<Image>();
		
		WaitForSecondsRealtime fadeDelay = new WaitForSecondsRealtime(fadeRate);

		// Fade out
		while (timer < fadeInTime) {
			faderImage.color = Color.Lerp(faderImage.color, fadeColor, fadeInSpeed * fadeRate);
			timer += fadeRate;
			yield return fadeDelay;
		}
		faderImage.color = fadeColor;

		// Load Scene
		if (level == null) {
			SceneManager.LoadScene(levelIndex);
		} else {
			SceneManager.LoadScene(level);
		}
		yield return new WaitForSecondsRealtime(fadedTime);
		timer = 0f;

		// Fade in
		while (timer < fadeOutTime) {
			faderImage.color = Color.Lerp(faderImage.color, Color.clear, fadeOutSpeed * fadeRate);
			timer += fadeRate;
			yield return fadeDelay;
		}

		faderImage.color = Color.clear;
		Destroy(gameObject);
	}
}

That prefab is never added manually to any scenes.

It’s being instantiated by another prefab the LevelManager (this is not prefab only, hence no “Prefab” in the name). This can be added to scenes to initiate auto next level loading after a specified time.

I only have it in the SplashScene, because that’s the only place I need auto loading (can see on the above image that it’s set to 3 seconds). The FaderCanvasPrefab prefab is drag & dropped on the prefab of this, so it can instantiate the fader canvas when needed.

I don’t have the LevelManager in other scenes. Just dragged & dropped the prefab itself on buttons’ “On Click ()”.

This is the code from LevelManager.cs

using UnityEngine;
using System.Collections;
#if UNITY_EDITOR
using UnityEditor;
#endif


public class LevelManager : MonoBehaviour {

	[SerializeField] Transform faderCanvasPrefab;
	[SerializeField] float fadeInTime = 0.12f;
	[SerializeField] float fadedTime = 0.05f;
	[SerializeField] float fadeOutTime = 0.12f;
	[SerializeField, Tooltip("Autoload in seconds. Set negative to disable autoload.")]
	float autoloadNextLevelAfter = -1f;
	[SerializeField] Color fadeColor = Color.black;
	

	private void Start() {
		if (autoloadNextLevelAfter >= 0) {
			Invoke("LoadNextLevel", autoloadNextLevelAfter);
		}
	}


	public void LoadLevel(string level) {
		Instantiate(faderCanvasPrefab, Vector3.zero, Quaternion.identity);
		// The canvas prefab has a singleton script attached to it, which destroys the Game Object at the end of the fading Coroutine
		FaderCanvasSingleton.instance.LoadLevel(level, fadeInTime, fadedTime, fadeOutTime, fadeColor);
	}

	
	public void LoadNextLevel() {
		Instantiate(faderCanvasPrefab, Vector3.zero, Quaternion.identity);
		// The canvas prefab has a singleton script attached to it, which destroys the Game Object at the end of the fading Coroutine
		FaderCanvasSingleton.instance.LoadNextLevel(fadeInTime, fadedTime, fadeOutTime, fadeColor);
	}


	public void QuitRequest() {
#if UNITY_EDITOR
		EditorApplication.isPlaying = false;
#else
		Application.Quit ();
#endif
	}
}

Instead of LoadNextLevel() there could be method overloads, but that’s not in the scope in that section.

Hope this helps

Sorry, I wrote the answer yesterday but my account was limited and had to wait 17 hours to send it.

Privacy & Terms