Skip to content

MonoGame — How I Built a Platformer in Pure C# and Kept My Sanity (Mostly)

One day I decided to make a game. Not in Unity, not in Godot — in raw C#. Why? Because I have a CS degree and I wanted to know what actually sits underneath every “drag your sprite here” button in every engine ever.

The result: MathNerd — a Mario-style platformer where a glasses-wearing nerd in a bow tie must defeat bullies by solving math problems. Yes, it’s an educational game. No, I’m not proud. Yes, it’s funny.

What is MonoGame?

MonoGame is an open-source .NET game framework — the spiritual successor to Microsoft’s XNA. It gives you:

  • SpriteBatch for rendering
  • Content Pipeline for asset management (fonts, textures, sounds)
  • GameWindow, keyboard/gamepad input, and the game loop

What it does not give you: scenes, component systems, visual editors, or a hand to hold. It’s just you and Update(GameTime).

Architecture in 5 minutes

The whole game lives in a handful of .cs files:

scripts/
├── Player.cs          — the nerd (pixel art made of rectangles)
├── Enemy.cs           — the bully (also rectangles, fancier)
├── Level.cs           — platforms, question blocks, enemies
├── Physics.cs         — AABB collision, gravity
├── MathPopup.cs       — math question overlay + countdown timer
├── MathChallengeManager.cs — question generator
├── HUD.cs             — hearts, score, rotating taunts
└── Camera.cs          — horizontal scrolling

No magic. One 1×1 white Texture2D, and SpriteBatch.Draw(pixel, rect, color) for everything. The entire game is colored rectangles. I am not joking.

Pixel art without images

void D(int dx, int dy, int w, int h, Color c) =>
    sb.Draw(pixel, new Rectangle(bx + dx, by + dy, w, h), c);

// The nerd's face
D( 5,  4, 22, 18, skin);   // face
D( 3, 12, 12,  9, frm);    // left glasses frame
D( 4, 13, 10,  7, lens);   // left lens
D( 6, 15,  4,  3, pup);    // pupil

Result: a character with glasses, a bow tie, blue overalls, and a moustache. It’s no Ori and the Blind Forest, but it has character.

Physics without an engine

// X axis — move, then resolve collisions
pos.X += vel.X * dt;
// resolve X overlaps...

// Y axis — move, then resolve collisions
pos.Y += vel.Y * dt;
// resolve Y overlaps... (sets onGround = true when falling)

Separating X and Y is the classic AABB approach. Without it the player would clip into platform corners like a family member stuck in a doorway at Christmas.

Math challenges

When the player stomps a bully, a popup appears:

=== MATH CHALLENGE ===
[ Algebra - face your destiny, mortal ]

ALGEBRA! The final boss of elementary school!
3x + 5 = 23
What is x?

[ _______________________ ]

Brain cells: [###]          ████████████░░░  12s

15 seconds, 3 attempts. Fail and you lose a life. Math has never hurt this much.

Coyote time and jump buffering

Two pillars of good-feeling platformers:

  • Coyote time (0.10s) — you can still jump a moment after walking off a ledge. Because humans have reaction times, not robots.
  • Jump buffer (0.12s) — pressing jump just before landing registers the jump. Eliminates the “I pressed it!” frustration.
if (_jumpBufferTimer > 0 && _coyoteTimer > 0)
{
    Velocity.Y       = JumpVel;
    _jumpBufferTimer = 0;
    _coyoteTimer     = 0;
}

Try it yourself

Repository: github.com/letyshub/MathNerd

git clone https://github.com/letyshub/MathNerd
cd MathNerd
dotnet tool restore   # installs dotnet-mgcb content pipeline tool
dotnet run

Requirements: .NET 8 SDK. No Unity Hub, no launchers, no 40 GB download. Refreshing.

Final thoughts

MonoGame is not an engine — it’s a framework. It won’t do anything for you, but it won’t stop you from doing anything either. It’s ideal if you:

  • Want to understand how games actually work under the hood
  • Like C# and don’t want to learn a new scripting language
  • Are building a small 2D game without needing a level editor
  • Have too much time and not enough sleep

If you want drag-and-drop and built-in shaders — stick with Unity. If you enjoy debugging collision at 2am — MonoGame is waiting.


Tags: