Jump to content

  • Log In with Google      Sign In   
  • Create Account


System-wide data and systems - best practices?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
4 replies to this topic

#1 Kylotan   Moderators   -  Reputation: 3333

Like
1Likes
Like

Posted 22 October 2012 - 12:41 PM

Most of us know that Globals and their well-dressed friends Singletons are often a sign of bad design. But recently I've found myself in situations where global access to certain variables and functions seem like a good idea, because passing in each possible instance to every function that might need it becomes unwieldy. But I'm not sure if I've just been away from C++ long enough to forget the better alternatives.

Examples:
  • a random number generator. You may have your own system (rather than rand() or some other standard library) and presumably you don't want to instantiate such an object in each routine that needs one. If you did, you'd need to seed it with something different on each occasion. Which leads me to...
  • the time. You'll probably use a system routine to get the real world time but what about the current in-game time?
  • pseudo-constant values that you read in from a config file, or which a designer might tweak during play - if you store them in one large dictionary of values, that probably has to have global visibility - but if you instead were able to inject the values directly into the subsystems that needed them, you could avoid that, at the cost of the reader/parser needing access to all the separate systems. What is best?
Usually I have tried to avoid putting things in global scope as it creates a lot of coupling that would otherwise not be there. But having had some experience of game frameworks like Flixel and Unity recently, I've seen that they provide various globally accessible values that get the job done, not just for the examples above but also for querying all objects in the world, getting system info, screen resolution, etc.

What are your thoughts on globally visible utility classes and variables, and where do you draw the line between convenience and low coupling?

Sponsor:

#2 frob   Moderators   -  Reputation: 19635

Like
1Likes
Like

Posted 22 October 2012 - 02:20 PM

Singleton are not the same as globally accessible data, so lets not confuse those in the mix. Saying "there can only be one, ever, and it is enforced by code", is a singleton. I think we can all agree those are bad for reasons that have been discussed way too many times.

As for globally accessible objects, there are many of them that make sense.

Certain read-only data is great for that. Pointers to the simulator, pointers to the game configuration or game state, a pointer to the current time object, and other items that are typically read-only, mostly invariant, stateless, etc., those things are great for a globally-visible structure.

We've had various incarnations of this global game data structure on every game I've been on.

Edited by frob, 22 October 2012 - 02:22 PM.

Check out my personal indie blog at bryanwagstaff.com.

#3 Kylotan   Moderators   -  Reputation: 3333

Like
0Likes
Like

Posted 22 October 2012 - 03:50 PM

Singleton are not the same as globally accessible data, so lets not confuse those in the mix.

In theory, you're right. In practice, pretty much everybody's implementation of a singleton exposes a public static interface (in C++/Java terms) to a usually-mutable object, and that's because the common use-case seems to be "lots of different parts of the program need to access a thing, but there should only be one of those things, so make it easy for everywhere to get that one thing". But your point is taken - it doesn't necessarily have to be that way.

Certain read-only data is great for that. Pointers to the simulator, pointers to the game configuration or game state, a pointer to the current time object, and other items that are typically read-only, mostly invariant, stateless, etc., those things are great for a globally-visible structure.

We've had various incarnations of this global game data structure on every game I've been on.

So you provide read-only access to these elements (eg. pointer to const)? Is it a common situation to need to be able to read the current game state but not need to be able to modify it in any way? I can't think of many examples where that would be useful - perhaps for a GUI that just renders the current state, perhaps.

#4 frob   Moderators   -  Reputation: 19635

Like
1Likes
Like

Posted 22 October 2012 - 04:07 PM

Is it a common situation to need to be able to read the current game state but not need to be able to modify it in any way? I can't think of many examples where that would be useful - perhaps for a GUI that just renders the current state, perhaps.

The pointers are invariant once created, not const. The pointers don't rely on state. Initialization and pre-simulation code creates those pointers. During simulation anybody can read them.

For example, nothing prevents the creation of another SimClock object. Anybody can call functions like globals.simClock->GetCurrentTime();

As for testability (which is one big reason against singletons), your test setup can create whatever of these objects it needs during test setup, exactly as it would need to set up input parameters or mock objects. Nothing prevents you from creating a mock clock object.

It takes only a small amount of discipline to prevent misuse, but it isn't too hard to enforce through code reviews that everyone uses these shared well-known pointers correctly. Since a good chunk of the engine is under automated unit tests it helps reduce the coupling that could occur in an undisciplined environment.
Check out my personal indie blog at bryanwagstaff.com.

#5 Serapth   Crossbones+   -  Reputation: 5252

Like
0Likes
Like

Posted 23 October 2012 - 07:59 AM

I am a big fan of the service locator pattern ( demoed here ).

Basically instead of exposing your object globally, you expose an object that exposes your object. This keeps your systems slightly less coupled, makes supporting multiple interfaces or making changes easier, gives a single testing entry point.

Of course, it's still a global system with all the downsides that entails.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS