Tutorial 47: GameComponent and DrawableGameComponent
What you’ll learn
GameComponentfor update-only logic andDrawableGameComponentwhen it also draws.- Registering components in
Game::getComponentsProperty(). - Controlling participation with
Enabled/Visibleand ordering withUpdateOrder/DrawOrder.
Before you start — Tutorial 04: The Game Class Lifecycle — components are just a way to split those same callbacks across classes.
The component system lets you split cross-cutting concerns (input, audio, debug overlay, fps counter) into self-contained objects that Game updates and draws for you each frame — without per-component boilerplate in your main Update and Draw methods. There is one rule to remember: the components are driven from inside the base-class Game::Update and Game::Draw, so your overrides must call them (see below).
GameComponent base — Update only
#include "Microsoft/Xna/Framework/GameComponent.hpp"
using namespace Microsoft::Xna::Framework;
class InputManager final : public GameComponent {
public:
explicit InputManager(Game& game) : GameComponent(game) {
// Lower UpdateOrder runs first
setUpdateOrderProperty(0);
}
void Update(GameTime& gameTime) override {
prevKeys_ = currKeys_;
currKeys_ = Keyboard::GetState();
prevPad_ = currPad_;
currPad_ = GamePad::GetState(PlayerIndex::One);
}
bool WasPressed(Keys key) const {
return currKeys_.IsKeyDown(key) && prevKeys_.IsKeyUp(key);
}
bool WasButtonPressed(Buttons btn) const {
return currPad_.IsButtonDown(btn) && prevPad_.IsButtonUp(btn);
}
private:
KeyboardState currKeys_, prevKeys_;
GamePadState currPad_, prevPad_;
};
DrawableGameComponent — Update + Draw
#include "Microsoft/Xna/Framework/DrawableGameComponent.hpp"
class FPSCounter final : public DrawableGameComponent {
public:
explicit FPSCounter(Game& game) : DrawableGameComponent(game) {
setUpdateOrderProperty(100); // run after game logic
setDrawOrderProperty(1000); // draw on top of everything
}
void LoadContent() override {
font_ = getGameProperty().getContentProperty().Load<SpriteFont>("fonts/debug");
sb_ = std::make_unique<SpriteBatch>(
getGameProperty().getGraphicsDeviceProperty());
}
void Update(GameTime& gameTime) override {
elapsed_ += gameTime.getElapsedGameTimeProperty().getTotalSecondsProperty();
if (elapsed_ >= 1.0) {
fps_ = frames_;
frames_ = 0;
elapsed_ = 0.0;
}
}
void Draw(const GameTime&) override {
++frames_; // count drawn frames: counting Update calls would report the update rate
if (!getVisibleProperty()) return;
sb_->Begin();
sb_->DrawString(*font_,
"FPS: " + std::to_string(fps_),
Vector2(10, 10),
Color::Yellow);
sb_->End();
}
private:
std::optional<SpriteFont> font_; // SpriteFont has no default ctor
std::unique_ptr<SpriteBatch> sb_;
int fps_ = 0;
int frames_ = 0;
double elapsed_ = 0.0;
};
The Components collection
class MyGame final : public Game {
public:
MyGame() : graphics_(this) {
// Add components in the constructor — before Initialize() is called
input_ = std::make_unique<InputManager>(*this);
fpsHud_ = std::make_unique<FPSCounter>(*this);
// Raw pointers are borrowed: this game keeps the components alive (the unique_ptrs below)
getComponentsProperty().Add(input_.get());
getComponentsProperty().Add(fpsHud_.get());
}
// You do not call each component yourself, but the base class does, so your
// overrides MUST call Game::Update / Game::Draw. An override that forgets them
// silently updates and draws no component at all.
protected:
void Update(GameTime& gt) override {
Game::Update(gt); // updates every enabled component (input_ first: UpdateOrder 0),
// then runs FrameworkDispatcher::Update()
if (input_->WasPressed(Keys::F1))
fpsHud_->setVisibleProperty(!fpsHud_->getVisibleProperty());
if (input_->WasPressed(Keys::Escape))
Exit();
}
void Draw(const GameTime& gt) override {
getGraphicsDeviceProperty().Clear(Color::CornflowerBlue);
// ... game drawing ...
Game::Draw(gt); // draws every visible component (the FPS overlay ends up on top)
// No Present() here: Game presents the frame after Draw() returns.
}
private:
GraphicsDeviceManager graphics_;
std::unique_ptr<InputManager> input_;
std::unique_ptr<FPSCounter> fpsHud_;
};
If a component is heap-allocated and nothing else owns it, CNA also offers a CNAEXT overload that shares ownership, so the collection keeps it alive (it is also the safe choice when a loading thread adds components):
auto hud = std::make_shared<FPSCounter>(*this);
getComponentsProperty().Add(hud); // std::shared_ptr<IGameComponent> overload; Insert(index, ptr) exists too
Enabled and Visible
// Pause a component's Update
input_->setEnabledProperty(false);
// Hide a drawable component (Update still runs)
fpsHud_->setVisibleProperty(false);
// Re-enable
input_->setEnabledProperty(true);
UpdateOrder and DrawOrder
Components are kept sorted by their order values. Lower values run first. Both UpdateOrder and DrawOrder default to 0; the other numbers in the table are only this tutorial’s recommendations, and components with equal orders run in the order they were added.
| Component | Recommended UpdateOrder | DrawOrder |
|---|---|---|
| InputManager | 0 (first) | n/a |
| AudioManager | 10 | n/a |
| Game logic | 50 (recommended) | 50 |
| UI / HUD | 80 | 800 |
| Debug overlay | 100 | 1000 (last) |
Use cases
- InputManager — centralise keyboard, mouse, and gamepad polling into one place; expose WasPressed helpers.
- AudioManager — manage music transitions, sound pools, and volume settings independently of game logic.
- FPSCounter / DebugOverlay — toggle with F1; zero cost when
Visible = false. - ParticleSystem — self-contained drawable that manages its own vertex buffers.
- NetworkManager — update-only component that ticks the network layer each frame.
Deep dives on this topic
Long-form pages that explain the exact semantics, invariants and evidence behind this subject.
- Game components and the service container — Exact contracts of GameComponent, DrawableGameComponent, GameComponentCollection and GameServiceContainer in CNA: ordering, content loading, disposal without unregistration, events, iterators and type-keyed services.