-1

In the Main Menu scene I have this method that I call it from a UI button event.

public void ContinueGameButton()
    {
        transform.GetComponent<AudioSource>().Play();
        LoadSceneForSavedGame = true;
        newGameDialog.SetActive(false);

        foreach (var btn in MenuDefaultButtons.GetComponentsInChildren<Button>(true))
        {
            btn.interactable = false;
        }

        StartCoroutine(sceneFader.FadeAndLoadScene(SceneFader.FadeDirection.In, _newGameButtonLevel));
    }

Then in the Game scene when the Game scene is loaded this script is executed :

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 = 3f;
    //public SaveLoad saveLoad;

    private Image fadeOutUIImage;

    private void Start()
    {
        SceneManager.sceneLoaded += SceneManager_sceneLoaded;
    }

    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.LoadSceneAsync(sceneToLoad);
    }
    private void SceneManager_sceneLoaded(Scene arg0, LoadSceneMode arg1)
    {
        MenuController.newGameClicked = false;

        if (MenuController.LoadSceneForSavedGame == 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
}

When it's getting to this line the Game scene in the SceneFader script I used a breakpoint and it's executing this line twice in a row :

saveLoad.Load();

I can't figure out why.

Maybe because I'm registering the loaded event once in the Start()

SceneManager.sceneLoaded += SceneManager_sceneLoaded;

And then also in the IEnumerator

SceneManager.LoadSceneAsync(sceneToLoad);

But if I will remove the registering line in the Start() it will never get into the SceneManager_sceneLoaded function and if I remove the LoadSceneAsync line from the IEnumerator then it will never fade outback.

So I keep both lines in both places but I can't figure out why it's calling twice the line :

saveLoad.Load();

2 Answers2

0

I believe you're subscribing twice, once in the scene you're unloading and once in the one you're loading(Start() will get called again in the new scene). I'm assuming both of them have this script, or is the same scene. You can't unsubscribe in OnDisable() because you need it after scene load. You can try this: keep a static bool and set it to true when the event has been subscribed to, and when subscribing, only do so if it's false.

private static bool _isSubscribed = false;
private void Start()
{
    if(!_isSubscribed)
    {
        SceneManager.sceneLoaded += SceneManager_sceneLoaded;
        _isSubscribed = true;
    }
}

One thing to keep in mind is that this will run the method on the instance of the class from the previous scene, so that may or may not cause other problems.

I think this is probably one of the few places where it's better to use a singleton.

SpicyCatGames
  • 863
  • 7
  • 25
-1

In general in order to make sure you subscribed only exactly once I would always use this pattern

SceneManager.sceneLoaded -= SceneManager_sceneLoaded;
SceneManager.sceneLoaded += SceneManager_sceneLoaded;

It is "okey" to unsubscribe even if it was never subscribed before (in that case just nothing happens). This way it is ensured that the callback is added always only exactly once.


Then make sure it actually is getting called on the same object. To me it seems that it might happen that it is called on two different instances of SceneFader.

derHugo
  • 83,094
  • 9
  • 75
  • 115