Csharp has no true global variable?

Global variables are bad. No argument there.

And I know there are many, many, many and then even more questions on this subject, but I’m not happy with the answers there. None really address the global var issue, they just give either static as a solution (not valid) or some good coding practices on how we should not use globals (quite valid).

Well, thing is, global variables are needed.

Specially if you want to create bad design and have headaches on a growing application. And, eventually, to keep serious stuff like logs or caches.

If we really have no native global var in csharp, what can we do to keep data among whole scene changes, with all objects being naturally destructed, other than manually using DontDestroyOnLoad and/or LoadLevelAdditive?

edit / disclaimer

If you want more history on what happened here, go ahead and check the edit revisions. I’ve decided it’s better to remove the clutter (and the shame).

I should have mentioned I’m also aware of Singletons and their issues. I thought it would be evident from my immediate answer with the singleton Toolbox, but I was wrong.

I should also have explained what I mean by true global var. I actually expected some people would not get it. Heck, I still don’t quite get it. What I mean is there is no global scope (other than classes). Thus, no global members. And static members would not be the same thing because they are in fact in side of a class. But, in practice, what all that means? Well…

Turns out it means basically nothing. For many circumstances I believed static wasn’t a global var. But it is. There are some few differences with that “true global var concept” on the way it’s allocated in memory, so there are cautions to take but that’s just if you are writing unsafe code or doing some sort of heavy interoperating with unmanaged code.

So this whole question is mostly dull (or even d’oh). Sorry.

I have almost 15 years of programming experience and it is very useful indeed to have global singletons. It is commonly taught in schools that global variables are ‘bad’ because the novice programmer may try something as such.

public static int a;
public static int b;
public static int c;

void AddNumbers() { c = a + b; }

void Main() {
  a = 5; b = 3;
  AddNumbers();
  Debug.Log(c); // should output 8
}

As you can see there are issues here. For example another thread may change a and b before the AddNumbers() function is called, not to mention it is confusing to read.

However global singletons are great.

public class HUD {
  public static HUD instance;
  public void Awake() {
     if(instance) {
        Debug.LogError("HUD already instantiated!");
        return;
     }
     instance = this;
  }
  public static void Add(HUDElement element) {
    ....
  }
  ....
}

public HUDElement {
  public void Awake() {
     HUD.Add(this);
  }
}

Singletons are good if the application needs exactly one of them. Some built in unity globals are Time.deltaTime, and Input.mousePosition. How are these bad?

As for using my own global variables(which change) and not just singletons(which are set once), I have used a mouse picker to let the user select an object. I set a global variable called ‘selected’. All the rest of my code can access the variable as UserInterface.selected. I think this coding practice is fine and has served me well. Believing that globals are bad is a common misconception.

EDIT >>

Looking at your question again SilentSin are you more asking about persistency in Unity rather than globals in c#? Unity destroys gameobjects when you load another level. So if you do have a singleton with a public static gameobject or component then it will be nulled when the level changes. You could put all your info into a static non-unity class.

public class PersistantInfo {
public static PersistantInfo instance;
members…
}

All the members will keep their data unless they are monobehaviours attached to gameobjects in the scene. To save these you need DontDestroyOnLoad();

You can also use OnLevelWasLoaded(int level) and Awake() { Application.levelLoaded } to better manage who to destroy and who to let live.

So maybe

class Player : MonoBehaviour {
  void Awake() { 
   DontDestroyOnLoad(gameObject); 
  }
  void OnLevelWasLoaded(int level) {
    switch(level) {
      case startMenu: Destroy(gameObject); return;
      case level1: MoveToLevelStart(); return;
      case level2: MoveToLevelStart(); return;
      case gameOver: Destroy(gameObject); return;
    }
}

Make sure Player is attached to a top level scene object. DontDestroyOnLoad does not work if the parent object is destroyed.

“The problem with A.Value if nothing else, is conceptual. It only exists while A exists. Guess what? If A inherits from MonoBehavior it can be destroyed and A.Value becomes null. And this can easily happen by accident. If you write code thinking “There, I just created the static as global and as such it’s all fine, it won’t lose its value unless I explicitly say so”. Not that simple. Not a true global var.”

Your assumption is misguided.

Instances of a class have nothing to do with that class’s statics. Destroying an instance will not null any static variables unless you have code that explicitly does so in OnDestroy().

“And there will be rare but useful cases where they can be properly applied. Just not if you want global vars.”

Maybe it just depends on what kind of project you’re working on, but for an action RPG it seems that it’s not really a rare case that they are useful.

Many of my scripts variables like all the player information for the custom stats based on your armor, your skill tree, where you spend your stat points, etc. they all need to be accessed by multiple scripts and in multiple scenes. There is just one instance of these variables in the entire project and there are many of these variables. There would be no reason not to use public static variables in a case like this, hardly a rare case.

I also have all kinds of other variables that must be easily accessed from any script and that will only ever have 1 value at any time during game play. Things like option menu logic to make sure that the multiple menus and panels never cause any conflicts or interference with each other.

For example:

public static bool gamePausedBool = false;
public static bool mainPauseMenuBool = false;
public static bool optionsMenuBool;
public static bool inventoryBool;
public static bool playerStatsBool;
public static bool skillTreeBool;
public static bool glovesEquippedBool = false;
public static bool bootsEquippedBool = false;
public static bool helmEquippedBool = false;
public static bool chestEquippedBool = false;
public static bool beltEquippedBool = false;
public static bool storeBool = false;
public static bool dropItemSelectedBool;
public static bool itemSlot1AvailableBool;
public static bool itemSlot2AvailableBool;
//etc	

All of those are just for my GUIButtonsScript

I agree with Hoeloe that it is very useful to just set up a GlobalDataScript where you keep the majority of your global public static variables organized. You can load up all these variables locally from the PlayerPrefs on Awake() and then set new local private variables for all the other scripts that regularly use them in the other scripts Start() functions.

Here’s an example of some of my GlobalDataScript stuff, just trying to give a good example of how they are useful to me. I heavily rely on them and they work perfectly, so I don’t quite understand what’s the problem with them.

using UnityEngine;
using System.Collections;

using System.Collections.Generic;

using System.Linq;

using System.Text;

public class GlobalDataScript : MonoBehaviour 
{
	public static bool videoPlayingBool = false;

	public static int globalTotalHealthPotions;
	public static int globalTotalManaPotions;
	
	public static int globalTotalCrystals;
	
	public static int globalLevelsUnlocked;
	public static int currentLevelInt;
	
	public static int inventoryItemSlotsUsed;

	public static int inventoryFirstItemSlot;
	public static int inventorySecondItemSlot;
//etc

        public static int inventoryFirstItemType;
	public static int inventorySecondItemType;
//etc

        public static int inventoryFirstItemID;
	public static int inventorySecondItemID;
//etc

        public static int globalPlayerStrength;
	public static int globalPlayerDexterity;
	public static int globalPlayerIntelligence;
	public static int globalPlayerVitality;

public static int globalPlayerArmor;
	
	public static float playerXPTotal;
	
	public static int playerStatPointsAvailable;
	public static int playerSkillPointsAvailable;
	
	public static int playerLevel;

	public static int playerAtStoreInt;
	
	public static int tierOneArmorPiecesFound;
	public static int tierTwoArmorPiecesFound;
	
	public static int tierThreeArmorPiecesFound;

public static int monsterKillCount = 0;

	public static bool loadingScreenImageBool;
	
	public static int arenaLevelsUnlocked;
	
	public static bool globalStoryActiveBool;

public static float playerHealthBeforeArmorChange;
	public static float playerEnergyBeforeArmorChange;
	
	public static float globalDebugCounter;

	public static int globalBloodSettings;
	public static int globalMusicSettings;

	public static int playerHasPetInt;
	public static int playerPetTypeInt;

//set all variables on Awake()
//allows easy access and storage of large amounts of global variables
//can all be used locally with new private variables on any other scripts via Start()

void Awake()
{
globalTotalHealthPotions = PlayerPrefs.GetInt ("Total Health Potions");
		globalTotalManaPotions = PlayerPrefs.GetInt ("Total Mana Potions");
		
		globalTotalCrystals = PlayerPrefs.GetInt ("Total Crystals");
			
		globalLevelsUnlocked = PlayerPrefs.GetInt ("Levels Unlocked");
		arenaLevelsUnlocked = PlayerPrefs.GetInt ("Arena Levels Unlocked", 0);

//etc

}

Had to make this into an answer since it wouldn’t fit as a comment. But I guess this is a pretty good example of things you can do where public static variables are extremely useful and easy to use to store variables globally. Even if they aren’t technically global since they belong to the classes, I still feel like they accomplish the same thing as a global variable would when used correctly.

I’ll have to look at some of those other discussion posts a bit later to delve a little deeper into what kind of disadvantages there would be to using public static variables, but it seems to work fine for me.

Here I just wanted to give an example of how I use public static variables. For me they seem like the exact opposite of a rare case, and it’s not hard to apply them properly.

I mean all these variables I listed are just from 2 scripts, I use a lot more public static variables than this and I use quite a lot of public static functions as well which generally just bring up an instance to the local stuff like:

//enemy script and when enemy dies it calls this:

if (XPUpdateBool == false)
{
XPUpdateBool = true;
PlayerXPScript.XPUpdateCall();
}

//on PlayerXPScript

public static PlayerXPScript XPInstance;

void Start()
{
XPInstance = GetComponent<PlayerXPScript>();
}

public static void XPUpdateCall()
	{
	XPInstance.XPUpdateFunction();
	}

void XPUpdateFunction()
{
//tons of local code which only updates the global public static variables and playerprefs when changes occur.
}

You say you can’t give a small example of what you can’t do in C# but here’s a pretty huge example of what you can do. I can’t think of any easier way for me to use all these “global” variables throughout my project even if they aren’t technically global. If I just have to take one extra step to type the name of the script in front of the variable that’s not a big deal to me.

Wrong (thanks @SilentSin for this link). static is in fact a way of doing global var (in csharp). But it’s the worst way.

Singletons are much better than static members. As the already linked link explains, using static members as global vars go against OOP.

This means once you set a static member you can’t pass it around as an object. The more you use static as global var, the more difficult it is for unit testing / mocking classes.

But singletons are often abused, so be very careful when using them!

So…

Toolbox is probably the best work around for global vars:

public class MyClass : MonoBehaviour {
	void Awake () {
		Debug.Log(Toolbox.Instance.myGlobalVar);
		Debug.Log(Toolbox.Instance.language.current);
 
		// Optional: allows runtime registration of global objects
		MyComponent myComponent = Toolbox.Instance.GetOrAddComponent<MyComponent>();
		Debug.Log(myComponent.anotherGlobalVar);
		Destroy(myComponent);
	}
}
 
public class MyComponent : Component {
	public string anotherGlobalVar = "yeah";
}

Beware to use it almost never, for almost nothing. Keep in mind global vars should be avoided as much as possible!