Tutorial 47: GameComponent and DrawableGameComponent
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
setUpdateOrder(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) {
setUpdateOrder(100); // run after game logic
setDrawOrder(1000); // draw on top of everything
}
void LoadContent() override {
font_ = getGame()->getContentProperty().Load<SpriteFont>("fonts/debug");
sb_ = std::make_unique<SpriteBatch>(
getGame()->getGraphicsDeviceProperty());
}
void Update(GameTime& gameTime) override {
elapsed_ += gameTime.ElapsedGameTime.TotalSeconds();
++frames_;
if (elapsed_ >= 1.0) {
fps_ = frames_;
frames_ = 0;
elapsed_ = 0.0;
}
}
void Draw(const GameTime&) override {
if (!getVisible()) return;
sb_->Begin();
sb_->DrawString(*font_,
"FPS: " + std::to_string(fps_),
Vector2(10, 10),
Color::Yellow);
sb_->End();
}
private:
SpriteFont* font_ = nullptr;
std::unique_ptr<SpriteBatch> sb_;
int fps_ = 0;
int frames_ = 0;
double elapsed_ = 0.0;
};
Game::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);
Components().Add(input_.get());
Components().Add(fpsHud_.get());
}
// No manual Update/Draw calls for components —
// the Game loop iterates Components() automatically.
protected:
void Update(GameTime& gt) override {
// Components().Update() is called by Game base before this
if (input_->WasPressed(Keys::F1))
fpsHud_->setVisible(!fpsHud_->getVisible());
if (input_->WasPressed(Keys::Escape))
Exit();
}
void Draw(const GameTime& gt) override {
getGraphicsDeviceProperty().Clear(Color::CornflowerBlue);
// ... game drawing ...
getGraphicsDeviceProperty().Present();
// Components().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_->setEnabled(false);
// Hide a drawable component (Update still runs)
fpsHud_->setVisible(false);
// Re-enable
input_->setEnabled(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.