Hey guys,
I’m relatively new to Unity, but I’ve been programming for some time now. Some friends and I are building a game and I had the idea of making a central manager that has references to certain scripts or objects that are consistent throughout the game to make it easier for us to code. I did this by making a Game Object and adding to it a script, GlobalManager.
Now, the idea here is that instead of having to constantly make references to scripts and find them on the scene, etc., the objects and scripts that aren’t instantiated or destroyed in the scene have a static reference from this GlobalManager.
Example:
Let’s say our main player is the game object Player and its script is the PlayerScript. Then this GlobalManager script will have variables
public static GameObject player;
public static PlayerScript playerScript;
To the playerpublic var, I will drag the player object and the playerScript will be declared on start
playerScript = player.getComponent<PlayerScript>();
So why do this? It’s basically so from every other class that needs to reference either of these things, I can just simply do
GlobalManager.player;
Rather than having to declare new references to the player or potentially complex getComponent chains for these two things that are, for all intents and purposes, static.
That being said, OOP design principles hate static variables and pretty much every advice on the internet is DON’T USE STATIC VARIABLES.
Is there a particular reason why this would be a bad design style in Unity?
Thanks guys!
As you already figured out, there are tons of very strong opinions about whether statics aka Singletons aka global variables are good or the root of all evil or make children cry etc… pp. Each side has very convincing arguments about why its very bad or totally cool to have these.
I won’t even try to start that meta-discussion going here.
I just assume you decide to use global variables in Unity and are more interested about the cave-eats of doing so.
So specifically about Unity, keep the following in mind:
- Static variables are nulled on an “assembly reload” and on certain other events in the editor, e.g. when you press “Play” or when you change a script file.
- The value of static variables are never persisted in the map, prefabs or in custom asset files.
- Although you can use “Script Execution Order” to manipulate the order in which Awake / Start is called, this feature is VERY buggy in Unity at the moment (4.3.4). You are probably better off writing the classes in a way that will still work if the order gets messed up.
- The order of OnDestroy() and OnDisable can NOT be controlled and is always random. This is a major headache when you try to access these global variables during shutdown.
- If you create an GameObject during the game shutdown-phase in the editor (e.g. OnDestroy), it will END UP IN THE PERSISTED SCENE.
- FindObjectsOfType during OnDestroy won’t find gameobjects that already got their OnDestroy.
What does this list mean? You should try to solve 1., 2. and 3. by providing some kind of intelligent static getter that retrieve the game object on-demand if its not initialized yet.
static GameObject _m
public static GameObject manager
{ get { return _m ? _m : (_m = FindObjectsOfType<MyManager>()); } }
That way, you will return the manager regardless of whether its own Awake has been called already or not. To speedup the lookup, you can add a function void Awake() { _m = this; }
into the manager. (Or use OnEnable
, which is called during Assembly Reload as well. Awake
is only called when loading the scene the first time.)
What you should NOT do is, try to create a new manager via new GameObject
if it does not exist. That is because of 4. and 5.! You would end up with multiple instances of your manager saved into the map when accidently accessing the global manager during OnDestroy (or OnDisable)
Because of 6., it is probably easier to not clear out the manager in OnDestroy
/ OnDisable
, because then it is undefined whether you can still access the manager during shutdown functions of other scripts.
Hope it helped.
Sorry to necro, but this is one of the first pages that appear on google so people will find this first I hope.
I realize this is not the best scenario, but lets roll with it. What is easier on the cpu?
-
//Gameobject1 has script attached to it called 'GameMaster.cs' ->
public class GameMaster: MonoBehaviour{
public bool fooBool = false;
public int fooInt = 4;
}
and then…
// GameObject2 has script attached to it called 'MainMenuOptions.cs'
public class MainMenuOptions: MonoBehaviour{
public Text fooScore;
public GameMaster gameMaster;
public void Update(){
fooScore.text = gameMaster.fooInt.ToString();
}
}
OR
-
//Gameobject1 has script attached to it called 'GameMaster.cs' ->
public class GameMaster: MonoBehaviour{
private static GameMaster _instance;
public static GameMaster Instance { get { return _instance; } }
public bool fooBool = false;
public int fooInt = 4;
public void Awake(){
if (_instance != null && _instance != this)
Destroy(this.gameObject);
else
_instance = this;
}
}
and then…
// GameObject2 has script attached to it called 'MainMenuOptions.cs'
public class MainMenuOptions: MonoBehaviour{
public Text fooScore;
public void Update(){
fooScore.text = gameMaster.Instance.fooInt.ToString();
}
}
Lets say were not calling things in an update, is it easier on memory to go with case 1, instead of 2? Whats best here, in terms of cpu / memory consumption. Is there any difference?
Kind regards.