Sunday, January 14, 2007

XNA game structure thoughts

I've seen a few posts and articles around the net with recommendations on how to structure an XNA game, for example this great post at Cornflower Blue.

Most of these revolve around the same idea of building an own rolled Game Component collection class as the backbone for your game's partitioning, e.g. Startup:GCCollection, LevelSelect:GCCollection, InGame:GCCollection, HUD:GCCollection and so on. The collection usually implements at least Update and Draw, propagating the calls through the internal collection, and in most cases is actually derived from Game Component its self.

I'd like to propose a slightly different approach.

Back to the original reasoning for the problem:
  • XNA games want to stick to the Game class's methodology, i.e. Initialise, get LoadGraphicsContent and UnloadGraphicsContent called at the right times including any adapter malarkey, make sure Load is called before Draw, and conversely that Draw is never called when unloaded.
  • The most obvious and arguably the easiest way to do this is to implement Game Components. Bonus: sticking to Game Components should make code more recyclable!
  • Games tend to be easy to partition, usually with pretty huge chunks belonging naturally with each other, e.g. the level select screen's background and OK button belong together, while neither appears when a game is in progress.
  • Micro managing each Game Component would suck. Adding and removing each at every transition is error prone, and there may be any number of other operations that need to be done to them as a group that would similarly suffer.

These definitely seem to point at having a collection class around to hold the groupings, but here's where I diverge: why do more than the problem requires? Why reimplement Update and Draw iteration over the collection, and in the process actually lose the Order and DrawOrder functionality? Who's to say you won't want these interleaved between the partitions, e.g. Portions of the HUD group rendering and updating in between portions of the game world group?

What I reckon we really need is just a collection through which to issue orders to all its members. We're looking for more of a set really. So I propose something like the following:

    class GameComponentSet : List<GameComponent>

    {

        GameComponentSet( Game game )

        {

            this.game = game;

        }

        private Game game;

 

        public void Activate()

        {

            foreach ( GameComponent g in this )

                game.Components.Add( g );

        }

 

        public void Deactivate()

        {

            foreach ( GameComponent g in this )

                game.Components.Remove( g );

        }

    }



With a few of these we can manage our logical partitions as groups, but we don't chuck the functionality that is hidden away inside the Game class.

While Activate and Deactivate are pretty plain here, they serve as entry points for all manner of verification. For instance in Activate, you could check to see if the component is already in the game's Component Collection, and deal with the situation as you see fit, effectively adding in a bit of late checking to see if a Game Component is mistakenly in two sets at once.

Specific to each game, we could add other registration functions and properties here as well, e.g. RegisterPhysics to add all the Game Components to your physics manager, or RegisterUI to pass them onto your UI manager. Obvious additions would be the Game Component Enabled and Visible properties. Sure they may end up as just simple function/property wrappers around foreach loops, but as with the above Activate sample, it's always handy to bottleneck this sort of thing just in case you need to address it later.

Clearly we can also derive from GameComponentSet and make specific versions, e.g. UIGameComponentSet, PhysicalGameComponentSet, AIDrivenGameComponentSet, and so on.

Confession: The above simple sample deriving from List<>, while serving as an easy illustration, isn't entirely robust. The problem lies in the being able to change the collection after you've called Activate. Further evolution of this class would probably want to hide the collection implementation internally, and validate for this.

The only problem we face now is how to control the unloading of graphics content for our sets. Frankly, I have no clue how this is supposed to work. The function its self is not public, and it seems that the only way to invoke it is to lose the graphics adapter. I've taken to adding my own public interface addition to classes derived from DrawableGameComponent to chain the call over to the protected version. If anyone knows how this is meant to function, could you please shed some light here?

Finally, while admittedly it is more runtime work to add and remove all the members of the list individually rather than enabling or disabling a single entry point to them, bear in mind that the cost should still be really small, and rarely paid: how often do you reckon you'll want to turn entire modes on and off! For smaller sets, the difference should be so small, that its perfectly viable to use the same scheme more often, e.g. for HUD mode changes like overhead map on/off.

2 comments:

Karl Fredberg said...

If you have some time over could you make a simple sample of this? I think there is alot of people interested in this design topic.

Cheers

Gamey Little Hacker said...

You know I'd love to, but I haven't really been using it. Anything else I could say on the matter would be academic.

As it stands, all the projects I've dabbled with so far have been quite happy to just build what they needed, then flushed everything away between modes.

They've had more exotic and problem specific solutions for doing that, so I haven't really needed a generic solution yet.