Quote:Original post by EJH
For example I have static SoundManager class. A ton of classes need to play sounds including: Input, UI, Buttons, Gun, Explosion, Impact, all AI classes, Player, Collision, etc. Putting a "refToSoundManager" in every one of those classes seems inferior to be able to just call SoundManger.PlaySound().
Sure. Until you need the option to control sound generation on per context-basis.
- Input and UI need simple, constant (UI setting) volume sounds
- Gun, Explosion and Impact all need to generate spatial sound. Environment generates those from their own resources
- Player generates several types of sounds. Some are spatial, others are same as UI (out of ammo warning is not spatial sound)
- Music is on different volume, so are some ambient sounds, some AI chatter, some events
- Depending on settings/hardware, you may need to limit number of sounds currently played. But this requires you to know which are important and which can be omitted. For example, rat squeek is omitted, but not if player stepped on it.
- Next, you want player to be able to play their own mp3s instead of music. To correct for volume, you hash each individual mp3 and remember player's volume setting.
- And so on....
So you end up with a huge mess of dependencies between all different subsystems which need to know about every aspect of the design.
Quote:Random Number Generator is another example. A ton of classes need to generate randoms and it doesn't seem logical that a Monster class should have a "refToRandomNumberGenerator" as a class variable.
Which works, until you go into networking, and want to share the RNG across all clients, so they advance deterministically.
Even more, RNG is needed in multiple threads. It is also used by some non-shared systems, which behave differently depending on which platform you're running on.
For example, the AI and Logic RNG needs to be completely deterministic, even though they each run in separate threads. But particle system RNG is used by arbitrary number of threads, depending on how powerful a certain machine is (1 core, 1 particle thread, 4 cores, 3 particle threads, 3 times as many particles and calls to RNG). Logic and AI use Mersenne for better results, particle system uses default, fast version. or Mersenne as well, if default RNG produces too visually skewed results. Or you choose on the fly, depending on client performance.
Problem with globals in general is that they don't scale with regard to project complexity and actually make the problem worse.
Global context can be convenient in some cases, simply due to reduced typing and implicit dependencies. But even then, it commonly saves a few characters of typing, and on some rare occasions sizeof(void*) of memory.
But those cases are so limited, or so specialized, they need to be considered individually.
The main point is - if you start with globals and singletons, changing that is really hard, or in actual projects, closer to impossible. Nobody is going to allow you to change singleton to instance 1 week before project ships, since it requires restructuring of entire code.
Contrary to regular class, which can be, at any given point be turned into a singleton using a templated wrapper, should the need for a single instance arise.