0
\$\begingroup\$

I've got a singleton set up with a overarching Managers class that has references to each of my other managers classes.

I also have a state machine set up that handles changing scenes between the Main Menu, the Gameplay, and the Game Over scene. These are also the corresponding states.

I'm using DontDestroyOnLoad on the Managers class so all that stuff should stay put between scenes.

The problem I'm running into however is that when it switches from the MainMenu to the Gameplay state, it apparently loses the reference to the GameManager object.

The error I get pops when I try to hit the GameManager for the second time when the game tries to change from Gameplay state to the Gameover state. It says it has been deleted, but if I put a debug.Log in the GameManager class's update function, it is still firing. I have tried to put a method in the Managers class that I could call to reassign it after the new level is loaded, but that doesn't seem to work.

I've pretty much tried everything I could think of, and asked on another site but nobody could figure it out there either. Hopefully somebody here can shed some light on what the problem is and how to fix it.

Below is all the code I think should be needed for this and also an image of the error I'm getting. Thanks everyone!

public class Managers : MonoBehaviour 
{
public static Managers instance = null;

private static GameManager _gameManager;
public static GameManager Game 
{
    get{ return _gameManager; }
}

private static UIManager _uiManager;
public static UIManager UI 
{
    get{ return _uiManager; }
}

private static LevelManager _sceneManager;
public static LevelManager Level 
{
    get{ return _sceneManager; }
}

private static BoardManager _boardManager;
public static BoardManager Board 
{
    get{ return _boardManager; }
}

private static PickUpManager _pickUpManager;
public static PickUpManager PickUp 
{
    get{ return _pickUpManager; }
}

private static SoundManager _soundManager;
public static SoundManager Sound 
{
    get { return _soundManager; }
}


void Awake ()
{
    if (instance == null) 
    {
        instance = this;
    } else if (instance != this) 
    {
        Destroy(gameObject);
    }

    DontDestroyOnLoad(gameObject);

    _gameManager = GetComponent<GameManager>();
    _uiManager = GetComponent<UIManager>();
    _sceneManager = GetComponent<LevelManager>();
    _boardManager = GetComponent<BoardManager>();
    _pickUpManager = GetComponent<PickUpManager>();
    _soundManager = GetComponent<SoundManager>();
}

void Update ()
{
    if (Managers.Game == null) //this if statement is here simply to show me that Managers does not have a reference to this anymore
    {
        Debug.Log("This is null");
    }
}
}




public class GameManager : MonoBehaviour 
{
public static MonoBehaviour monoBehaviour;
public bool isGameActive;
private int level = 50;
private _StatesBase currentState;

// Use this for initialization
void Awake ()
{   
    isGameActive = false;
    InitGame();
    monoBehaviour = this;
}

public _StatesBase State 
{
    get { return currentState;}
}

void InitGame ()
{
    Managers.Board.SetupScene (level);
}

public void SetState (System.Type newStateType)
{
    if (currentState != null) 
    {
        currentState.OnDeactivate ();
    }

    currentState = GetComponentInChildren (newStateType) as _StatesBase;
    if (currentState != null)
    {
        currentState.OnActivate();
    }
}

void Update ()
{
    Debug.Log("THIS IS A GAME MANAGER!!!"); //Just to check that it's still 
here. It is still showing even when Managers class reference is null.
    if (currentState != null) 
    {
        currentState.OnUpdate();
    }
}

void Start ()
{
    SetState(typeof(MainMenuState));
}
}


public class GamePlayState : _StatesBase 
{
public override void OnActivate ()
{
    Debug.Log("GamePlay OnActivate");
    Managers.Level.LoadLevel("Level_01");
    Managers.Game.isGameActive = true;

}

public override void OnDeactivate ()
{
    Debug.Log("GamePlay OnDeactivate");
    Managers.Game.isGameActive = false;
}

public override void OnUpdate ()
{
    Debug.Log("GamePlay OnUpdate");
}
}


public class LevelManager : MonoBehaviour 
{
public void LoadLevel (string name)
{
    SceneManager.LoadSceneAsync(name);
    Brick.breakableCount = 0;
}

public void QuitGame ()
{
    Application.Quit();
}

}

Error

\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

Changing scenes destroys all game objects in that scene. Your GameManager is a MonoBehaviour and is therefore likely attached to a game object that is being destroyed.

\$\endgroup\$
12
  • \$\begingroup\$ Right but it was my understanding that using DontDestroyOnLoad would prevent that. It allows you to have persistent objects between scenes. \$\endgroup\$ Commented Nov 3, 2018 at 3:12
  • \$\begingroup\$ Besides that, it's already been established that the Game Manager has not been destroyed as it is still ticking off the debug.Log in the Update function even after Managers class reference is returning null. \$\endgroup\$ Commented Nov 3, 2018 at 3:14
  • \$\begingroup\$ Are you calling DontDestroyOnLoad on the GameManager's object? Looks like you're only calling it on the Manager's object and may have a (second, duplicate) version of GameManager in the new scene. \$\endgroup\$ Commented Nov 3, 2018 at 3:15
  • \$\begingroup\$ You should only need it on Managers. However this did already occur to me as a possibility and I did try to put DontDestroyOnLoad on the GameManager as well but it did not change the outcome. \$\endgroup\$ Commented Nov 3, 2018 at 3:17
  • \$\begingroup\$ Unity looks to be doing something weird then, I'm not sure. You might want to use a Singleton or Toolbox instead. \$\endgroup\$ Commented Nov 3, 2018 at 15:11
0
\$\begingroup\$

I'm not sure this will be the answer you're looking for, but for managers I don't let them extend MonoBehaviour, in part because all MonoBehaviours are controlled by Unity processes in the backend and this can have unintended consequences.

I create manager classes as individual singletons. I then create a GameObject that contains a single MonoBehaviour script that pulls the instances of those managers. They don't reflect in the inspector (though that can be worked around with custom drawers and such), but those instances never get destroyed no matter what happens in the scene. In that event, even if the ManagerManager GO gets destroyed and recreated, the instances of the individual managers are static and persistent and therefore don't lose any information when the new GO picks them up.

This is a gist of my skeletal singleton class:

https://gist.github.com/xepherys/34d3d5ce3f44749e8649a25b38127347

I always start with this. It's threadsafe, provides easy access via the .Instance property, and is used in quite a few of the applications I write and maintain for my day job very successfully.

The ManagerManager script would just look something like this:

public class ManagerManager : MonoBehaviour
{
   public PlayerManager _playerMgr = PlayerManager.Instance;
   public GameManager _gameMgr = GameManager.Instance;
   public TimeManager _timeMgr = TimeManager.Instance;
}

The additional benefit of having a truly instanced, lazy singleton for your managers is that it's easy to call them from any class. You could have any script anywhere at any time reference GameManager.Instance.Property or GameManager.Instance.Method()

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .