Ludo Atlas · Engine Tracks · MonoGame / FNA¶
Engine Tracks. Positioning: a code-first C# framework line descended from XNA — MonoGame carries it forward into the modern day, FNA reproduces it faithfully; the main loop, architecture and toolchain are all yours to organize. Companions: Programming Handbook · Game Design Handbook · Indie Survival · Engine Internals Path.
1. Positioning and Choice¶
MonoGame and FNA are two open-source continuations of Microsoft's discontinued XNA framework, sharing one worldview: C# code-first, no scene editor — you get a window, a main loop, and a set of drawing, input and audio interfaces; everything else you organize yourself. Plenty of commercial work has been built with this plain toolchain — Stardew Valley and Celeste among them — and it can carry a full project, provided you accept its worldview.
The two differ in "attitude toward the original":
| Dimension | MonoGame | FNA |
|---|---|---|
| Positioning | An XNA-style modern framework; interfaces and behavior allowed to evolve step by step | A faithful reproduction targeting complete compatibility with the original; fidelity first |
| Who it is for | New projects; people who want the modern .NET toolchain | Porting existing XNA projects; people who need frame-for-frame identical behavior |
| Content pipeline | Ships MGCB; assets compiled as part of the build | No content pipeline; the official stance encourages building your own light conversion tools |
| Project shape | One project per target platform | One project covers every platform it supports |
| Ecosystem | Community libraries mostly target it | Plainer; most tooling is yours to wire up |
| Platforms | Desktop, mobile, consoles (consoles via registered-developer channels) | Mainly desktop and consoles; mobile via community forks |
Selection rule: new projects default to MonoGame; if you hold a working old XNA codebase that needs a fidelity port, go FNA; do not mix the two in one project, and do not expect a painless migration between them.
1.1 Who it fits¶
- C# programmers: the language is your working language, and the whole .NET ecosystem (dependency management, testing, CI) applies — the skills you invest are the same ones your career uses.
- Learner-oriented developers: what happens each frame is fully transparent, making it direct material for understanding what an engine does for you; complements the Engine Internals Path.
- Porters: existing XNA projects moving to modern platforms — FNA minimizes the migration.
- Authors who want control: render order, memory, threads and platform integration all stay in your hands; no engine black box to wrestle.
- Small, focused-mechanics 2D games: tooling gaps have a limited blast radius, and code-first buys extremely fast iteration.
1.2 Who it does not fit¶
- Projects with heavy content where designers and artists need to build levels in an editor: no visual scene tooling, and no one else will fill that workflow gap.
- Projects that depend on off-the-shelf systems: navigation, animation state machines, UI kits — mostly write your own or assemble one by one.
- Teams aiming for consoles at launch without a publisher or porting resources: console platforms carry qualification thresholds and confidentiality requirements — budget them through registered-developer channels.
- People who want things to work out of the box: window dragging, resolution strategy, save paths — none of it exists by default.
1.3 Against Unity and Godot (C#)¶
| Dimension | MonoGame / FNA | Unity | Godot C# |
|---|---|---|---|
| Way of working | Code is the project; no editor | Editor-first; code attaches to objects | Editor-first; nodes plus scripts |
| What you learn | C# and graphics fundamentals; no engine jargon to memorize | The whole Unity system | Godot's node and scene model |
| Off-the-shelf systems | Almost none; build or pull in libraries | The most, with uneven quality | Moderate; a batch built in officially |
| Platform coverage | Desktop and mobile smooth; consoles via developer channels | All platforms, consoles mature | Desktop and mobile good; consoles via third parties |
| Licensing and control | Open source; runtime fully controllable | Commercial engine; terms need tracking | Open source, permissive license |
| Content collaboration | Content in data files; tools are yours to write | Artists and designers work directly | Artists and designers work directly |
In one line: want off-the-shelf systems and content collaboration — choose Unity or Godot; want native C#, full control, and a tech stack you can read through — choose MonoGame or FNA.
2. Ecosystem and Project Structure¶
2.1 Toolchain¶
The workbench is the .NET toolchain itself; the editor provides only completion and debugging:
| Tool | What it handles |
|---|---|
| .NET SDK and the dotnet CLI | Templates, dependencies, build, run, publish |
| IDE (Rider, Visual Studio or VS Code) | Coding, debugging, profiling |
| MGCB (content builder) | Compiles raw assets into runtime formats as part of the build |
| Template commands | Generate per-platform project skeletons; desktop, mobile and other targets each have templates |
One discipline: compress the "edit code to see a frame" loop into seconds. Code-first's whole advantage rests on this.
2.2 Directories and project shape¶
A single solution with per-platform launcher projects is the common shape:
game/
├── Game.Core/ # All game code, platform-agnostic
├── Game.Desktop/ # Desktop launcher project, references Core
├── Game.Mobile/ # Mobile launcher project, references Core
├── Content/ # Source assets and content pipeline config
└── tools/ # Self-built editors and conversion scripts
- Put all logic in the platform-agnostic Core project; launcher projects keep only the entry point and a bit of adaptation — the most economical way to handle platform differences.
Content/holds both source assets and pipeline configuration; outputs go to separate per-platform directories and stay out of version control.- Build
tools/from day one: level editors, tuning panels and conversion scripts are first-class citizens of the project.
Drift from this convention and, a month later, a second copy of the game grows inside the platform branches.
2.3 Version control¶
- Ignore generated output directories (compiled output and content artifacts) — they all rebuild automatically.
- Commit content pipeline configs and asset source files so builds are identical on any machine.
- Large files — images, audio — go through Git LFS, with the rules written early.
- Keep levels and data in text formats where possible so diffs stay readable; binary formats need a separate review approach.
2.4 Ecosystem and common libraries¶
The framework gives only the foundation; the upper layers you assemble. Common needs and established options:
| Need | Common approach |
|---|---|
| Tile maps and levels | The Tiled editor with community loaders, or your own format and editor |
| In-game UI | UI libraries such as Gum, or a hand-rolled minimal widget set |
| Font rendering | Community font libraries reading font files directly, or baking through the content pipeline |
| Physics | An off-the-shelf 2D physics library, wired in as needed |
| Debug panel | .NET bindings for Dear ImGui for runtime overlays |
| Extension kits | Community extension libraries (cameras, pixel art, particles) picked as needed |
Library discipline: check recent commits and issue responsiveness first, then license and target framework. The XNA era left a vast pile of old libraries — compiling is not the same as working.
3. Core Workflows¶
3.1 From zero to running¶
- Environment: install the .NET SDK and an IDE, generate a target-platform project from the template, and get the bundled empty-window example running first.
- Skeleton: split the template into Core plus launcher projects; write the first game class — a minimal loop of window, clear, exit.
- Set the rules: wire up version control, write ignore rules and editor configs, and create
tools/right now. - First pixels: draw one sprite, add input and a camera, and complete the minimal loop of "a character stands on a level and moves."
- Content pipeline: get one minimal pipeline through first (one font or one audio file); for assets that need no conversion, reading raw files directly is less trouble.
- Ship something: do a desktop release first, script "one command produces the build," and reuse that script for every platform afterwards.
3.2 Owning the main loop¶
The framework hands you the game class; the loop has only two entry points: update and draw. Four disciplines:
- Logic on a fixed step (the default), decoupled from rendering; before changing step strategy, think through the effects on physics and feel.
- Cap single-frame catch-up, or a stutter snowballs into worse stutters.
- Make frame-time distribution visible: build a frame-timer display and a state overlay from day one.
- Define loop behavior clearly for pause, scene switches and focus loss — the three states where bugs take up residence.
3.3 No editor, so build your own tools¶
This is the route's biggest hidden workload; list it separately when estimating:
| Gap | If unfilled | Common approach |
|---|---|---|
| Level editing | Levels live in code; moving one wall means recompiling | A runtime debug mode placing objects and exporting data, or a small standalone tool reading and writing the same format |
| Asset conversion | Manual handling every time assets change; mistakes eventually | Scripted batch conversion wired into the build |
| Tuning panel | Every value needs a code edit and restart | A runtime overlay panel reading and writing config files |
| Data tables | Values scattered through the codebase | Tables or text plus conversion scripts, generated at build time |
Rule of thumb: tool cost counts in hours; hand-maintenance cost counts in hours times iterations. Before levels pass ten or values pass a few dozen, the tools must be in place.
3.4 Packaging and distribution¶
| Target | Key points |
|---|---|
| Desktop | Self-contained publish plus installers and store integration; hold the line that players need no preinstalled runtime, and mind runtime-library dependencies for platform-native backends |
| Mobile | Android needs toolchain and signing setup; iOS final signing depends on a Mac environment; package size, memory and input forms all need separate adaptation |
| Consoles | Registered-developer channels; SDKs and docs under NDA; without a channel, plan a third-party porting route early |
| Browser | Not officially supported; community forks such as KNI offer experimental ports — verify maturity yourself |
| Common | Updaters, crash collection and log reporting are all yours to wire up; schedule backwards from the release date and budget at engine-project scale |
Desktop graphics backends each carry trade-offs (different window systems, audio libraries and driver dependencies); before release, run the chosen backend through the full path on target machines.
4. Idioms for Key Systems¶
4.1 Architecture: state machine plus services¶
- No built-in scene system; the mainstream approach is a self-built state stack: main menu, match, pause each a layer, each responsible for its own resource loading and cleanup.
- Pass global services (saves, audio, settings, input mapping) down through one explicit service object — no scattered static classes.
- One class per system, one responsibility, a clear interface; directories split by system.
- State must be serializable and restorable at any moment — the shared foundation of saves and debugging.
4.2 Input¶
- Input is a polling model: read keyboard, mouse, gamepad and touch state actively each frame; maintain pressed, held, released and input buffering yourself.
- Keys and actions must be separated by a mapping layer, so runtime rebinding and device switching do not touch everything at once.
- Handle gamepad dead zones, touch virtual buttons and keyboard focus loss from day one — retrofitting is expensive.
4.3 Rendering¶
- Start 2D with SpriteBatch: keep one texture and one set of state within a batch — atlases are the foundation of batch efficiency.
- Cameras via transform matrices: follow, dead zone, look-ahead and screen shake are all matrix math.
- Decide resolution and pixel-alignment strategy (integer scaling, letterboxing, high-DPI scrolling) up front — do not change it after the art is done.
- Custom shaders compile to different outputs per graphics backend; verify each one before cross-backend release.
4.4 Audio and assets¶
- Short sound effects resident in memory, long music streamed; plan capacity and formats together with platforms.
- Asset lifecycle is the managed-language trap: hand-created resources such as textures must be released by you; pool everything created at high frequency.
- Keep a single asset loading entry (a unified resource manager) to leave room for hot reload, fallback assets and reference counting.
- The FNA route has no content pipeline — reading raw files directly is more natural, or build conversion scripts as needed.
4.5 Debug facilities¶
- The trio — frame timing, state overlay, parameter panel — before the first playable version.
- A runtime console suits tuning gameplay parameters better than breakpoints.
- Build tool UI and game UI separately; do not let debug widgets creep into the player's screen.
5. Performance and Optimization¶
General rule: measure before optimizing, and trust target devices (methodology in the Programming Handbook §3).
5.1 CPU and managed memory¶
- Managed bottlenecks usually sit in two places: per-frame allocations and virtual calls; in hot paths avoid string concatenation, boxing, LINQ and temporary collections.
- Pool and pre-warm high-frequency objects to remove garbage spikes during play.
- Find hot spots with a profiler and keep records before and after; smooth on desktop is not smooth on mobile.
5.2 Drawing¶
- Draw calls and texture switches are the primary metrics: look at atlases, sorting and batching together.
- Every batch break each frame should be explainable; an unexplainable break is performance thrown away.
- Overdraw is especially expensive with large textures and particles; on mobile, squeeze this first.
5.3 Content and memory¶
- Choose texture compression per target platform; unify audio into stream-friendly formats before launch.
- Load large assets on demand with prefetch; avoid pressing everything in at once on scene switches.
- On mobile, consider trimming and ahead-of-time compilation settings, and run the build through the full path on real devices.
6. Learning Path¶
Follow it in order; every step needs a runnable artifact:
- C# and .NET fundamentals: solidify the language, generics, collections, async and the debugger first — the framework cannot save someone who does not know the language.
- Official docs and template projects: walk the getting-started tutorials and get four things running — empty window, sprite, sound, gamepad.
- Reuse XNA-era knowledge: that body of tutorials and books still applies at the interface level; where it diverges from the current implementation, trust what you measure.
- Three small games: Pong, Breakout, a scrolling platformer — adding a state machine, a camera and level data in turn.
- Toolchain focus: the content pipeline, the tile-map workflow, a self-built tuning panel — write each once and distill them into your own project template.
- Ship one small game: walk the full path from build scripts to store page; to go deeper, read the implementation against FNA (path in the Engine Internals Path).
Acceptance criteria (observable): without tutorials, build from an empty project to "input, state switching, levels"; explain the execution order of each frame; the released artifact runs directly on a clean machine.
7. Common Pitfalls¶
- Content pipeline black hole: assets like fonts and shaders stuck in conversion with unreadable build errors. Get one minimal pipeline through first; for the rest, prefer reading raw files directly.
- Scattered documentation: official docs cover the basics; deeper questions live in source, issues and community chats. Reading source is a basic skill on this route.
- Fragmented ecosystem: libraries either long unmaintained or compatible with only one of the two. Check maintenance status and target framework before choosing; do not bet core systems on abandoned libraries.
- Scope creep: without engine guardrails, you want to build every system yourself and half a year later you are still building an engine. Set hard caps on self-built systems in the milestones.
- Misconfigured main loop: variable step used as fixed step, physics and feel drifting with framerate; or uncapped catch-up causing stutter avalanches.
- Per-frame allocations: strings, boxing, closures quietly making garbage in update and draw; framerate eaten by the GC.
- Load without release: high-frequency objects and hand-created textures are the usual leak suspects; manage them through the single entry point from day one.
- Platform differences postponed: resolution, scaling, path case, save directories — handled at the end of testing with a large rework bill.
- Wrong lineage: new projects copying FNA's old ways, or forcing MonoGame into a port that demands frame-identical behavior. Choose per the rules in §1.
- Treating the template as the product: a template is a starting point; platform branches, build scripts and the content flow all get rebuilt for the project.
Further Reading¶
- Engine Tracks overview: the list and plan for all 12 tracks — this page is the "MonoGame / FNA" one.
- Programming Handbook: the general version of core systems, performance methodology and engineering infrastructure; echoes §3 and §4 throughout.
- Engine Internals Path: reading MonoGame's source through as a first full source-reading exercise.
- Indie Survival: scope control, scheduling and cash flow; complements the audience positioning of §1.
- Pitfalls & Anti-patterns: read against section 7 here.
- Resources: official entry points, community libraries and tutorial indexes for both lineages.