Advertisement

Global Variables

Started by November 22, 2018 03:41 AM
11 comments, last by Gnollrunner 6 years ago

I'm a bit confused by what would be the best way to handle this. I have always been taught that global variables are a big no-no except when absolutely necessary and only as a constant. So, if you have a game with tons of objects that all need to be kept track of, and have to be shared between multiple files, what do you do? Do you just have to make a few data-structures and use them as globals regardless, or is there a better way?

29 minutes ago, RidiculousName said:

I'm a bit confused by what would be the best way to handle this. I have always been taught that global variables are a big no-no except when absolutely necessary and only as a constant. So, if you have a game with tons of objects that all need to be kept track of, and have to be shared between multiple files, what do you do? Do you just have to make a few data-structures and use them as globals regardless, or is there a better way?

I always make classes in their own files and include them into projects as needed. If you need to share data then you would either create a manager class for whatever operations you need, or just pass the objects by reference as needed. My answer is based on C++ of course. ;) 

Programmer and 3D Artist

Advertisement
1 hour ago, RidiculousName said:

I'm a bit confused by what would be the best way to handle this. I have always been taught that global variables are a big no-no except when absolutely necessary and only as a constant. So, if you have a game with tons of objects that all need to be kept track of, and have to be shared between multiple files, what do you do? Do you just have to make a few data-structures and use them as globals regardless, or is there a better way?

Well you have a few options:

1) Make 'em globals and ignore what everyone thinks

2) Use a singleton (and ignore what everyone thinks)

4) Make a special class to hold them (kind of like a singleton, but it doesn't piss people off so much) . But then you have to figure out if the class object itself will be global or you're going to pass around it's address.

4) Put them in a namespace. This is kind of like putting them in a class but you don't have the option of passing it's address

5) Make them static and put them in a global class object. Pretty much the same as the namespace option (but you might piss people off again)

6) This one you have to think about a bit........Sometimes you will find that one of your global variables is really kind of a main object and you can add the other variables into that one as members.

I usually try to figure out which option makes the most people angry and go that direction ?

3 hours ago, RidiculousName said:

So, if you have a game with tons of objects that all need to be kept track of, and have to be shared between multiple files

Pass references/pointers to the list of objects around to the functions that need access to them.  For classes which need a long term reference, store a reference in a member.  For short lived references, pass as a parameter and use as a local.

This is typically easier to deal with in garbage collecting languages because there are fewer ways the concept of ownership matters.

11 hours ago, RidiculousName said:

I have always been taught that global variables are a big no-no except when absolutely necessary

I think I'm doing something wrong here ? I sometimes use globals, I haven't made anything 'massive' so I've not seen any issues. I would have thought in the case of needing to keep track of data between loads of objects a global would be great for this? What's the downside? :/ 

9 hours ago, Gnollrunner said:

I usually try to figure out which option makes the most people angry and go that direction

I like your style ?

Check out my game '3NDL3ZZ: Base Defense' A simple militant styled, tower defense game.

GameDev.Net Project Page

11 hours ago, RidiculousName said:

So, if you have a game with tons of objects that all need to be kept track of, and have to be shared between multiple files, what do you do?

  1. Try to minimize those dependencies as much as possible, right down to the function level. Does your class need a thing you want global? Try to make it so that only a few of its functions do. That way you can pass the "global" in as a function argument. If class A has functions foo and bar, but only bar needs a reference to class B, don't make class A store a reference to class B!
  2. Try to keep your function call hierarchies as shallow as practical. Instead of A calling B that calls C, try to rework your code so that A, B, and C are called from the same place, in sequence. This way, if C has a dependency that A and B do not, you don't have to pass that dependency to A and B.
  3. Have a section of your code that exists just to glue things together, and put your global objects there. Your main event loop or some kind of "app" class is the place I would typically put this. You would invoke your actual business logic from the glue code, without the business logic knowing more than it absolutely needs to about the glue code.
Advertisement
13 hours ago, Gnollrunner said:

Well you have a few options:

1) Make 'em globals and ignore what everyone thinks

2) Use a singleton (and ignore what everyone thinks)

4) Make a special class to hold them (kind of like a singleton, but it doesn't piss people off so much) . But then you have to figure out if the class object itself will be global or you're going to pass around it's address.

4) Put them in a namespace. This is kind of like putting them in a class but you don't have the option of passing it's address

5) Make them static and put them in a global class object. Pretty much the same as the namespace option (but you might piss people off again)

6) This one you have to think about a bit........Sometimes you will find that one of your global variables is really kind of a main object and you can add the other variables into that one as members.

I usually try to figure out which option makes the most people angry and go that direction ?

Thanks! I think I'll try putting them in a special class. I am not an experienced enough programmer to know what a singleton or a namespace is exactly. Out of curiosity, is all of this possible in Java?

Use the (sometimes intimidating-sounding) pattern of dependency injection when possible. That simply means having a parameter or member that refers to the other system, which you can replace with the main system. Remember and follow SOLID principles.

There are some systems that are widely used or cumbersome to pass around. In those situations dependency injection can be a pain. A common pattern is having a well-known instance, which is far less nasty than general globals, static objects, or Singletons, introducing a visible coupling point with established dependencies.

You create a single global object that is nothing more than empty container for those systems. Those systems are created or destroyed at well-established times, and are available for the duration of the main program running.  This lets you avoid problems like static initialization time that happens before main is run, unspecified initialization order that comes with global variables or lazy-constructed static instances, and allows multiple instances of objects that is anti-Singleton pattern.

You still need to be careful about hidden couplings between systems, since hidden dependencies and values that change without access control can still cause nightmares. Even so, most of the worst problems can be avoided through a bit of discipline.

All of this is perfectly compatible with Java.  Have your container as a member, and create the new instance when Main starts.  

19 hours ago, RidiculousName said:

Thanks! I think I'll try putting them in a special class. I am not an experienced enough programmer to know what a singleton or a namespace is exactly. Out of curiosity, is all of this possible in Java?

Ahh well Java the Hutt...... It's not my forte but....... I believe you have statics (members that are per class instead of per instance) and you of course have classes.  If I remember everything has to be in a class in Java anyway so you don't really have globals (by design), but you can cheat by making a public static in a class and that should more or less do the same thing.

Also you should be able to build a singleton which is nothing more than a class that only allows one instance of itself to be created.  So for instance, in the constructor you could set some flag which is a static member of the class and check to see if it's already been created.  Typically you will also have some static function called "Instance", "GetInstance" or some such in your class that returns itself. The first time it's called you can create and/or initialize you class. On subsequent calls you simply return the existing class. 

Alternatively depending on what you are doing you might just create the singleton instance on startup so you don't have to do any run-time checks, but..... you have to be careful with that because if you are using multiple singletons the order of initialization might be import.  This is kind of true with gobbalish stuff in general. The advantage of run-time initialization is you mostly don't get in trouble with the order because you are initializing explicitly through "GetInstance".

Most of the time you don't really need a singleton. It's often just a means of protecting you from yourself.   However it does have the beneficial side effect of enraging some purists, so it might be worth considering. 

I have never seen a case where a Singleton was necessary, or was the best solution. Not one in nearly three decades. Singletons have a long list of problems, well documented elsewhere, from initialiation to destruction, from promiscuitiy and hidden dependencies to the brittleness of being forever bound to an instance. An alternative may take a little more effort, but they prevent an enormous number of headaches.

A well-known instance as described above can help with most of those.  You need to follow patterns to know when they are initialied, when they are valid or invalid, when and how instances may be swapped out, but they can work well enough.  Common examples of these are the well-known instances of the main console input/output streams, debugger connections, or loggers. However, they are not Singletons as you can have additional console streams, can have additional or alternate debugger connections, and can have multiple independent loggers.  

You can use a well-known instance to reproduce many problems Singletons can have, like shared mutable state, but thats a different problem. You can misuse or abuse just about any pattern or practice. Study them so you can learn the dangers, then be responsible and disciplined to use them properly.

This topic is closed to new replies.

Advertisement