Migrating from MonoGame / XNA
Alpha.1 status: the XNA-shaped namespaces and core types make structural translation largely mechanical. CNA reads built-in XNB types and lets games register explicit C++ type readers. Already-compiled XNA/FNA Effect Framework bytecode can now load on FNA3D and opt-in EasyGL, SDL_GPU or Vulkan builds; HLSL source and MonoGame MGFX still need a separate porting strategy.
Overview
CNA is a C++23 reimplementation of Microsoft XNA 4.0, built on SDL3. Its API is intentionally faithful to XNA: the same namespace hierarchy, the same class names, the same method names. For a developer coming from MonoGame or XNA the conceptual model is identical — Game, SpriteBatch, ContentManager, GraphicsDevice, BasicEffect — all exist and behave as expected.
The differences are almost entirely syntactic: C++ does not have garbage collection, properties, delegates, or using-disposal blocks. Each of these maps cleanly to a C++ equivalent. Once you have internalised the mechanical substitutions in this guide, most XNA code ports line-by-line.
1. Namespace mapping
XNA uses dot-separated C# namespaces; CNA uses the identical names with :: separators. The using directive works the same way.
| C# (XNA / MonoGame) | C++ (CNA) |
|---|---|
Microsoft.Xna.Framework |
Microsoft::Xna::Framework |
Microsoft.Xna.Framework.Graphics |
Microsoft::Xna::Framework::Graphics |
Microsoft.Xna.Framework.Input |
Microsoft::Xna::Framework::Input |
Microsoft.Xna.Framework.Audio |
Microsoft::Xna::Framework::Audio |
Microsoft.Xna.Framework.Content |
Microsoft::Xna::Framework::Content |
using Microsoft.Xna.Framework; |
using namespace Microsoft::Xna::Framework; |
2. C++ vs C# quick reference
The table below covers the most common syntactic substitutions you will encounter during a port.
| Concept | C# (XNA) | C++ (CNA) |
|---|---|---|
| Property read | game.IsActive |
game.getIsActiveProperty() |
| Property write | graphics.PreferredBackBufferWidth = 800 |
graphics.setPreferredBackBufferWidthProperty(800) |
| Inheritance | class MyGame : Game |
class MyGame final : public Game |
| Object creation | new SpriteBatch(gd) |
std::make_unique<SpriteBatch>(gd) |
| Null reference | null |
nullptr |
| String type | string |
std::string or String (sharp-runtime alias) |
| Boolean | bool (true/false) |
bool (true/false) — same |
| Delegates / events | EventHandler handler = ... |
Virtual method override or EventHandler<T> |
using resource disposal |
using (var x = ...) {} |
RAII / std::unique_ptr<> |
| Abstract override | override void Draw(...) |
void Draw(...) override |
| Integer types | int, byte, short |
intcs, bytecs, shortcs (sharp-runtime aliases) |
| Float type | float |
Single or float |
| Array | T[] |
std::vector<T> or std::array<T, N> |
| Optional / nullable | T? |
std::optional<T> |
foreach |
foreach (var x in col) |
Range-for: for (auto& x : col) |
3. Game class migration
The Game subclass is the entry point for any XNA game. The lifecycle methods — Initialize, LoadContent, Update, and Draw — exist identically in CNA. The only differences are C++ syntax: override comes after the signature, member variables are declared with their types (no implicit nullability), and resources are owned by std::unique_ptr rather than being garbage-collected.
| C# (MonoGame) | C++ (CNA) |
|---|---|
|
|
4. Memory management
XNA relies on the .NET garbage collector: objects are allocated with new and reclaimed automatically. C++ has no garbage collector; you manage object lifetimes explicitly. CNA follows standard modern C++ ownership conventions:
std::unique_ptr<T>— single-owner resource. Use this for game components, renderers, and anything yourGameclass owns exclusively. The resource is released automatically when the owning pointer goes out of scope or is reset.std::shared_ptr<T>— shared ownership. Use this for assets (textures, sounds, models) that multiple objects may reference simultaneously.- Stack allocation — use for temporaries such as
Vector2,Color,Rectangle, andGameTime. These are small value types and incur no heap allocation. ContentManager::Unload()— releases all assets currently held by the content manager. Call this when transitioning between scenes to free GPU and CPU memory.- Game components — add drawable/updatable components to the
Componentscollection (identical to XNA), or hold them asstd::unique_ptrmembers on yourGamesubclass.
The key rule: if you would write new Foo(...) in C#, write std::make_unique<Foo>(...) in C++ and store the result in a std::unique_ptr<Foo> member. The destructor will run automatically when your game object is destroyed.
5. Content pipeline
XNA and MonoGame pre-compile source assets into .xnb binary files. CNA reads XNB directly. Its 50 built-in readers with FFmpeg (49 without video) cover primitives, math, textures, fonts, audio, video, stock and compiled effects, and models, with LZX and shared-resource resolution. Built-ins must be registered explicitly; ContentManager can then migrate asset by asset between XNB and supported loose formats.
You must call CNA::Internal::Xnb::RegisterAllBuiltInXnbReaders() once at startup. The registry is deliberately empty by default, so an .xnb load will fail until you do. See XNB Content Pipeline for the full reader list and the known gaps.
| XNA / MonoGame | CNA |
|---|---|
| Pre-compile assets with MGCB / XNA Content Pipeline Tool | Existing .xnb output can be copied across as-is; loose source files also work with no build step |
.xnb binary files at runtime |
.xnb binary files at runtime, or raw asset files (PNG, WAV, OGG) / JSON descriptors |
Content.Load<Texture2D>("player") loads player.xnb |
Content->Load<Texture2D>("player") prefers player.xnb, else falls back to player.png |
Fonts: .spritefont + MGCB |
Fonts: a compiled SpriteFont .xnb, or a .cnj descriptor naming a pre-rendered glyph-atlas texture |
Models: .fbx / .obj compiled to .xnb |
Models: the XNB model readers, a .gltf/.glb file read directly, or a .cnj from the gltf_to_cnj converter (CNA's own model path) |
Custom shaders: .fx HLSL compiled by pipeline |
XNA/FNA D3D9 Effect Framework binaries load on FNA3D or opted-in EasyGL-family, SDL_GPU and Vulkan builds. Otherwise migrate to renderer-native ShaderEffect. HLSL .fx source and MGFX are not accepted as the binary format. |
| Custom / user-defined content types | No reflection-based discovery. Implement ContentTypeReader<T> and register its creator explicitly, or move the data to a loose format. |
For a typical 2D game the migration is straightforward: copy your existing content directory across, register the built-in readers at startup, and the Load<T> calls are identical. Where you would rather drop the pre-compilation step entirely, place PNG and OGG files in the content directory instead and point Content->setRootDirectoryProperty() at it.
6. Audio
CNA implements the main XNA audio API in the Microsoft::Xna::Framework::Audio namespace. The common classes work as expected:
SoundEffect— loads a WAV or OGG file and plays it as a fire-and-forget sound. API identical to XNA.SoundEffectInstance— a controllable instance of aSoundEffect: pause, resume, loop, and volume control all work.MediaPlayer/Song— background music playback. API identical to XNA.
The XACT subsystem (AudioEngine, SoundBank, WaveBank, Cue) is a real parser and player rather than a facade, with FACT-shaped volume/RPC behavior and 3D calculations. Important caveats: only the first PlayWave per track is honoured; XMA/WMA wave-bank entries log and return a null sound rather than throwing; reverb is a no-op; and the XNB SoundEffectReader has different failure behavior. SoundEffect and MediaPlayer remain simpler alternatives.
7. Known gaps and limitations
Before porting, review these known gaps. The API surface is largely there, but the items below are absent, partial, or need hardware validation. See Verification & Known Issues for the complete current list.
- Compiled effects have a renderer boundary. XNA/FNA D3D9 Effect Framework binaries and XNB Effect payloads load on FNA3D and on EasyGL, SDL_GPU or Vulkan when their compiled-effect build option is enabled. HLSL
.fxsource and MonoGame MGFX are not accepted. QueryGraphicsCapability::CompiledEffectsbefore depending on the path. - Surface formats are renderer-dependent — Skia exposes the broad public promoted set and IGL promotes verified
Rg32/Single. Other families defer to the framework'sColor-only public gate even where the native layer has additional internal mappings. Verify the active renderer. - XNB is supported, with explicit registration — built-in video, typed external-reference and renderer-qualified effect readers exist. Custom types can register a reader/creator, but there is no reflection-based catch-all. See XNB Content Pipeline.
- GamerServices has no online service, but it is not a pure shim — all 52 types are present with complete signatures, and unlike FNA's no-op shim CNA backs them with a real local implementation. Achievements and leaderboard entries are genuinely written as JSON under
SDL_GetPrefPath()and reload in a later process, andLeaderboardWriter/LeaderboardReaderdo real sorting, ranking and paging.Guide::BeginShowMessageBoxandBeginShowKeyboardInputare complete implementations with overlay rendering. What genuinely does nothing: fifteen otherGuide::Show*overlay entry points, and the XNA-faithfulAvatarRenderer::Draw(). There is no Steam, Xbox Live or HTTP integration of any kind. - Media is platform-dependent — the catalogue and playback layers are real, while FFmpeg-backed video is present only where those translation units and dependencies are enabled. It is absent from Windows, Emscripten and Android builds.
- Touch input — the
TouchPanelAPI and gesture pipeline are fully wired, with all 10 XNA gesture types genuinely detected (~95%). One bug:TouchCollectionreportsIsReadOnly == truebut its mutators mutate rather than throwing. - Apple targets are scoped — macOS/Metal has automatic CI. iOS is experimental and limited to SDL_RENDERER final-link plus a one-frame simulator smoke path; tvOS is unsupported.
- Renderer-dependent state —
FillMode::WireFrameis not honoured on every renderer, and DIRECTX12 implements no scissor, viewport, stencil or blend factor. Do not trustSupportsCapability()to tell you which; verify visually. See Graphics State.
8. Code examples
Full Game class
The following pair shows a minimal but complete game loop: window setup, content loading, update, and draw.
| C# (MonoGame) | C++ (CNA) |
|---|---|
|
|
SpriteBatch draw
| C# (MonoGame) | C++ (CNA) |
|---|---|
|
|
ContentManager load
| C# (MonoGame) | C++ (CNA) |
|---|---|
|
|