How can I access a variable on a script in another scene that I made it dontdestroyonload ?

Or maybe not using dontdestrroyonload and using some other way to do it.

I need to get access to the variable flag loading from some script in another scene. The variable loading in my main menu scene and I need to access it in scripts in my game scene.

I’m not sure if using dontdestroyonload in this case is the right way and I don’t want to make the loading variable public static either :

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEditor;
using Cinemachine;
using UnityStandardAssets.Characters.ThirdPerson;

public class MenuController : MonoBehaviour
{
   #region Default Values
   [Header("Default Menu Values")]
   [SerializeField] private float defaultVolume;
   [SerializeField] private int defaultSen;
   [SerializeField] private bool defaultInvertY;

   [Header("Levels To Load")]
   public string _newGameButtonLevel;
   private string levelToLoad;

   public SceneFader sceneFader;
   public GameObject player;

   private int menuNumber;
   #endregion

   #region Menu Dialogs
   [Header("Main Menu Components")]
   [SerializeField] private GameObject menuDefaultCanvas;
   [SerializeField] private GameObject GeneralSettingsCanvas;
   [SerializeField] private GameObject graphicsMenu;
   [SerializeField] private GameObject soundMenu;
   [SerializeField] private GameObject controlsMenu;
   [SerializeField] private GameObject confirmationMenu;
   [Space(10)]
   [Header("Menu Popout Dialogs")]
   [SerializeField] private GameObject noSaveDialog;
   [SerializeField] private GameObject newGameDialog;
   [SerializeField] private GameObject loadGameDialog;
   #endregion

   #region Slider Linking
   [Header("Menu Sliders")]
   [SerializeField] private Text controllerSenText;
   [SerializeField] private Slider controllerSenSlider;
   public float controlSenFloat = 2f;
   [Space(10)]
   [SerializeField] private Text volumeText;
   [SerializeField] private Slider volumeSlider;
   [Space(10)]
   [SerializeField] private Toggle invertYToggle;
   #endregion

   public bool loading = false;

   #region Initialisation - Button Selection & Menu Order
   private void Start()
   {
       DontDestroyOnLoad(this);
       menuNumber = 1;
   }
   #endregion

   //MAIN SECTION
   public IEnumerator ConfirmationBox()
   {
       confirmationMenu.SetActive(true);
       yield return new WaitForSeconds(2);
       confirmationMenu.SetActive(false);
   }

   private void Update()
   {
       if (Input.GetKeyDown(KeyCode.Escape))
       {
           if (menuNumber == 2 || menuNumber == 7 || menuNumber == 8)
           {
               GoBackToMainMenu();
               ClickSound();
           }

           else if (menuNumber == 3 || menuNumber == 4 || menuNumber == 5)
           {
               GoBackToOptionsMenu();
               ClickSound();
           }

           else if (menuNumber == 6) //CONTROLS MENU
           {
               ClickSound();
           }
       }
   }

   private void ClickSound()
   {
       GetComponent<AudioSource>().Play();
   }

   #region Menu Mouse Clicks
   public void MouseClick(string buttonType)
   {
       if (buttonType == "Controls")
       {
           controlsMenu.SetActive(true);
           menuNumber = 6;
       }

       if (buttonType == "Graphics")
       {
           GeneralSettingsCanvas.SetActive(false);
           graphicsMenu.SetActive(true);
           menuNumber = 3;
       }

       if (buttonType == "Sound")
       {
           GeneralSettingsCanvas.SetActive(false);
           soundMenu.SetActive(true);
           menuNumber = 4;
       }

       if (buttonType == "Exit")
       {
           Debug.Log("YES QUIT!");
           Application.Quit();
       }

       if (buttonType == "Options")
       {
           menuDefaultCanvas.SetActive(false);
           GeneralSettingsCanvas.SetActive(true);
           menuNumber = 2;
       }

       if (buttonType == "LoadGame")
       {
           menuDefaultCanvas.SetActive(false);
           loadGameDialog.SetActive(true);
           menuNumber = 8;
       }

       if (buttonType == "NewGame")
       {
           menuDefaultCanvas.SetActive(false);
           newGameDialog.SetActive(true);
           menuNumber = 7;
       }
   }
   #endregion

   public void VolumeSlider(float volume)
   {
       AudioListener.volume = volume;
       volumeText.text = volume.ToString("0.0");
   }

   public void VolumeApply()
   {
       PlayerPrefs.SetFloat("masterVolume", AudioListener.volume);
       Debug.Log(PlayerPrefs.GetFloat("masterVolume"));
       StartCoroutine(ConfirmationBox());
   }

   public void ControllerSen()
   {
       controllerSenText.text = controllerSenSlider.value.ToString("0");
       controlSenFloat = controllerSenSlider.value;
   }

   #region ResetButton
   public void ResetButton(string GraphicsMenu)
   {
       if (GraphicsMenu == "Audio")
       {
           AudioListener.volume = defaultVolume;
           volumeSlider.value = defaultVolume;
           volumeText.text = defaultVolume.ToString("0.0");
           VolumeApply();
       }

       if (GraphicsMenu == "Graphics")
       {
           controllerSenText.text = defaultSen.ToString("0");
           controllerSenSlider.value = defaultSen;
           controlSenFloat = defaultSen;

           invertYToggle.isOn = false;
       }
   }
   #endregion

   #region Dialog Options - This is where we load what has been saved in player prefs!
   public void ClickNewGameDialog(string ButtonType)
   {
       if (ButtonType == "Yes")
       {
           // Here to use a script to load the start game scene slowly smooth fade to black
           // Then fade back to transparent when loading/loaded the scene game !!!!!
           //SceneManager.LoadScene(_newGameButtonLevel);

           // When making new game to reset everything state scene everythig.
           // Including the uiSceneText for example.
           // To check how to reset everything to default !
           loading = false;
           newGameDialog.SetActive(false);
           StartCoroutine(sceneFader.FadeAndLoadScene(SceneFader.FadeDirection.In, _newGameButtonLevel));
       }

       if (ButtonType == "No")
       {
           GoBackToMainMenu();
       }
   }

   public void ClickLoadGameDialog(string ButtonType)
   {
       if (ButtonType == "Yes")
       {
           loading = true;
           newGameDialog.SetActive(false);
           StartCoroutine(sceneFader.FadeAndLoadScene(SceneFader.FadeDirection.In, _newGameButtonLevel));
       }

       if (ButtonType == "No")
       {
           GoBackToMainMenu();
       }
   }
   #endregion

   #region Back to Menus
   public void GoBackToOptionsMenu()
   {
       GeneralSettingsCanvas.SetActive(true);
       graphicsMenu.SetActive(false);
       soundMenu.SetActive(false);

       VolumeApply();

       menuNumber = 2;
   }

   public void GoBackToMainMenu()
   {
       menuDefaultCanvas.SetActive(true);
       newGameDialog.SetActive(false);
       loadGameDialog.SetActive(false);
       noSaveDialog.SetActive(false);
       GeneralSettingsCanvas.SetActive(false);
       graphicsMenu.SetActive(false);
       soundMenu.SetActive(false);
       menuNumber = 1;
   }

   public void ClickQuitOptions()
   {
       GoBackToMainMenu();
   }

   public void ClickNoSaveDialog()
   {
       GoBackToMainMenu();
   }
   #endregion
}

At the top line 56 :

public bool loading = false;

Inside Start at line 61 :

DontDestroyOnLoad(this);

Then inside the method for starting a new game in line 207 :

loading = false;

And in the method that loading a saved game in line 228 :

loading = true;

Now that I know when it’s starting a new game and when it’s loading a game I want to use this loading variable in some scripts in my game scene.

For example this script :

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

public class SceneFader : MonoBehaviour
{
   #region FIELDS
   public GameObject fadeOutUIGameobjectImage;
   public float fadeSpeed = 0.8f;

   private MenuController menuController;
   private Image fadeOutUIImage;

   private void Start()
   {
       
   }

   public enum FadeDirection
   {
       In, //Alpha = 1
       Out // Alpha = 0
   }
   #endregion

   #region FADE
   public IEnumerator Fade(FadeDirection fadeDirection)
   {
       fadeOutUIGameobjectImage.SetActive(true);

       float alpha = (fadeDirection == FadeDirection.Out) ? 1 : 0;
       float fadeEndValue = (fadeDirection == FadeDirection.Out) ? 0 : 1;
       if (fadeDirection == FadeDirection.Out)
       {
           while (alpha >= fadeEndValue)
           {
               SetColorImage(ref alpha, fadeDirection);
               yield return null;
           }
           fadeOutUIGameobjectImage.SetActive(false);
       }
       else
       {
           fadeOutUIGameobjectImage.SetActive(true);
           while (alpha <= fadeEndValue)
           {
               SetColorImage(ref alpha, fadeDirection);
               yield return null;
           }
       }
   }
   #endregion

   #region HELPERS
   public IEnumerator FadeAndLoadScene(FadeDirection fadeDirection, string sceneToLoad)
   {
       yield return Fade(fadeDirection);
       SceneManager.LoadScene(sceneToLoad);

       SceneManager.sceneLoaded += SceneManager_sceneLoaded;
   }

   private void SceneManager_sceneLoaded(Scene arg0, LoadSceneMode arg1)
   {
       if (menuController.loading == true)
       {
           var saveLoad = GameObject.Find("Save System").GetComponent<SaveLoad>();
           saveLoad.Load();
       }
   }

   private void SetColorImage(ref float alpha, FadeDirection fadeDirection)
   {
       if(fadeOutUIImage == null)
       {
           fadeOutUIImage = fadeOutUIGameobjectImage.GetComponent<Image>();
       }

       fadeOutUIImage.color = new Color(fadeOutUIImage.color.r, fadeOutUIImage.color.g, fadeOutUIImage.color.b, alpha);
       alpha += Time.deltaTime * (1.0f / fadeSpeed) * ((fadeDirection == FadeDirection.Out) ? -1 : 1);
   }
   #endregion
}

At the top I did :

private MenuController menuController;

Then inside the event SceneManager_sceneLoaded I did at line 67 :

if (menuController.loading == true)

but I didn’t make any reference to the MenuController script even if it’s not destroyed. so loading will be null here I guess.

The idea is to use global in my project with the loading bool flag variable to find if the game is loading or if a new game started.

As you seem to have figured out: In order to access a non-static member of another class, you’ll need a reference to the specific instance you want to access.

The simplest way to do this for an object that isn’t originally part of the same scene is probably to use GameObject.Find (or one of its relatives).

You could also consider creating a public static variable that points to the primary (or only) instance of the class. (You might or might not find that objectionable, depending on your reason for not wanting to make the “loading” variable public static.) This is a somewhat common thing to do with singletons, particularly in Unity.

1 Like

This is what I tried to do but it’s not working as expected.

In the main menu script MenuController I removed deleted all the places with the loading bool variable and also the variable it self.

Then I created a new script that I added attached to empty GameObject :

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

public class NewGame : MonoBehaviour
{
    // Here you store the actual instance
    private static NewGame _instance;

    // Public read-only access property
    public static NewGame Instance
    {
        get
        {
            // if already set simply return directly
            if (_instance) return _instance;

            // Otherwise try to find it in the scene
            _instance = FindObjectOfType<NewGame>();
            if (_instance) return _instance;

            // Otherwise create it now
            _instance = new GameObject(nameof(NewGame)).AddComponent<NewGame>();

            return _instance;
        }
    }

    private bool _gameStarted;
    public static bool GameStarted => Instance._gameStarted;

    private void Awake()
    {
        if (_instance && _instance != this)
        {
            // There already exist another instance 
            Destroy(this.gameObject);
            return;
        }

        // Otherwise this is the active instance and should not be destroyed
        _instance = this;
        DontDestroyOnLoad(this.gameObject);

        SceneManager.sceneLoaded += OnSceneLoaded;

        // update it once now 
        _gameStarted = SceneManager.GetActiveScene().buildIndex != 0;
    }

    void OnSceneLoaded(Scene scene, LoadSceneMode mode)
    {
        _gameStarted = scene.buildIndex != 0;
    }
}

Since the main menu scene is on index 0 and the game scene is on index 1 I wanted to check if the current scene is not index 0 then it’s a new game or a loaded game. but I guess I’m wrong.

Then for testing in this script when I’m loading either a new game or loading a saved game I did :

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

public class SceneFader : MonoBehaviour
{
    #region FIELDS
    public GameObject fadeOutUIGameobjectImage;
    public float fadeSpeed = 0.8f;

    private Image fadeOutUIImage;

    private void Start()
    {
       
    }

    public enum FadeDirection
    {
        In, //Alpha = 1
        Out // Alpha = 0
    }
    #endregion

    #region FADE
    public IEnumerator Fade(FadeDirection fadeDirection)
    {
        fadeOutUIGameobjectImage.SetActive(true);

        float alpha = (fadeDirection == FadeDirection.Out) ? 1 : 0;
        float fadeEndValue = (fadeDirection == FadeDirection.Out) ? 0 : 1;
        if (fadeDirection == FadeDirection.Out)
        {
            while (alpha >= fadeEndValue)
            {
                SetColorImage(ref alpha, fadeDirection);
                yield return null;
            }
            fadeOutUIGameobjectImage.SetActive(false);
        }
        else
        {
            fadeOutUIGameobjectImage.SetActive(true);
            while (alpha <= fadeEndValue)
            {
                SetColorImage(ref alpha, fadeDirection);
                yield return null;
            }
        }
    }
    #endregion

    #region HELPERS
    public IEnumerator FadeAndLoadScene(FadeDirection fadeDirection, string sceneToLoad)
    {
        yield return Fade(fadeDirection);
        SceneManager.LoadScene(sceneToLoad);

        SceneManager.sceneLoaded += SceneManager_sceneLoaded;
    }

    private void SceneManager_sceneLoaded(Scene arg0, LoadSceneMode arg1)
    {
        if (NewGame.GameStarted != true)
        {
            var saveLoad = GameObject.Find("Save System").GetComponent<SaveLoad>();
            saveLoad.Load();
        }
    }

    private void SetColorImage(ref float alpha, FadeDirection fadeDirection)
    {
        if(fadeOutUIImage == null)
        {
            fadeOutUIImage = fadeOutUIGameobjectImage.GetComponent<Image>();
        }

        fadeOutUIImage.color = new Color(fadeOutUIImage.color.r, fadeOutUIImage.color.g, fadeOutUIImage.color.b, alpha);
        alpha += Time.deltaTime * (1.0f / fadeSpeed) * ((fadeDirection == FadeDirection.Out) ? -1 : 1);
    }
    #endregion
}

Just using this line in the SceneManager_sceneLoaded and checking if it’s a new game or not :

if (NewGame.GameStarted != true)

but the GameStarted is all the time true.

The main goal is to detect to know if the player started a new game or loading a saved game.
I guess the script I did the NewGame is not what I wanted. or that I did something wrong with it.

In the main menu scene script MenuController I just don’t using any flag to know if it’s loading game or new game.

public void ClickNewGameDialog(string ButtonType)
    {
        if (ButtonType == "Yes")
        {
            newGameDialog.SetActive(false);
            StartCoroutine(sceneFader.FadeAndLoadScene(SceneFader.FadeDirection.In, _newGameButtonLevel));
        }

        if (ButtonType == "No")
        {
            GoBackToMainMenu();
        }
    }

    public void ClickLoadGameDialog(string ButtonType)
    {
        if (ButtonType == "Yes")
        {
            newGameDialog.SetActive(false);
            StartCoroutine(sceneFader.FadeAndLoadScene(SceneFader.FadeDirection.In, _newGameButtonLevel));
        }

        if (ButtonType == "No")
        {
            GoBackToMainMenu();
        }
    }

So what am I doing wrong and what should I change/fix in the NewGame script to be able to use it to find if the player is starting a new game clicked the new game button or clicked the load game button and loading a saved game ?

This is pretty much the only pattern I ever use for this purpose. Perhaps one of them might work for you.

Some super-simple Singleton examples to take and modify:

Simple Unity3D Singleton (no predefined data):

Unity3D Singleton with Prefab used for predefined data:

These are pure-code solutions, do not put anything into any scene, just access it via .Instance!

You have a fairly complicated system involving several different techniques that I suspect you have never tried before, and you are testing the end result of this combined system instead of testing the individual parts. You should really be doing much smaller tests for the parts you are unfamiliar with to make sure they work like you expect.

For instance: Have you made a script that just checks the build index of the current scene, and make sure that gets the answer that you expect both in your game’s menu and in other scenes? Without trying to do all of the singleton stuff at the same time, or incorporate the answer into your SceneFader logic.

One problem I immediately notice: You have two scripts that both subscribe to the event “SceneManager.sceneLoaded”, and you are assuming those scripts are going to run in a specific order, with NewGame updating its variable before SceneFader tries to read it. That order is not guaranteed. And frankly, you seem to be overthinking this; the SceneFader already has the same information that the NewGame is using to decide whether the game has started, and the algorithm for deriving that information is very simple; SceneFader should probably just make the determination itself, rather than relying on a singleton.

Also, when describing the NewGame script, you say that you are just trying to check whether the player is currently in the menu scene or some other scene, but towards the end of your post you say you’re trying to tell the difference between loading a saved game and starting a new game, and I just don’t see how the first thing is going to help in any way with the second thing.