25
\$\begingroup\$

I've read in many places that DrawableGameComponents should be saved for things like "levels" or some kind of managers instead of using them, for example, for characters or tiles (Like this guy says here). But I don't understand why this is so. I read this post and it made a lot of sense to me, but these are the minority.

I usually wouldn't pay too much attention to things like these, but in this case I would like to know why the apparent majority believes this is not the way to go. Maybe I'm missing something.

\$\endgroup\$
8
  • \$\begingroup\$ I'm curious as to why, two years later, you've removed the "accepted" mark from my answer? Normally I wouldn't really care - but in this case it causes VeraShackle's infuriating misrepresentation of my answer to bubble to the top :( \$\endgroup\$ Apr 6, 2013 at 9:19
  • \$\begingroup\$ @AndrewRussell I read this thread not long ago (due to some responses and votes) and thought I was not qualified to give an opinion on whether your answer is the correct one or not. As you say, though, I don't think VeraShackle's answer should be the one with the most upvotes, so I'm marking your answer as correct again. \$\endgroup\$
    – Kenji Kina
    Apr 7, 2013 at 3:38
  • 1
    \$\begingroup\$ I can condense my answer here as follows: "Using DrawableGameComponent locks you into a single set of method signatures and a specific retained data model. These are typically the wrong choice initially, and the lock-in significantly hinders the evolution of the code the future." But let me tell you what is happening here... \$\endgroup\$ Dec 8, 2015 at 14:03
  • 1
    \$\begingroup\$ DrawableGameComponent is part of the core XNA API (again: mostly for historical reasons). Novice programmers use it because it is "there" and it "works" and they don't know any better. They see my answer telling them they are "wrong" and -- due to the nature of human psychology -- reject that and pick the other "side". Which happens to be VeraShackle's argumentative non-answer; which happened to pick up momentum because I didn't call it out immediately (see those comments). I feel that my eventual rebuttal there remains sufficient. \$\endgroup\$ Dec 8, 2015 at 14:04
  • 1
    \$\begingroup\$ If anyone is interested in questioning the legitimacy of my answer on the basis of upvotes: while it is true that (GDSE being aflush with aforementioned novices) VeraShackle's answer has managed to acquire a couple more upvotes than mine in recent years, don't forget that I have thousands more upvotes network-wide, largely on XNA. I have implemented the XNA API on multiple platforms. And I am a professional game developer using XNA. The pedigree of my answer is impeccable. \$\endgroup\$ Dec 8, 2015 at 14:05

5 Answers 5

21
\$\begingroup\$

"Game Components" were a very early part of the XNA 1.0 design. They were supposed to work like controls for WinForms. The idea was that you would be able to drag-and-drop them into your game using a GUI (kind of like the bit for adding non-visual controls, like timers, etc) and set properties on them.

So you could have components like maybe a Camera or a... FPS counter?... or...?

You see? Unfortunately this idea doesn't really work for real-world games. The kind of components you want for a game aren't really reusable. You might have a "player" component, but it will be very different to every other's game's "player" component.

I imagine it was for this reason, and for a simple lack of time, that the GUI was pulled before XNA 1.0.

But the API is still usable... Here's why you shouldn't use it:

Basically it comes down to what is easiest to write and read and debug and maintain as a game developer. Doing something like this in your loading code:

player.DrawOrder = 100;
enemy.DrawOrder = 200;

Is much less clear than simply doing this:

virtual void Draw(GameTime gameTime)
{
    player.Draw();
    enemy.Draw();
}

Especially when, in a real-world game, you might end up with something like this:

GraphicsDevice.Viewport = cameraOne.Viewport;
player.Draw(cameraOne, spriteBatch);
enemy.Draw(cameraOne, spriteBatch);

GraphicsDevice.Viewport = cameraTwo.Viewport;
player.Draw(cameraTwo, spriteBatch);
enemy.Draw(cameraTwo, spriteBatch);

(Admittedly you could share the camera and spriteBatch using the similar Services architecture. But that is also a bad idea for much the same reason.)

This architecture - or lack thereof - is so much more light weight and flexible!

I can easily change the draw order or add multiple viewports or add a render target effect, just by changing a few lines. To do it in the other system requires me to think about what all those components - spread across multiple files - are doing, and in what order.

It's sort of like the difference between declarative vs imperative programming. It turns out that, for game development, imperative programming is far more preferable.

\$\endgroup\$
6
  • 2
    \$\begingroup\$ I was under the impression they were for Component-based programming, which is apparently widely used for game programming. \$\endgroup\$ May 27, 2011 at 21:50
  • 3
    \$\begingroup\$ The XNA game component classes aren't really directly applicable to that pattern. That pattern is all about composing objects out of multiple components. The XNA classes are geared to one-component-per-object. If they fit perfectly in your design, then by all means use them. But you should not build your design around them! See also my answer here - look out for where I mention DrawableGameComponent. \$\endgroup\$ May 28, 2011 at 3:03
  • 1
    \$\begingroup\$ I agree that the GameComponent/Service architecture isn't all that useful and sounds like Office-like or web application concepts being forced into a game framework. But I'd much prefer to use the player.DrawOrder = 100; method over the imperative one for draw order. I'm finishing a Flixel game right now, which draws objects in the order in which you add them to the scene. The FlxGroup system alleviates some of this, but having some sort of Z-index sorting built in would have been nice. \$\endgroup\$ Feb 21, 2012 at 3:57
  • \$\begingroup\$ @michael.bartnett Note that Flixel is a retained-mode API, while XNA (except, obviously, the game components system) is an immediate-mode API. If you use a retained-mode API, or implement your own, the ability to change draw-order on the fly is definitely important. In fact, the need to change draw-order on the fly is a good reason to make something retained-mode (the example of a windowing system comes to mind). But if you can write something as immediate-mode, if the platform API (like XNA) is built that way, then IMO you should. \$\endgroup\$ Oct 4, 2012 at 11:13
  • \$\begingroup\$ For further reference gamedev.stackexchange.com/a/112664/56 \$\endgroup\$
    – Kenji Kina
    Dec 10, 2015 at 9:36
29
\$\begingroup\$

Hmmm. Got challenge this one for the sake of a bit of balance here I'm afraid.

There seem to be 5 charges in Andrew's reply, so I'll try and deal with each in turn:

1) Components are an out-of-date and abandoned design.

The idea of components design pattern existed long before XNA - in essence it is simply a decision to make objects, rather than the over-arching game object, the home of their own Update and Draw behaviour. For objects in its component collection, the game will call these methods (and a couple of others) automatically at the appropriate time - crucially the game object does not itself have to 'know' any of this code. If you wish to re-use your game classes in different games (and thus different Game objects), this de-coupling of objects is still fundementally a good idea. A quick look at the latest (professionally produced) demos of XNA4.0 from the AppHub confirms my impression that Microsoft are still (quite rightly in my view) firmly behind this one.

2) Andrew can't think of more than 2 re-usable game classes.

I can. In fact I can think of loads! I've just checked my current shared lib, and it comprises more than 80 re-usable components and services. To give you a flavour, you could have:

  • Game State/Screen Managers
  • ParticleEmitters
  • Drawable primitives
  • AI controllers
  • Input controllers
  • Animation controllers
  • Billboards
  • Physics & Collision Managers
  • Cameras

Any particular game idea will need at least 5-10 things from this set - guaranteed - and they can be fully functional in a completely new game with one line of code.

3) Controlling draw order by the order of explicit draw calls is preferrable to using the component's DrawOrder property.

Well, I basically agree with Andrew on this one. BUT, if you leave all you component's DrawOrder properties as default, you can still explicitly control their draw order by the order in which you add them to the collection. This is really identical in 'visibility' terms to explicit ordering of their manual draw calls.

4) Calling a load of object's Draw methods yourself is more 'lightweight' than having them called by an existing framework method.

Why? Plus, in all but the most trivial of projects your Update logic and Draw code will totally overshadow your method calling in terms of performance hit in any case.

5) Placing your code in one file is preferrable, when debugging, than having it in the individual object's file.

Well, firstly, when I'm debugging in Visual Studio the location of a breakpoint in terms of files on disk is almost invisible these days - the IDE deals with the location without any drama. But also consider for a moment that you have a problem with your Skybox drawing. Is going to the Skybox's Draw method really more onerous than locating the skybox drawing code in the (presumably very lengthy) master draw method in the Game object? Nope, not really - in fact I would argue that it is quite the opposite.

So, in summary - please take a long hard look at Andrew's arguments here. I don't believe they actually give substantive grounds to write entangled game code. The upsides of the decoupled component pattern (used judiciously) are massive.

\$\endgroup\$
5
  • 2
    \$\begingroup\$ I only just noticed this post, and I must issue a strong rebuttal: I have no issue with reusable classes! (Which may have draw and update methods and so on.) I am taking specific issue with using (Drawable)GameComponent to provide your game architecture, due to loss of explicit control over draw/update order (which you agree with in #3) the rigidity of their interfaces (which you don't address). \$\endgroup\$ Jun 5, 2011 at 5:50
  • 1
    \$\begingroup\$ Your point #4 takes my use of the word "lightweight" to mean performance, where I actually clearly mean architectural flexibility. Your remaining points #1, #2, and especially #5 seem to erect the absurd straw-man that I think most/all code should be shoved into your main game class! Read my answer here for some of my more detailed thoughts on architecture. \$\endgroup\$ Jun 5, 2011 at 5:50
  • 5
    \$\begingroup\$ Finally: if you're going to post a response to my answer, saying that I'm wrong, it would have been courteous to also add a reply to my answer so I would see a notification about it, rather than have to stumble upon it two months later. \$\endgroup\$ Jun 5, 2011 at 5:51
  • \$\begingroup\$ I may have misunderstood, but how does this answer the question? Should I use a GameComponent for my sprites or not? \$\endgroup\$
    – Superbest
    Feb 21, 2012 at 3:27
  • \$\begingroup\$ Further rebuttal: gamedev.stackexchange.com/a/112664/56 \$\endgroup\$
    – Kenji Kina
    Dec 10, 2015 at 9:40
4
\$\begingroup\$

I disagree a bit with Andrew, but I also think there is a time and place for everything. I would not use a Drawable(GameComponent) for something as simple as a Sprite or Enemy. However, I would use it for a SpriteManager or EnemyManager.

Going back to what Andrew said about draw and update order, I think Sprite.DrawOrder would be very confusing for many sprites. Moreover, its relatively easy to set up a DrawOrder and UpdateOrder for a small (or reasonable) amount of Managers that are (Drawable)GameComponents.

I think that Microsoft would have done away with them if they really where that rigid and nobody used them. But they haven't because they work perfectly fine, if the role is right. I myself use them to develop Game.Services that are updated in the background.

\$\endgroup\$
2
\$\begingroup\$

I was thinking about this as well, and it occurred to me: For instance, to steal the example in your link, in an RTS game you could make every unit a game component instance. Okay.

But you could also make a UnitManager component, which keeps a list of units, draws and updates them as necessary. I guess the reason you want to make your units into game components in the first place is to compartmentalize code, so would this solution adequately achieve that goal?

\$\endgroup\$
2
\$\begingroup\$

In comments, I posted a condensed version of my answer old answer:

Using DrawableGameComponent locks you into a single set of method signatures and a specific retained data model. These are typically the wrong choice initially, and the lock-in significantly hinders the evolution of the code the future.

Here I shall attempt to illustrate why this is the case by posting some code from my current project.


The root object maintaining the state of the game world has an Update method that looks like this:

void Update(UpdateContext updateContext, MultiInputState currentInput)

There are a number of entry-points to Update, depending on whether the game is running on the network or not. It is possible, for example, for the game state to be rolled back to a previous point in time and then re-simulated.

There are also some additional update-like methods, such as:

void PlayerJoin(int playerIndex, string playerName, UpdateContext updateContext)

Within the game state there is a list of actors to be updated. But this is more than a simple Update call. There are additional passes before and after to handle physics. There's also some fancy stuff for deferred spawning/destroying of actors, as well as level transitions.

Outside of the game state, there's a UI system that needs to be managed independently. But some screens in the UI need to also be able to run in a network context.


For drawing, because of the nature of the game, there is a custom sorting and culling system that takes inputs from these methods:

virtual void RegisterToDraw(SortedDrawList sortedDrawList, Definitions definitions)
virtual void RegisterShadow(ShadowCasterList shadowCasterList, Definitions definitions)

And then, once it figures out what to draw, automatically calls:

virtual void Draw(DrawContext drawContext, int tag)

The tag parameter is required because some actors can hold onto other actors - and so need to be able to draw both above and below the held actor. The sorting system ensures that this happens in the correct order.

There is also the menu system to draw. And a hierarchical system for drawing the UI, around the HUD, around the game.


Now compare those requirements with what DrawableGameComponent provides:

public class DrawableGameComponent
{
    public virtual void Update(GameTime gameTime);
    public virtual void Draw(GameTime gameTime);
    public int UpdateOrder { get; set; }
    public int DrawOrder { get; set; }
}

There is no reasonable way to get from a system using DrawableGameComponent to the system I described above. (And those are not unusual requirements for a game of even modest complexity).

Also, it is very important to note that those requirements did not simply spring-up at the start of the project. They evolved over many months of development work.


What people typically do, if they start down the DrawableGameComponent route, is they start building on hacks:

  • Instead of adding methods, they do some ugly down-casting to their own base type (why not just use that directly?).

  • Instead of replacing the code for sorting and invoking Draw/Update, they do some very slow things to change the ordering numbers on-the-fly.

  • And worst of all: Instead of adding parameters to methods, they start "passing" "parameters" in memory locations that are not the stack (the use of Services for this is popular). This is convoluted, error-prone, slow, fragile, rarely thread-safe, and likely to become a problem if you add networking, make external tools, and so on.

    It also makes code re-use harder, because now your dependencies are hidden inside the "component", instead of published at the API boundary.

  • Or, they almost see how crazy this all is, and start doing "manager" DrawableGameComponents to work around these problems. Except they still have these problems at the "manager" boundaries.

    Invariably these "managers" could easily be replaced with a series of method calls in the desired order. Anything else is over-complicated.

Of course, once you start employing those hacks, it then takes real work to undo those hacks once you realise you need more than what DrawableGameComponent provides. You become "locked in". And the temptation is often to continue to pile upon hacks - which in practice will bog you down and limit the kinds of games you can make to relatively simplistic ones.


A lot of people seem to reach this point and have a visceral reaction - possibly because they use DrawableGameComponent and don't want to accept being "wrong". So they latch onto the fact that it is at least possible to make it "work".

Of course it can be made to "work"! We're programmers - making things work under unusual restraints is practically our job. But this restraint is not necessary, useful or rational...


What is particularly flabbergasting about is that it is astonishingly trivial to side-step this whole issue. Here, I'll even give you the code:

public class Actor
{
    public virtual void Update(GameTime gameTime);
    public virtual void Draw(GameTime gameTime);
}

List<Actor> actors = new List<Actor>();

foreach(var actor in actors)
    actor.Update(gameTime);

foreach(var actor in actors)
    actor.Draw(gameTime);

(It's simple to add in sorting as per DrawOrder and UpdateOrder. One simple way is to maintain two lists, and use List<T>.Sort(). It is also trivial to handle Load/UnloadContent.)

It is not important what that code actually is. What is important is that I can trivially modify it.

When I realise that I need a different timing system to gameTime, or I need more parameters (or an entire UpdateContext object with about 15 things in it), or I need a completely different order-of-operations for drawing, or I need some extra methods for different update passes, I can just go ahead and do that.

And I can do it immediately. I don't need to faff about picking DrawableGameComponent out of my code. Or worse: hack it in in some way that will make it even harder the next time such a need comes around.


There is really only one scenario where it is a good choice to use DrawableGameComponent: and that is if you are literally making and publishing drag-and-drop-component-style middleware for XNA. Which was its original purpose. Which - I will add - no one is doing!

(Similarly, it only really makes sense to use Services when making custom content types for ContentManager.)

\$\endgroup\$
1
  • \$\begingroup\$ While I agree with your points, I also don't see anything particularly wrong in using stuff which inherits from DrawableGameComponent for certain components, especially for stuff like menu screens (menus, lots of buttons, option and stuff), or the FPS/debug overlays you mentioned. In these cases you usually don't need fine grained control over draw order and you end up with less code you need to manually write (which is a good thing, unless you need to write that code). But I guess that's pretty much the drag-and-drop scenario you were mentioning. \$\endgroup\$
    – vgru
    Dec 21, 2015 at 11:56

You must log in to answer this question.

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