- Home /
Static References to Game Objects: Bad Idea?
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!
A singleton would be much better for this. None of this static
stuff.
One really big flaw with static variables that reference Unity objects, is that Unity objects can be Destroy
ed at any point by anything, freeing the memory and nullifying all references. But then again, that goes for any reference to Unity objects.
Personally static
anything should only be for singular functionality, without any side effects, such as $$anonymous$$athf.sin
. Thats a static function that causes no side effects and therefore has a valid reason for being static, because it doesn't rely on an instance. static
variables should solely be constants.
There are of course exception for libraries and such, but thats generally the rules I follow for static
stuff.
After many years of game development, the general principle I $$anonymous$$ch junior programmers straight out of computer science degrees is "forget what you've been taught about fancy architecture - do whatever makes the code simple!". You can always clean it up later if your design decisions turn out to have caused issues. Over engineering is a much bigger time sink than the odd dodgy shortcut!
You'll need to make sure you initialize your static variables at the correct times, but provided that's done things will work fine.
The typical reason to avoid statics or singletons (statics are really just a very simple type of singleton) is that it prevents you changing your $$anonymous$$d about having just 1 global manager at a later date. But as I said, if you don't expect that to happen, keep the code simple. You can always fix it later if you need to!
Have a look here, skip to the good coding habits section, I talked about when it is a good idea to use static, and when it is not.
@wibble82 I agree with you, but most the times new-comers (to unity and/or program$$anonymous$$g) tend to get use statics in very wrongly manner that's why we tell them that if you're not sure what you're doing with your static usage, keep your distance from them...
@Benproductions1, I looked into using Singletons in Unity before posting this question and came across this article: http://redframe-game.com/blog/global-managers-with-generic-singletons/ It was very insightful, but it confirmed my feelings about not using singletons for two reasons: 1, the model I want is more along the lines of the monolithic manager they mention at the beginning and 2, I would hate to run into any of the singleton leaks they talk about towards the end.
@wibble82, I've gotten that advice several times, but it doesn't hurt to learn better design and architectural skills along the way :D I appreciate the comment, though, I understand what you're getting at and you're right, spending too much time overengineering is a problem.
@vexe, that article is perfect!!! It showed me exactly what I want to do, which is ultimately create and abstract class like the Tags class they have there. The only problem is I also want my monolithic manager to contain references to scripts so I would have it be able to cross-reference anything so I might not be able to make it abstract.
Thanks for all the help guys. While I understand that static references to objects mihgt be dangerous for construction/destruction purposes of objects, most of the references are to objects that are consistent across the game, so I think it will be fine. Worst case I just have to replace the static references with references to actual objects later on in case of bugs or performance hits, but it shouldn't be too bad work-wise.
@$$anonymous$$au5trap: $$anonymous$$ost the times you don't need managers! - For example, recently I was working on a system that displays text to the user (ex if an item is not used in the right place "You can't use this item here", etc) - I'm using NGUI there we use UILabel. Now, it would be inconvenient to inject a UILabel everywhere I want to print a msg, so I thought oh well, I'll use some static class or a singleton (some sort of "manager") and have it store a reference to the UILabel I want... but then I thought, wait a $$anonymous$$ute, if I do this then, what if I wanted to use a dif kind of label? it would be nice to inject what I want to use. But most the times, I'll be using one UILabel, like a default fallback type of label. So what's the best of the two worlds?
Inside my abstract TextObject class (which has children to manage different type of texts, user interaction, etc) I did this:
private static UILabel defaultTextLabel;
public static UILabel DefaultTextLabel { get { if (defaultTextLabel == null) defaultTextLabel = GetDefaultInteractionTextLabel(); return defaultTextLabel; } }
[SerializeField]
private UILabel mTextLabel;
public UILabel textLabel { get { if (mTextLabel == null) return mTextLabel = DefaultTextLabel; return mTextLabel; } }
So now if somebody wants to access textLabel, he would definitely get a label (let it be injected, or the default one)
So now I could 'statically' create msgs, not caring about if there's a label or not.
public static T CreateTextObject<T> (string[] msgs) where T : TextObject
{
var text = Utils.CreateGoWith$$anonymous$$b<T>("[Generated] " + typeof(T));
text.msgs = msgs.ToList();
text.handleUserInput = true;
text.Show$$anonymous$$sg(true);
text.CycleText();
EventDelegate.Add(text.onCycleFinish, () => { Object.Destroy(text.gameObject); thereIsAlreadyAnObject = false; });
return text;
}
Bottom line, you could always find ways that don't require the use of a manager. Use your imagination.
Answer by Immanuel-Scholz · Feb 20, 2014 at 11:12 AM
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.
nice - I'll reference this answer to other static-related question in the future.
Just out of interest - I use Script Execution Ordering all over the place - is this something you think broke recently? Just about to upgrade to 4.3.4
@whydoidoit The answer on this question covers the behaviour well for 3.5.2: http://answers.unity3d.com/questions/217941/onenable-awake-start-order.html I haven't tested myself but people seem to think it's much the same (it not exactly the same) in the latest.
@Immanuel Scholz Re the OnDestroy/OnDisable ordering issues, for doing stuff on quit you can at least rely on OnApplicationQuit being called before OnDestroy... I think.
Answer by ClearRoseOfWar · Dec 02, 2020 at 06:12 PM
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?
1.
//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
2.
//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.
Your answer
![](https://koobas.hobune.stream/wayback/20220613133859im_/https://answers.unity.com/themes/thub/images/avi.jpg)