Migrating from MonoGame / XNA
Implementation status: CNA implements 227 of the 245 public XNA 4.0 types. The structural mapping is mechanical — the same namespaces, the same class names. The work in a port is almost never the API; it is the content pipeline (no .xnb) and custom shaders (no .fx).
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.getIsActive() |
| Property write | graphics.PreferredBackBufferWidth = 800 |
graphics.setPreferredBackBufferWidth(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 use a pre-compilation pipeline that converts source assets into proprietary .xnb binary files, which are then loaded at runtime via ContentManager.Load<T>. CNA does not support the XNB format.
| XNA / MonoGame | CNA |
|---|---|
| Pre-compile assets with MGCB / XNA Content Pipeline Tool | No build step required for most asset types |
.xnb binary files at runtime |
Raw asset files (PNG, WAV, OGG) or JSON descriptor files |
Content.Load<Texture2D>("player") loads player.xnb |
Content->Load<Texture2D>("player") loads player.png directly |
Fonts: .spritefont + MGCB |
Fonts: .font.json descriptor referencing a TTF file |
Models: .fbx / .obj compiled to .xnb |
Models: .model.json descriptor referencing OBJ/FBX |
Custom shaders: .fx HLSL compiled by pipeline |
Shaders: .shader.json referencing GLSL (EasyGL) or SPIR-V (Vulkan) |
For a typical 2D game the migration is straightforward: place PNG and OGG files in your content directory and update Content->setRootDirectory() to point at it. The Load<T> call is identical.
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 now genuinely implemented (~97% functional) — a real .xgs/.xsb/.xwb parser plays back through SDL3_mixer with category/instance-limit/fade handling and continuous RPC curves. Games that rely on XACT for audio can keep doing so; SoundEffect and MediaPlayer remain simpler alternatives for projects without existing XACT content.
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.
- No XNB content pipeline —
.xnbfiles are not supported. Replace with raw assets (PNG, WAV, OGG) and JSON descriptors. - GamerServices — a complete API port exists (
Gamer,SignedInGamer,GamerProfile, leaderboards,Guide, achievements), with local/synthetic semantics rather than real Xbox Live binary compatibility — the same approach FNA itself uses for this namespace. - Touch input — the
TouchPanelAPI and gesture pipeline (Tap, FreeDrag, Flick, Pinch...PinchComplete) are fully wired and byte-faithful to FNA's behavior (~98%). - macOS — not an official target. SDL3 supports macOS and CNA may compile there, but it is untested.
- FillMode::WireFrame — available on the Vulkan backend only; not supported on EasyGL.
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) |
|---|---|
|
|