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 the Game loop calls automatically each frame — without boilerplate in your main Update and Draw methods.
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();
++frames_;
if (elapsed_ >= 1.0) {
fps_ = frames_;
frames_ = 0;
elapsed_ = 0.0;
}
}
void Draw(const GameTime&) override {
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);
getComponentsProperty().Add(input_.get());
getComponentsProperty().Add(fpsHud_.get());
}
// No manual Update/Draw calls for components —
// the Game loop iterates getComponentsProperty() automatically.
protected:
void Update(GameTime& gt) override {
// getComponentsProperty().Update() is called by Game base before this
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 ...
getGraphicsDeviceProperty().Present();
// getComponentsProperty().Draw() is called by Game base after this
}
private:
GraphicsDeviceManager graphics_;
std::unique_ptr<InputManager> input_;
std::unique_ptr<FPSCounter> fpsHud_;
};
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 sorted by their order values before each call. Lower values run first.
| Component | Recommended UpdateOrder | DrawOrder |
|---|---|---|
| InputManager | 0 (first) | n/a |
| AudioManager | 10 | n/a |
| Game logic | 50 (default) | 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.