Search Unity

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

Discussion in 'Scripting' started by SharonL75, Oct 18, 2020.

  1. SharonL75

    SharonL75

    Joined:
    Aug 13, 2020
    Posts:
    91
    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 :

    Code (csharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.SceneManagement;
    6. using UnityEngine.UI;
    7. using UnityEngine.EventSystems;
    8. using UnityEditor;
    9. using Cinemachine;
    10. using UnityStandardAssets.Characters.ThirdPerson;
    11.  
    12. public class MenuController : MonoBehaviour
    13. {
    14.    #region Default Values
    15.    [Header("Default Menu Values")]
    16.    [SerializeField] private float defaultVolume;
    17.    [SerializeField] private int defaultSen;
    18.    [SerializeField] private bool defaultInvertY;
    19.  
    20.    [Header("Levels To Load")]
    21.    public string _newGameButtonLevel;
    22.    private string levelToLoad;
    23.  
    24.    public SceneFader sceneFader;
    25.    public GameObject player;
    26.  
    27.    private int menuNumber;
    28.    #endregion
    29.  
    30.    #region Menu Dialogs
    31.    [Header("Main Menu Components")]
    32.    [SerializeField] private GameObject menuDefaultCanvas;
    33.    [SerializeField] private GameObject GeneralSettingsCanvas;
    34.    [SerializeField] private GameObject graphicsMenu;
    35.    [SerializeField] private GameObject soundMenu;
    36.    [SerializeField] private GameObject controlsMenu;
    37.    [SerializeField] private GameObject confirmationMenu;
    38.    [Space(10)]
    39.    [Header("Menu Popout Dialogs")]
    40.    [SerializeField] private GameObject noSaveDialog;
    41.    [SerializeField] private GameObject newGameDialog;
    42.    [SerializeField] private GameObject loadGameDialog;
    43.    #endregion
    44.  
    45.    #region Slider Linking
    46.    [Header("Menu Sliders")]
    47.    [SerializeField] private Text controllerSenText;
    48.    [SerializeField] private Slider controllerSenSlider;
    49.    public float controlSenFloat = 2f;
    50.    [Space(10)]
    51.    [SerializeField] private Text volumeText;
    52.    [SerializeField] private Slider volumeSlider;
    53.    [Space(10)]
    54.    [SerializeField] private Toggle invertYToggle;
    55.    #endregion
    56.  
    57.    public bool loading = false;
    58.  
    59.    #region Initialisation - Button Selection & Menu Order
    60.    private void Start()
    61.    {
    62.        DontDestroyOnLoad(this);
    63.        menuNumber = 1;
    64.    }
    65.    #endregion
    66.  
    67.    //MAIN SECTION
    68.    public IEnumerator ConfirmationBox()
    69.    {
    70.        confirmationMenu.SetActive(true);
    71.        yield return new WaitForSeconds(2);
    72.        confirmationMenu.SetActive(false);
    73.    }
    74.  
    75.    private void Update()
    76.    {
    77.        if (Input.GetKeyDown(KeyCode.Escape))
    78.        {
    79.            if (menuNumber == 2 || menuNumber == 7 || menuNumber == 8)
    80.            {
    81.                GoBackToMainMenu();
    82.                ClickSound();
    83.            }
    84.  
    85.            else if (menuNumber == 3 || menuNumber == 4 || menuNumber == 5)
    86.            {
    87.                GoBackToOptionsMenu();
    88.                ClickSound();
    89.            }
    90.  
    91.            else if (menuNumber == 6) //CONTROLS MENU
    92.            {
    93.                ClickSound();
    94.            }
    95.        }
    96.    }
    97.  
    98.    private void ClickSound()
    99.    {
    100.        GetComponent<AudioSource>().Play();
    101.    }
    102.  
    103.    #region Menu Mouse Clicks
    104.    public void MouseClick(string buttonType)
    105.    {
    106.        if (buttonType == "Controls")
    107.        {
    108.            controlsMenu.SetActive(true);
    109.            menuNumber = 6;
    110.        }
    111.  
    112.        if (buttonType == "Graphics")
    113.        {
    114.            GeneralSettingsCanvas.SetActive(false);
    115.            graphicsMenu.SetActive(true);
    116.            menuNumber = 3;
    117.        }
    118.  
    119.        if (buttonType == "Sound")
    120.        {
    121.            GeneralSettingsCanvas.SetActive(false);
    122.            soundMenu.SetActive(true);
    123.            menuNumber = 4;
    124.        }
    125.  
    126.        if (buttonType == "Exit")
    127.        {
    128.            Debug.Log("YES QUIT!");
    129.            Application.Quit();
    130.        }
    131.  
    132.        if (buttonType == "Options")
    133.        {
    134.            menuDefaultCanvas.SetActive(false);
    135.            GeneralSettingsCanvas.SetActive(true);
    136.            menuNumber = 2;
    137.        }
    138.  
    139.        if (buttonType == "LoadGame")
    140.        {
    141.            menuDefaultCanvas.SetActive(false);
    142.            loadGameDialog.SetActive(true);
    143.            menuNumber = 8;
    144.        }
    145.  
    146.        if (buttonType == "NewGame")
    147.        {
    148.            menuDefaultCanvas.SetActive(false);
    149.            newGameDialog.SetActive(true);
    150.            menuNumber = 7;
    151.        }
    152.    }
    153.    #endregion
    154.  
    155.    public void VolumeSlider(float volume)
    156.    {
    157.        AudioListener.volume = volume;
    158.        volumeText.text = volume.ToString("0.0");
    159.    }
    160.  
    161.    public void VolumeApply()
    162.    {
    163.        PlayerPrefs.SetFloat("masterVolume", AudioListener.volume);
    164.        Debug.Log(PlayerPrefs.GetFloat("masterVolume"));
    165.        StartCoroutine(ConfirmationBox());
    166.    }
    167.  
    168.    public void ControllerSen()
    169.    {
    170.        controllerSenText.text = controllerSenSlider.value.ToString("0");
    171.        controlSenFloat = controllerSenSlider.value;
    172.    }
    173.  
    174.    #region ResetButton
    175.    public void ResetButton(string GraphicsMenu)
    176.    {
    177.        if (GraphicsMenu == "Audio")
    178.        {
    179.            AudioListener.volume = defaultVolume;
    180.            volumeSlider.value = defaultVolume;
    181.            volumeText.text = defaultVolume.ToString("0.0");
    182.            VolumeApply();
    183.        }
    184.  
    185.        if (GraphicsMenu == "Graphics")
    186.        {
    187.            controllerSenText.text = defaultSen.ToString("0");
    188.            controllerSenSlider.value = defaultSen;
    189.            controlSenFloat = defaultSen;
    190.  
    191.            invertYToggle.isOn = false;
    192.        }
    193.    }
    194.    #endregion
    195.  
    196.    #region Dialog Options - This is where we load what has been saved in player prefs!
    197.    public void ClickNewGameDialog(string ButtonType)
    198.    {
    199.        if (ButtonType == "Yes")
    200.        {
    201.            // Here to use a script to load the start game scene slowly smooth fade to black
    202.            // Then fade back to transparent when loading/loaded the scene game !!!!!
    203.            //SceneManager.LoadScene(_newGameButtonLevel);
    204.  
    205.            // When making new game to reset everything state scene everythig.
    206.            // Including the uiSceneText for example.
    207.            // To check how to reset everything to default !
    208.            loading = false;
    209.            newGameDialog.SetActive(false);
    210.            StartCoroutine(sceneFader.FadeAndLoadScene(SceneFader.FadeDirection.In, _newGameButtonLevel));
    211.        }
    212.  
    213.        if (ButtonType == "No")
    214.        {
    215.            GoBackToMainMenu();
    216.        }
    217.    }
    218.  
    219.    public void ClickLoadGameDialog(string ButtonType)
    220.    {
    221.        if (ButtonType == "Yes")
    222.        {
    223.            loading = true;
    224.            newGameDialog.SetActive(false);
    225.            StartCoroutine(sceneFader.FadeAndLoadScene(SceneFader.FadeDirection.In, _newGameButtonLevel));
    226.        }
    227.  
    228.        if (ButtonType == "No")
    229.        {
    230.            GoBackToMainMenu();
    231.        }
    232.    }
    233.    #endregion
    234.  
    235.    #region Back to Menus
    236.    public void GoBackToOptionsMenu()
    237.    {
    238.        GeneralSettingsCanvas.SetActive(true);
    239.        graphicsMenu.SetActive(false);
    240.        soundMenu.SetActive(false);
    241.  
    242.        VolumeApply();
    243.  
    244.        menuNumber = 2;
    245.    }
    246.  
    247.    public void GoBackToMainMenu()
    248.    {
    249.        menuDefaultCanvas.SetActive(true);
    250.        newGameDialog.SetActive(false);
    251.        loadGameDialog.SetActive(false);
    252.        noSaveDialog.SetActive(false);
    253.        GeneralSettingsCanvas.SetActive(false);
    254.        graphicsMenu.SetActive(false);
    255.        soundMenu.SetActive(false);
    256.        menuNumber = 1;
    257.    }
    258.  
    259.    public void ClickQuitOptions()
    260.    {
    261.        GoBackToMainMenu();
    262.    }
    263.  
    264.    public void ClickNoSaveDialog()
    265.    {
    266.        GoBackToMainMenu();
    267.    }
    268.    #endregion
    269. }
    270.  
    At the top line 56 :

    Code (csharp):
    1.  
    2. public bool loading = false;
    3.  
    Inside Start at line 61 :

    Code (csharp):
    1.  
    2. DontDestroyOnLoad(this);
    3.  
    Then inside the method for starting a new game in line 207 :

    Code (csharp):
    1.  
    2. loading = false;
    3.  
    And in the method that loading a saved game in line 228 :

    Code (csharp):
    1.  
    2. loading = true;
    3.  
    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 :

    Code (csharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.SceneManagement;
    6. using UnityEngine.UI;
    7.  
    8. public class SceneFader : MonoBehaviour
    9. {
    10.    #region FIELDS
    11.    public GameObject fadeOutUIGameobjectImage;
    12.    public float fadeSpeed = 0.8f;
    13.  
    14.    private MenuController menuController;
    15.    private Image fadeOutUIImage;
    16.  
    17.    private void Start()
    18.    {
    19.        
    20.    }
    21.  
    22.    public enum FadeDirection
    23.    {
    24.        In, //Alpha = 1
    25.        Out // Alpha = 0
    26.    }
    27.    #endregion
    28.  
    29.    #region FADE
    30.    public IEnumerator Fade(FadeDirection fadeDirection)
    31.    {
    32.        fadeOutUIGameobjectImage.SetActive(true);
    33.  
    34.        float alpha = (fadeDirection == FadeDirection.Out) ? 1 : 0;
    35.        float fadeEndValue = (fadeDirection == FadeDirection.Out) ? 0 : 1;
    36.        if (fadeDirection == FadeDirection.Out)
    37.        {
    38.            while (alpha >= fadeEndValue)
    39.            {
    40.                SetColorImage(ref alpha, fadeDirection);
    41.                yield return null;
    42.            }
    43.            fadeOutUIGameobjectImage.SetActive(false);
    44.        }
    45.        else
    46.        {
    47.            fadeOutUIGameobjectImage.SetActive(true);
    48.            while (alpha <= fadeEndValue)
    49.            {
    50.                SetColorImage(ref alpha, fadeDirection);
    51.                yield return null;
    52.            }
    53.        }
    54.    }
    55.    #endregion
    56.  
    57.    #region HELPERS
    58.    public IEnumerator FadeAndLoadScene(FadeDirection fadeDirection, string sceneToLoad)
    59.    {
    60.        yield return Fade(fadeDirection);
    61.        SceneManager.LoadScene(sceneToLoad);
    62.  
    63.        SceneManager.sceneLoaded += SceneManager_sceneLoaded;
    64.    }
    65.  
    66.    private void SceneManager_sceneLoaded(Scene arg0, LoadSceneMode arg1)
    67.    {
    68.        if (menuController.loading == true)
    69.        {
    70.            var saveLoad = GameObject.Find("Save System").GetComponent<SaveLoad>();
    71.            saveLoad.Load();
    72.        }
    73.    }
    74.  
    75.    private void SetColorImage(ref float alpha, FadeDirection fadeDirection)
    76.    {
    77.        if(fadeOutUIImage == null)
    78.        {
    79.            fadeOutUIImage = fadeOutUIGameobjectImage.GetComponent<Image>();
    80.        }
    81.  
    82.        fadeOutUIImage.color = new Color(fadeOutUIImage.color.r, fadeOutUIImage.color.g, fadeOutUIImage.color.b, alpha);
    83.        alpha += Time.deltaTime * (1.0f / fadeSpeed) * ((fadeDirection == FadeDirection.Out) ? -1 : 1);
    84.    }
    85.    #endregion
    86. }
    87.  
    At the top I did :

    Code (csharp):
    1.  
    2. private MenuController menuController;
    3.  
    Then inside the event SceneManager_sceneLoaded I did at line 67 :

    Code (csharp):
    1.  
    2. if (menuController.loading == true)
    3.  
    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.
     
  2. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    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.
     
    SharonL75 likes this.
  3. SharonL75

    SharonL75

    Joined:
    Aug 13, 2020
    Posts:
    91
    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 :

    Code (csharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.SceneManagement;
    6.  
    7. public class NewGame : MonoBehaviour
    8. {
    9.     // Here you store the actual instance
    10.     private static NewGame _instance;
    11.  
    12.     // Public read-only access property
    13.     public static NewGame Instance
    14.     {
    15.         get
    16.         {
    17.             // if already set simply return directly
    18.             if (_instance) return _instance;
    19.  
    20.             // Otherwise try to find it in the scene
    21.             _instance = FindObjectOfType<NewGame>();
    22.             if (_instance) return _instance;
    23.  
    24.             // Otherwise create it now
    25.             _instance = new GameObject(nameof(NewGame)).AddComponent<NewGame>();
    26.  
    27.             return _instance;
    28.         }
    29.     }
    30.  
    31.     private bool _gameStarted;
    32.     public static bool GameStarted => Instance._gameStarted;
    33.  
    34.     private void Awake()
    35.     {
    36.         if (_instance && _instance != this)
    37.         {
    38.             // There already exist another instance
    39.             Destroy(this.gameObject);
    40.             return;
    41.         }
    42.  
    43.         // Otherwise this is the active instance and should not be destroyed
    44.         _instance = this;
    45.         DontDestroyOnLoad(this.gameObject);
    46.  
    47.         SceneManager.sceneLoaded += OnSceneLoaded;
    48.  
    49.         // update it once now
    50.         _gameStarted = SceneManager.GetActiveScene().buildIndex != 0;
    51.     }
    52.  
    53.     void OnSceneLoaded(Scene scene, LoadSceneMode mode)
    54.     {
    55.         _gameStarted = scene.buildIndex != 0;
    56.     }
    57. }
    58.  
    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 :

    Code (csharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.SceneManagement;
    6. using UnityEngine.UI;
    7.  
    8. public class SceneFader : MonoBehaviour
    9. {
    10.     #region FIELDS
    11.     public GameObject fadeOutUIGameobjectImage;
    12.     public float fadeSpeed = 0.8f;
    13.  
    14.     private Image fadeOutUIImage;
    15.  
    16.     private void Start()
    17.     {
    18.        
    19.     }
    20.  
    21.     public enum FadeDirection
    22.     {
    23.         In, //Alpha = 1
    24.         Out // Alpha = 0
    25.     }
    26.     #endregion
    27.  
    28.     #region FADE
    29.     public IEnumerator Fade(FadeDirection fadeDirection)
    30.     {
    31.         fadeOutUIGameobjectImage.SetActive(true);
    32.  
    33.         float alpha = (fadeDirection == FadeDirection.Out) ? 1 : 0;
    34.         float fadeEndValue = (fadeDirection == FadeDirection.Out) ? 0 : 1;
    35.         if (fadeDirection == FadeDirection.Out)
    36.         {
    37.             while (alpha >= fadeEndValue)
    38.             {
    39.                 SetColorImage(ref alpha, fadeDirection);
    40.                 yield return null;
    41.             }
    42.             fadeOutUIGameobjectImage.SetActive(false);
    43.         }
    44.         else
    45.         {
    46.             fadeOutUIGameobjectImage.SetActive(true);
    47.             while (alpha <= fadeEndValue)
    48.             {
    49.                 SetColorImage(ref alpha, fadeDirection);
    50.                 yield return null;
    51.             }
    52.         }
    53.     }
    54.     #endregion
    55.  
    56.     #region HELPERS
    57.     public IEnumerator FadeAndLoadScene(FadeDirection fadeDirection, string sceneToLoad)
    58.     {
    59.         yield return Fade(fadeDirection);
    60.         SceneManager.LoadScene(sceneToLoad);
    61.  
    62.         SceneManager.sceneLoaded += SceneManager_sceneLoaded;
    63.     }
    64.  
    65.     private void SceneManager_sceneLoaded(Scene arg0, LoadSceneMode arg1)
    66.     {
    67.         if (NewGame.GameStarted != true)
    68.         {
    69.             var saveLoad = GameObject.Find("Save System").GetComponent<SaveLoad>();
    70.             saveLoad.Load();
    71.         }
    72.     }
    73.  
    74.     private void SetColorImage(ref float alpha, FadeDirection fadeDirection)
    75.     {
    76.         if(fadeOutUIImage == null)
    77.         {
    78.             fadeOutUIImage = fadeOutUIGameobjectImage.GetComponent<Image>();
    79.         }
    80.  
    81.         fadeOutUIImage.color = new Color(fadeOutUIImage.color.r, fadeOutUIImage.color.g, fadeOutUIImage.color.b, alpha);
    82.         alpha += Time.deltaTime * (1.0f / fadeSpeed) * ((fadeDirection == FadeDirection.Out) ? -1 : 1);
    83.     }
    84.     #endregion
    85. }
    86.  
    Just using this line in the SceneManager_sceneLoaded and checking if it's a new game or not :

    Code (csharp):
    1.  
    2. if (NewGame.GameStarted != true)
    3.  
    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.

    Code (csharp):
    1.  
    2. public void ClickNewGameDialog(string ButtonType)
    3.     {
    4.         if (ButtonType == "Yes")
    5.         {
    6.             newGameDialog.SetActive(false);
    7.             StartCoroutine(sceneFader.FadeAndLoadScene(SceneFader.FadeDirection.In, _newGameButtonLevel));
    8.         }
    9.  
    10.         if (ButtonType == "No")
    11.         {
    12.             GoBackToMainMenu();
    13.         }
    14.     }
    15.  
    16.     public void ClickLoadGameDialog(string ButtonType)
    17.     {
    18.         if (ButtonType == "Yes")
    19.         {
    20.             newGameDialog.SetActive(false);
    21.             StartCoroutine(sceneFader.FadeAndLoadScene(SceneFader.FadeDirection.In, _newGameButtonLevel));
    22.         }
    23.  
    24.         if (ButtonType == "No")
    25.         {
    26.             GoBackToMainMenu();
    27.         }
    28.     }
    29.  
    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 ?
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,726
    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):

    https://pastebin.com/SuvBWCpJ

    Unity3D Singleton with Prefab used for predefined data:

    https://pastebin.com/cv1vtS6G

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

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    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.