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" DrawableGameComponent
s 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
.)
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\$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\$