Search Unity

Confused with something...

Discussion in 'Scripting' started by DustyShinigami, Jul 19, 2019.

  1. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    After some experimenting I've stumbled on a minor bug that's confusing me. I've no idea why it's an issue, but basically, each level has a Screen Fader object in the Hierarchy. It's essentially a canvas, which has my Screen Fader script on and has a Black Screen child object. When each scene loads, a coroutine is called and fades the screen to black for a second and then unfades. If I deactivate one of them in a level via the Inspector, pause the game, select Restart (restarts the entire game), the screen remains black and has an error in the console saying 'object reference not set to an instance of an object'. Clicking on this highlights line 49 in my PauseMenu script, which is where it tries to call the Screen Fader's coroutine. I don't understand why this is a problem. As I say, each scene has its own Screen Fader object set up, the black screen is 'on' by default (apart from one scene), so surely once the level is destroyed, that coroutine will be called from the first level's own Screen Fader...?

    These are the two scripts:

    PauseMenu:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.SceneManagement;
    5.  
    6. public class PauseMenu : MonoBehaviour
    7. {
    8.     public static bool GamePaused;
    9.     public GameObject pauseMenuUI;
    10.  
    11.     private ScreenFader theScreenFader;
    12.  
    13.     void Start()
    14.     {
    15.         theScreenFader = FindObjectOfType<ScreenFader>();
    16.         ControllerDetection();
    17.         GamePaused = false;
    18.     }
    19.  
    20.     void Update()
    21.     {
    22.         if (Input.GetKeyDown(KeyCode.Escape) || Input.GetKeyDown("joystick button 7") || Input.GetKeyDown("joystick button 9"))
    23.         {
    24.             if (GamePaused)
    25.             {
    26.                 Resume();
    27.             }
    28.             else
    29.             {
    30.                 Pause();
    31.             }
    32.         }
    33.     }
    34.  
    35.     public void Resume()
    36.     {
    37.         pauseMenuUI.SetActive(false);
    38.         Time.timeScale = 1f;
    39.         GamePaused = false;
    40.         //theScreenFader.blackScreen.enabled = true;
    41.         PlayerController.canMove = true;
    42.     }
    43.  
    44.     public void Restart()
    45.     {
    46.         SceneManager.LoadScene("start_area");
    47.         Debug.Log("level loaded");
    48.         pauseMenuUI.SetActive(false);
    49.         Debug.Log("pause menu disabled");
    50.         ScreenFader.black = true;
    51.         Debug.Log("screen is black");
    52.         theScreenFader.StartCoroutine("ScreenFade");
    53.         Time.timeScale = 1f;
    54.     }
    55.  
    56.     public void Pause()
    57.     {
    58.         Cursor.visible = true;
    59.         pauseMenuUI.SetActive(true);
    60.         Time.timeScale = 0f;
    61.         PlayerController.canMove = false;
    62.         GamePaused = true;
    63.         //theScreenFader.blackScreen.enabled = false;
    64.     }
    65.  
    66.     public void QuitGame()
    67.     {
    68.         Application.Quit();
    69.     }
    70.  
    71.     public void ControllerDetection()
    72.     {
    73.         string[] names = Input.GetJoystickNames();
    74.         for (int x = 0; x < names.Length; x++)
    75.         {
    76.             //print(names[x].Length);
    77.             if (names[x].Length == 19)
    78.             {
    79.                 //print("PS4 CONTROLLER IS CONNECTED");
    80.                 SceneManagement.ps4Controller = 1;
    81.                 SceneManagement.xbox360Controller = 0;
    82.                 if (SceneManagement.ps4Controller == 1)
    83.                 {
    84.                     //Debug.Log("PS4 controller detected");
    85.                 }
    86.             }
    87.             else if (names[x].Length == 33)
    88.             {
    89.                 //print("XBOX 360 CONTROLLER IS CONNECTED");
    90.                 SceneManagement.ps4Controller = 0;
    91.                 SceneManagement.xbox360Controller = 1;
    92.                 if (SceneManagement.xbox360Controller == 1)
    93.                 {
    94.                     //Debug.Log("Xbox 360 controller detected");
    95.                 }
    96.             }
    97.             else
    98.             {
    99.                 SceneManagement.ps4Controller = 0;
    100.                 SceneManagement.xbox360Controller = 0;
    101.             }
    102.  
    103.             if (SceneManagement.xbox360Controller == 0 && SceneManagement.ps4Controller == 0)
    104.             {
    105.                 //Debug.Log("No controllers detected");
    106.             }
    107.         }
    108.     }
    109. }
    110.  
    ScreenFader:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5. using UnityEngine.SceneManagement;
    6.  
    7. public class ScreenFader : MonoBehaviour
    8. {
    9.     public Image blackScreen;
    10.     public float fadeSpeed;
    11.     public float waitForFade;
    12.     //public static ScreenFader instance;
    13.     public static bool black;
    14.  
    15.     private bool isFadeToBlack;
    16.     private bool isFadeFromBlack;
    17.     //private bool isBlack;
    18.  
    19.     /*void Awake()
    20.     {
    21.         if (instance != null)
    22.         {
    23.             Destroy(gameObject);
    24.         }
    25.         else
    26.         {
    27.             instance = this;
    28.             DontDestroyOnLoad(gameObject);
    29.         }
    30.     }*/
    31.  
    32.     void Start()
    33.     {
    34.         StartCoroutine("ScreenFade");
    35.     }
    36.  
    37.     IEnumerator ScreenFade()
    38.     {
    39.         if (SceneManager.GetActiveScene() == SceneManager.GetSceneByName("level 1, room 1"))
    40.         {
    41.             isFadeToBlack = false;
    42.         }
    43.         else if (black)
    44.         {
    45.             Debug.Log("black");
    46.             yield return new WaitForSeconds(fadeSpeed);
    47.             isFadeToBlack = true;
    48.             yield return new WaitForSeconds(waitForFade);
    49.             isFadeFromBlack = true;
    50.             yield return new WaitForSeconds(2f);
    51.         }
    52.         else
    53.         {
    54.             yield return new WaitForSeconds(fadeSpeed);
    55.             isFadeToBlack = true;
    56.             yield return new WaitForSeconds(waitForFade);
    57.             isFadeFromBlack = true;
    58.             yield return new WaitForSeconds(2f);
    59.         }
    60.     }
    61.  
    62.     void Update()
    63.     {
    64.         if (isFadeToBlack)
    65.         {
    66.             blackScreen.color = new Color(blackScreen.color.r, blackScreen.color.g, blackScreen.color.b, Mathf.MoveTowards(blackScreen.color.a, 1f, fadeSpeed * Time.deltaTime));
    67.             if (blackScreen.color.a == 1f)
    68.             {
    69.                 isFadeToBlack = false;
    70.             }
    71.         }
    72.         if (isFadeFromBlack)
    73.         {
    74.             blackScreen.color = new Color(blackScreen.color.r, blackScreen.color.g, blackScreen.color.b, Mathf.MoveTowards(blackScreen.color.a, 0f, fadeSpeed * Time.deltaTime));
    75.             if (blackScreen.color.a == 0f)
    76.             {
    77.                 isFadeFromBlack = false;
    78.             }
    79.         }
    80.         if (black)
    81.         {
    82.             blackScreen.color = new Color(blackScreen.color.r, blackScreen.color.g, blackScreen.color.b);
    83.             if (blackScreen.color.a == 1f)
    84.             {
    85.                 black = false;
    86.             }
    87.         }
    88.     }
    89. }
    90.  
     
  2. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Deactivating the object as in toggling the GameObject off?

    FindObjectOfType only checks components on active objects. The components you're looking for can be enabled or disabled, which doesn't matter for this particular type of query, but the GO must be active, activeInHierarchy to be more exact.
     
    Last edited: Jul 19, 2019
  3. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    Yeah, the GameObject off. But again, surely if a scene has its own GameObject and it's on, it would use/find that...?
     
  4. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Have you checked whether the object is active or inactive when you enter the scene again?
     
  5. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    If I turn it off before hitting Play, it remains off. If I pause the game and select restart, that scene is destroyed, along with its Screen Fader, and the first level loads up. If I select the Screen Fader in the first level, it's enabled and the screen is black. If I set it to clear, the scene is visible, but the game is paused, there's no pause menu, and that error comes up.
     
  6. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    So follow-up question: How often does the pause menu component exist? Is there only one instance of it and you carry it over to the other scenes by marking it to stay alive? Or does it exist in every scene?

    If you carried it over, Start wouldn't be called again. Then you'd keep the old reference, which is - in the context of the engine - no longer a valid object even though its managed part still exists.

    But, unless something has changed recently, if you were to keep that destroyed reference, you'd get a different exception. That one used to say something along the meaning of you're trying to access an object that no longer exists (sort of, not the exact message).

    So actually, I believe you have multiple PauseMenu instances and then there should only be two cases in which you get 'null' and the corresponding exception + message:

    1) Start is not called, which means either the PauseMenu instance is disabled, or its GameObject is
    2) There is no such component that it attempts to find.

    You said that there is the component, and that it is active, so I believe we're getting closer if we take another look at the #1.

    Potential bug:
    Since the pause menu GO most-likely holds the object with the PauseMenu component, and the pause menu is likely disabled at the beginning, there's probably no chance the attached components can run their Start method before the Restart method is called.
    Then, you'd not have the reference for the screen fader right in time.

    Try to replace the reference access with the FindObjectOfType method, or link the screen fader via the inspector. Check what happens.
     
    Last edited: Jul 19, 2019
  7. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    I did have it carry over in every scene, but then decided to have it exist in every scene. I might have it carry over again, we'll see...

    Line 52 of the PauseMenu starts the Coroutine in the ScreenFader. Now, is that calling it independently of the ScreenFader's Start function, which I'm guessing it does, or is that calling the line from the Start function? If the latter, then that makes sense if the Pause Menu objects are disabled once Restart is initialised.

    I mean, this isn't a major deal or anything, but it's just something I'd like to try and understand why it's happening. :)