2
\$\begingroup\$

I'm hoping that some experienced programmers can give me their point of view. I'm writing a large game in Windows with dx11. So far, I've got global objects of the class that interfaces with dx11, the class holding the position of the camera, the terrain data class, and a few other classes. In future, if I kept going with this way, I'd also have objects for all the game data, and so on.

I'm thinking it might be best to rejig the code to only have local variables, because generally this is regarded as a Good Thing, but mainly because I personally have come to prefer using them. Here's some rough c++-ish pseudo-code of how I imagine local variables would be implemented:

WinMain()
{
  cDirectX dx;
  cCamera cam;
  cTerrain terrain;
  ...
  while (msg.message != WM_QUIT)
  {
    ...
    gameLoop(dx, cam, terrain, ...)
  }
}

void gameLoop(cDirectX &dx, cCamera &cam, cTerrain &terrain, ...)
{
  ...
  renderTerrain(dx, terrain);
  ...
}

Is this (very roughly!) a good way to organise such a project? I suppose WinMain is the only function where the locals can be instantiated?

The only disadvantage I can see is that top level function calls (by which I mean, functions like gameLoop(), functions at the bottom of the call stack) will have very long lists of arguments. Perhaps this doesn't matter.

Thanks, Paul

\$\endgroup\$
2
  • \$\begingroup\$ "the class holding the position of the camera, the terrain data class, and a few other classes" There is absolutely no reason for those things to be global. \$\endgroup\$ Aug 27, 2013 at 12:07
  • 1
    \$\begingroup\$ more importantly, which method will help you finish the game? \$\endgroup\$
    – wes
    Aug 27, 2013 at 12:09

2 Answers 2

4
\$\begingroup\$

The thing that breaks complicated programs is not complexity. It's dependencies.

The main advantage of not using singletons, globals, etc etc is that it significantly reduces what I've termed "spidering dependencies", where global variables are used throughout the code. Making any modification to how those systems work fraught with the possibility that some other downstream system may be relying on a particular behaviour.

So, what you want to do is work on minimising dependencies between subsystems.

For example, I've seen many games where randomly within the physics system the global HUD subsytem will be retrieved and have data poked to it, or worse, animations in the HUD started from the physics system itself, meaning any change to the way the HUD spawns animations may break something in the physics engine!

There's a few ways to "fix" this. All solutions stem from clearly defining the boundaries for your subsystems, and understanding what data needs to flow in, and out of them.

I'm a big fan of asynchronous messaging as part of this solution, as it removes the concept of "immediate" from the game engine. You have to request things from other subsystems, which will be fulfilled at some point in the future. So your systems must be able to deal with an intermediate state. (eg: resource requests result in an intermediate "ghost" resource which is handed across while the actual resource is loaded).

I'm also a fan of using intermediate layers between subsystems, such as our HUD and physics engine. The HUD should never be responsible for changing game state directly (it should post an asynchronous message when something has happened which is then handled by the game state) and it should certainly not know about anything in the physics engine. Our physics engine still needs a way to, for example, post the speed of the player to the HUD. So we can achieve this by posting to an intermediate layer under a "key". the HUD can then query this layer using the same "key" to retrieve the data. The HUD then does not know, or care about how the data reaches this layer, just that the data is there.

So, TLDR: try and minimise dependencies. Removing the use of globals is a good step. However, if you're just making a small game this may be more overhead than it saves, but always focus on keeping unnecessary dependencies to a minimum!.

\$\endgroup\$
10
  • \$\begingroup\$ You should emphasize the intricacies of using an asynchronous messaging system and its overheads for a real-time, FPS intensive application. Although it's very elegant, you should've mentioned at least that it's a bit more complicated than singletons and that it adds overhead. \$\endgroup\$
    – teodron
    Aug 27, 2013 at 14:12
  • \$\begingroup\$ I've worked on 60fps console games which used the above approach. The overheads are more in that as a programmer you have to often do more "work". The advantage is that you often spend less "work" later fixing odd problems due to dependencies. I could write a book on this stuff. But I'll add more if necessary tomorrow. \$\endgroup\$
    – Matt D
    Aug 27, 2013 at 14:15
  • \$\begingroup\$ Well, but I guess everyone that has worked on such kind of games knows how complicated is to modify things later on, especially if you didn't write your own code. People often are too religious about this stuff and make things so decoupled that it makes debugging hellish. Following the flow is not as easy, so maybe there's a point where decoupling needs to be sacrificed for maintainability. Also, throwing messages and packing information that different system digest can create a bit of fuss when in the latter phases of optimisation.+1 from me (it's hard to mention everything in 1 answer). \$\endgroup\$
    – teodron
    Aug 27, 2013 at 14:23
  • \$\begingroup\$ It gets even more complicated to modify code which is littered with globals. You're never sure about the impact of your changes. By constraining dependencies you can easily show that changes to one subsystem wont impact another. Its more work upfront, but makes later changes significantly easier. \$\endgroup\$
    – Matt D
    Aug 27, 2013 at 14:35
  • \$\begingroup\$ Isn't that possible with careful delegation of the globals? Instead of passing around pointers and/or messages/tokens, objects can also tap into providers or managers whenever needed. Of course, provided there's no need for intense synchronous operations. You can also achieve decoupling this way, but you don't have tons of references carried around, drilling the layers of individual subsystems. I say this because right now these are my main problems.. and yes, I'm also working on a huge game (we can't afford to work on consoles that support 60fps without cutting 50% of our gameplay/assets). \$\endgroup\$
    – teodron
    Aug 27, 2013 at 14:43
1
\$\begingroup\$

Well... as for DX instance, there will be only one (I suppose), so it should be declared globally. For the rest. Terrain should be part of some "model pool". So basicly, sou have all models at one place (this pool can again be globaly - as singleton). And you can obtain models from that pool. Camera goes with your scene management. Usually, there will be only one camera. But for some purposses (water, shadows etc.) you may have different camera (projective and view matrix resp.). In that case it will be better to have some kind of rendering manager, that will hold informations, what you are rendering and which camera to use.

In my engine, I do it this way:

SceneManager.setRenerer(dx); //I have engine multiplatform, so there also could be gl, gles...all rendering is done via abstract classes and main functionality is hidden for "user" of engine
SceneManager.addRenerableObject(terrain, "Terrain");
SceneManager.addRenerableObject(house, "House#1");
SceneManager.addRenerableObject(player, "Player");

SceneManager.bindPlayerToObject("Player")

SceneManager.UpdateCameras(); //each camera is updated based on binded player position and rotation

....

SceneManager.RenderAll();
\$\endgroup\$
3
  • 1
    \$\begingroup\$ Just because there's only one instance of something doesn't mean it should be global or a singleton. One reason for avoiding globals would be to better control dependencies. \$\endgroup\$ Aug 27, 2013 at 12:45
  • 1
    \$\begingroup\$ Yes... but on the other hand, global singleton is accesible everywhere and there is no need ot pass pointer everywhere (which is sometimes anoying). I dont see nothing wrong with it, if I know that there always will be only one reference and never more. \$\endgroup\$ Aug 27, 2013 at 12:54
  • 1
    \$\begingroup\$ I'll freely admit I use singletons in my code more often than I should, but here's one example: Let's say you want to carefully control the order in which things are initialized, and possibly even have a WinForm before the main game starts to show settings; or, break down the renderer and leave just that form upon exit. Or, you have one viewport (as a global) and decide to support splitscreen. Generally, I think keeping yourself more object-oriented allows future expansion. \$\endgroup\$
    – Katana314
    Aug 27, 2013 at 13:49

Not the answer you're looking for? Browse other questions tagged .