Skip to main content
GameDev.net gamedev.net
🔒 Locked 🎮 Unity

GameManager destroyed error

Started by ethancodes Oct 31, 2018 at 2:21 AM 17 replies 4.4k views
Original Post
ethancodes
ethancodes

I've got a state machine set up that I'm having an issue with. I have a Start scene, a Level_01 Scene, and a GameOver Scene. Each scene has a corresponding state. When the game starts, it sets the game to the Start scene. When I click the start button, it loads the Gameplay state. The game plays fine, but when you lose, the GameOver state does not load, the game continues to run, and I get one error that says the Game Manager has been destroyed and I am still trying to reference it. I have my managers set up on a Singleton pattern and I'm not sure how it's getting destroyed. This is my first time using both the state machine and the singleton, and I also have very little experience switching between scenes so I may be doing something simple wrong. The main thing I can tell you is that the currentState variable is coming back as null when I go to hit the GameOver state, and that the code is not even getting far enough to hit the OnDeactivate for the GamePlay state (probably because currentState is null). I'm really out of ideas on this one and haven't been able to find anything useful online. Hopefully someone can show me what I'm doing wrong. Thanks!


//Managers object code:

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>();
	}
	
}

//GamePlayState code, other states are basically the same
 
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");
	}
}
          
 //GameManager code
          
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 ()
	{
		if (currentState != null) 
		{
			currentState.OnUpdate();
		}
	}

	void Start ()
	{
		SetState(typeof(MainMenuState));
	}
}
      
 //The load level method from the LevelManager
 public void LoadLevel (string name)
	{
		SceneManager.LoadSceneAsync(name);
		Brick.breakableCount = 0;
	}         
          


Nypyren
Nypyren

Well, your Managers.Awake function looks fine - the DontDestroyOnLoad is what should be preventing your Managers from being auto-destroyed by scene loading.

I don't see a "GamePlay" manager anywhere in that code though... is that the actual error message you're getting?

ethancodes
ethancodes

No I apologize that was my mistake. It says Game Manager has been destroyed. I edited the original post.

Stragen
Stragen

As the Game Manager class in a different script to the Manager class you need to be aware that unless you set specific execution order of scripts, unity can be a bit random as to what loads first. Setting execution order does not guarantee that the order will be respected though, but its worth trying.

From your code above i can see you're expecting the GameManager script to have already executed its "Awake" when you "Awake" the managers script... I'm guessing your error is occurring on the "GetComponent" for GameManager (seeing as that's your first component grab).

Can you supply the exact text out of the console showing the error?

ethancodes
ethancodes

I will get the exact error text when I get home tonight, and I can certainly try to set the order, but if this is the problem, shouldn't it be getting an error when the GetComponent fails? Not later when it tries to call a method on the GameManager? It's also not the first time the GameManger is being called on as it is already being used to load the Menu state when the game starts, and then called again to move from the Menu state to the Gameplay state. So I'm certainly using it a few times before I'm getting this error.

Nypyren
Nypyren

GetComponent will return null if it can't find the component; it won't report any error until you try to use the null. However, a destroyed component may give you a different error than one which just can't be found at all.

Regarding Stragen's comment about script-execution-order: Another way to avoid issues with that is using Awake() to get all of your component references initialized, and then Start() for anything that needs to happen after all Awake() calls have been completed.

ethancodes
ethancodes

So I put a Debug.Log in the Managers script in the Update function that checks Managers.Game == null. If it is, it puts a message in the Console. When the game starts up and pulls up the Menu scene, it works fine. However, it is returning null as soon as we switch scenes. So, important question that maybe someone had previously answered incorrectly. I was told I only need to put the DontDestroyOnLoad on the Managers script and since everything else is on that, they would also not be destroyed on load. Is it possible that I need to put DontDestroyOnLoad on each script?


EDIT: I tried this and it did not make a difference. Still trying to figure out the issue, I have attached the error I am getting.

error.PNG

Nypyren
Nypyren

I thought DontDestroyOnLoad on a GameObject would keep all MonoBehaviours on that object alive, but I might be wrong. Try calling it on the MonoBehaviours as well and see if that helps.

ethancodes
ethancodes
Just now, Nypyren said:

I thought DontDestroyOnLoad on a GameObject would keep all MonoBehaviours on that object alive, but I might be wrong. Try calling it on the MonoBehaviours as well and see if that helps.

Sorry I put in my edit on the previous post that I did try that and it did not work. Error was the exact same.

I tried it from both the individual scripts of the various managers, and also as gameObject.GetComponent<> on the Managers script.

ethancodes
ethancodes

Does it matter which scene the Managers game object is in? I have it in the Level_01 scene simply because that's where I'm always working in for most of the game so it makes it convenient to have in there so I don't have to switch between scenes for it. Then when the game starts it loads the Start scene (main menu). I don't believe it should be a problem though since that game object should be persistent anyways right?

ethancodes
ethancodes

I put a debug.Log in the Gamemanager class and even when the Manager is saying it's null, it is still firing the debug.Log every update from the GameManager. I tried to put a function in the Managers class that I could call after loading a new scene to find the GameManager component again, but still got the same error. Any other ideas? I've posted this in a few other places as well hoping to find something. I've pretty well run out of ideas myself.

ethancodes
ethancodes

Still have not found a solution to this, if anyone has any more insight I would be truly grateful

Nypyren
Nypyren

Have you tried putting a Debug.Log in GameManager.OnDestroy?

ethancodes
ethancodes

I have not... You mean just to see when it's getting called? Or if it is getting called? I could certainly try this. Should we expect to see it called once, when it tries to make another one and finds that there is already one made so it gets destroyed? Is that what you are suggesting we try to find out?

So I just found this:

https://forum.unity.com/threads/missing-reference-after-reloading-a-scene-but-the-referencing-object-still-exist.383648/

Sounds like a few people here had the same issue and had to unsubscribe from global events. I'm not entirely sure what that means or how to do it?


ethancodes
ethancodes

Here is a picture of my console when switching from the MainMenu scene to the Level1 scene. You can see the MainMenu state's Update function being hit. The "This is a game manager" is the update function of the GameManager. So then we hit MainMenu state's OnDeactivate, and the GamePlay state's OnActivate. We can see the game manager update is still being hit. Then the Update function of the GamePlay state. and finally we see the GameManager being destroyed. That is when the reference ("This is null") is lost in the Managers game object. However, right after that we can see the GameManager update is getting hit still. So I'm not sure if it is 1.Destroying the old game manager and making a new one, or 2. Keeping the old game manager and destroying the new one. It's hard to say, but I guess number 2 since it's losing the reference. But I have no idea how it could be doing that since the singleton is in place and DontDestroyOnLoad should be preventing that.

debuglog.PNG

Nypyren
Nypyren

Can you post screenshots of your scene hierarchies (just including the gameobject(s) that contain(s) the managers) and the inspector when the gameobject with your GameManager is selected in each scene? Preferrably the scenes when they're not running yet.

ethancodes
ethancodes

Actually I've somewhat got it figured out. Maybe. I moved the Managers object to the Start scene instead of the level1 scene. Now when I start, I get two errors. They are both for Awake functions on two different managers. The errors are caused because the stuff they are trying to reference don't exist until Level1 scene is loaded. However, errors aside, the game will now switch between scenes just fine without losing the reference to the game manager. I'm gonna try to do a little reworking on these couple of methods and see if it works.

xexuxjy
xexuxjy

You could maybe follow the route that the Rewired Unity System uses for it's input manager (http://guavaman.com/projects/rewired/docs/InputManager.html) , allowing you to have it in multiple scenes for testing, then for real game have it in the 'start' scene and load other scenes persistently, keeping the same references. I did something similar for my GameManager state.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.