« Globals are underrated | Main | Globals underrated continued »

July 24, 2009

Comments

Bryan McNett

>It sounds like in Noel's perfect world,
>we have to copy our game state every frame -
>our update function would take the old game
>state as its input and spit out the new game
>state.

It's not a dream anymore. Multicore requires it, because one is typically working on several consecutive frames at once across the cores. A truly high performance game already requires zero globals, except a single ring buffer of frame-local data:

struct gApp
{
Frame m_frame[kFramesInFlight];
};

Charles Nicholson

It's not about the difference between

gGraphics->DrawMesh(myMesh);
and
Graphics::Inst()->DrawMesh(myMesh);

It's about constructing objects with the interfaces they need to do their work. So whatever objects you have in your codebase that need to do graphics work, you construct them with a Graphics* (or IGraphics*) and they use that throughout their lives. That reduces coupling in your client objects, and also restricts the knowledge that your Graphics thing is in fact an awful singleton/global.

Mat Noguchi

Interestingly enough, if you take the following statement: "Speaking of ideals, even if you have globals, you don't want any global to be accessed from anywhere. You do want proper layering." and follow it through, you get a proliferation of pointers (which you kind of decried before), which is really what you want. If something is intrinsically messy, you don't hide it behind globals and global-enabling accessors. Well, you do if you don't want to understand what's going on.

greggman

The ServiceLocator thing sounds complex but in actual use it's a joy and not complex at all.

It's not even complex to implement if you look at the code. It's rather short and elegant. I was skeptical myself before I was exposed to it.

Before we basically had gApp and one way or another almost every module would eventually need access to something in gApp. Then it was broken into services and now each module only needs access to the services it uses. Usually that's about 1/10th to 1/15th of what used to be in gApp.

It's helped not only with globals but also with dependencies so that changing one service means only modules that use that service get effected or recompiled. Before, since all the stuff was combined in gApp that meant changing almost anything ended up recompiling everything since gApp both included a ton of stuff and a ton of stuff included gApp.

Ivan-Assen Ivanov

We've mostly achieved "the ideal" - our renderer only pulls data from the game simulation, and the latter knows nothing about meshes, shaders, textures etc. The renderer itself is split in two parts, and what you'd call the "lower level" doesn't know about the game, it just submits render calls to DirectX.

An additional layer of separation comes from the fact that we use heavily Lua for all game logic and many "engine" parts; for example, our last few games were city building strategies, and the C++ code knows nothing about the economy - has no notion of money, buildings, units, upgrades etc; it also has no notion of dialogs, buttons, scroll bars etc.

And it feels less dirty when you access globals in Lua :-) Not to mention the language+runtime gives you the tools to e.g. inspect all globals used, automatically serialize some of them in savegames etc.

The comments to this entry are closed.

Jamie's Bragging Rights

  • Spider-Man 2
    The best superhero games of all time Game Informer
    Top five games of all time Yahtzee Croshaw
    Top five superhero games of all time MSNBC
    Top 100 PS2 games of all time Official Playstation 2 Magazine
    1001 Games You Must Play Before You Die Nomination for Excellence in Gameplay Engineering Academy of Interactive Arts & Sciences
  • Schizoid
    Penny Arcade PAX 10 Award
    Nominated for XBLA Best Original Game
    Nominated for XBLA Best Co-Op Game