Where do you keep global variables?

Started by
34 comments, last by Bob Janova 17 years, 6 months ago
This might be a stupid question, but I still haven't come up with a good code design for a game. People tend to dislike global variables, but how should I organize my project? Things like the directx device, camera, player actor reference, camera location, frustum, resource manager etc will be used all over the project. I don't think keeping a reference to each of these things in every object is a good solution eighter. Should all of these things be passed as arguments? If I then add one more "global object", I will have to update hundreds of functio calls. Should I create singletons for each object? Or perhaps create an abstract kind of class named renderer, engine or something like that, make it a singletone and let it own all the managers and global objects such as camera, frustum etc. However do I really want to have the "renderer-like-class" to keep a reference to the player actor? Should there be some other singleton to keep more specific game related globals too? I'm confused. I can't come up with a good solution for this :(
Advertisement
the camera, dx device frustrum etc will probably only be used by your renderer right ?
it might possibly be modified by some other part (game logic moving the camera) but that should be done using methods i guess.

i usually keep the player actor reference in the games main class along with all other actors though not always (depends abit on the game), in most cases i try to keep the player pretty generic so that the engine has support for multiple players or AI actors supporting the player, thus noone really needs to know about the player specifically, they just need to know where to look for other actors.

the renderer doesn't need actor information, it only needs to know about the graphical representation of the actors, the camera, the frustrum, etc.
[size="1"]I don't suffer from insanity, I'm enjoying every minute of it.
The voices in my head may not be real, but they have some good ideas!
When people say "theres too many objects to pass everything as arguments", what they're really saying is "I'm too lazy". Of course when you get over that mental gap you'll be pleasantly surprised that the problem is not as huge as you think. Most objects are kept pretty local, often you only need a single class as some kind of high-level interface. And not every system needs to talk to every other system.
Quote:Original post by DanielH
I don't think keeping a reference to each of these things in every object is a good solution eighter. Should all of these things be passed as arguments? If I then add one more "global object", I will have to update hundreds of functio calls.


Your problem stems from bad design of your functions, and not from data lifetime and accessibility. When you write a function, you decide what it will do. Updating a function means adding arguments to it, which can only occur if your function's purpose was incorrectly described in the first place — because you decided to have it do more work than previously thought. This is usually a bad idea: write your functions once you know how you will use them (so you don't need to add new functionality) or write a new function to perform a new task, perhaps using the previous unmodified function to do part of that task. Changing the contract of a function, whether by adding more arguments or by having it access global variables, is an excellent way to introduce bugs. I would honestly rather have to run through all places where the function is called, reading and correcting them, than having the expectations of a hundred function calls grossly violated without checking because I used a global variable.

Another problem you have is the hiding of information. A renderer class should keep references to all actors, because its job is rendering all currently visible actors. That one of these actors is, in fact, a "Player Actor" holds no relevance at all to the renderer, because a renderer cares about "rendering", not about a "player". Introducing "player" information where it does not belong requires you to create more special-case code without any associated gains — even if a Player Actor global exists, it's simpler to just handle it as any other actor and have the renderer reference it anyway. Things like a frustum should never be visible outside the renderer, because they're irrelevant to non-rendering code. A camera is a rightful property of a renderer. A resource manager may be associated to a renderer (at which point it would be a member) or even owned by it (at which point it might be made completely invisible to the user).

You should try to create a game or program without using a single global variable: it's a very good learning exercise in software design, which forces you to actually tackle ownership and composition issues, instead of sweeping them away under the "it's global" rug and get bitten in the rear later on.
The problem is that I need access to the frustum in for example my QuadTreeNode class and I have a class that is derived from a GameObject that render terrain patches. This class needs to know where the camera is to determine distance in the LoD calculations. So it's not as simple as just putting it all in the renderer class. So you mean I should pass all the needed arguments around? Like renderer, scene graph, frustum, camera etc to every update, render and device event functions? That would require insane amount of work and be really ugly! I'm coding in C# by the way. Would it really be such a bad idea to keep a totally static class with globals (renderer, camera, managers...)?
A single game engine can be done as a single global (or singleton if you prefer).

I don't believe being a purist and not-making any[b/] globals is advantageous.
It becomes a chore having to pass the objects down the hierachy to the smallest object (for example if a GUI button required sound/input/graphics subsections of the engine).

One reason not to use globals is that tracking down bugs becomes harder.
This can be partly solved by making accessor classes that enumerate all the classes which access a certain section of the engine.

For example if I wanted to use the Input subsystem (mouse/keyboard) I would derive from InputListener class which would provide a method to access the Input sub-system of the global/singleton game engine. Whenever a InputListener is instanced in debug mode it could log its access (sufficiently advanced error logging helps).
Quote:Original post by stevenmarky
for example if a GUI button required sound/input/graphics subsections of the engine


*BUZZ!* Bad design, sir. A GUI button should only be responsible for its being pressed and unpressed. The owner of the button, though (which would have access to said subsections) can bind the button's onClick event to what the button is meant to do.

Quote:Original post by DanielH
The problem is that I need access to the frustum in for example my QuadTreeNode class and I have a class that is derived from a GameObject that render terrain patches. This class needs to know where the camera is to determine distance in the LoD calculations. So it's not as simple as just putting it all in the renderer class. So you mean I should pass all the needed arguments around? Like renderer, scene graph, frustum, camera etc to every update, render and device event functions? That would require insane amount of work and be really ugly! I'm coding in C# by the way.


So, rendering certain GameObjects requires access to the Camera class. Pass the camera to them in their constructor. That way, no unnecessary passing of cameras occurs at all once you start rendering, and the use of the camera by that kind of GameObject is made clear.

Quote:Would it really be such a bad idea to keep a totally static class with globals (renderer, camera, managers...)?


Look at it this way: you decide to store a stick of dynamite next to a fireplace, and ask "Would lumber be a good replacement for the walls once they blow up?" Yes, lumber (global variables) are probably the best you can use in the situation where your walls have been blown up (very bad, vague and redundant design). However, not keeping a stick of dynamite next to the fireplace (using a correct design) would eliminate the need for rebuilding (global variables) altogether, in addition to making your life easier.

Quote:Original post by ToohrVyk
Quote:Original post by stevenmarky
for example if a GUI button required sound/input/graphics subsections of the engine


*BUZZ!* Bad design, sir. A GUI button should only be responsible for its being pressed and unpressed. The owner of the button, though (which would have access to said subsections) can bind the button's onClick event to what the button is meant to do.


Sorry, I meant 'a particular class of GUI button' e.g. class PlayAnimalSound derives from class Button. I didn't mean my generic button class should be responsible for anything other that monitoring being pressed and unpressed (and perhaps being drawn).

You should think really carefully the function of each class.

For example, I have a gfx class which does all the actual rendering (it is implemented through an abstract interface so that the program doesn't have to know anything about the rendering or the API).

For example, I keep my mesh data in a generic geometry class. When the mesh data is loaded, I give this geometry object to the gfx class and ask it to create a renderable object from the geometry. This way I am able to hide the implementation of the drawing from the program.

Try to avoid singletons. Even if it seems unlikely that you never need another instance of the object that day might come. Using singletons might lead to troublesome dependancies and the destruction of a singleton object isn't always clear. You can still have a class that contains the pointers to most of the interfaces in your program. Of course, at this point it is good to remember that all the objects shouldn't be able to do everything.

- So, try to make the classes in a way that they practically do only one thing.
- Share the duties, collision handling shouldn't be related to graphics.
- Keep thing clean, for example : no other class than the renderer should know about drawing details, locking buffers, etc.
- You might need several resource managers (renderer might need one for textures, meshes. sound system for audio samples etc).
- etc.

Cheers

This topic is closed to new replies.

Advertisement