Skip to content
E
Egmatic
monogame-tutorialmonogamecsharpgame-development2d-games

MonoGame Tutorial: Build Your First 2D Game Step by Step

MonoGame is a free, open-source .NET framework for 2D and 3D games written in C# — the modern successor to Microsoft's XNA. This tutorial walks through building your first MonoGame project from scratch: install the .NET SDK, create a project with dotnet new, understand the Game1 game loop, load a texture, draw a sprite with SpriteBatch, and move it with the keyboard. By the end you have a window with a player sprite you can steer, plus a clear path to collisions, animation, and audio.

Vladislav KovnerovJuly 7, 20267 min

MonoGame is a free, open-source .NET framework for making 2D and 3D games in C# — the community-maintained successor to Microsoft's XNA. In this tutorial you will build a real first project from an empty folder to a window with a player sprite you steer with the arrow keys, and along the way learn the parts of MonoGame that every game reuses: the project template, the game loop, texture loading, SpriteBatch drawing, and keyboard input.

You do not need an engine, and you do not need Visual Studio. All you need is the .NET SDK and a text editor. The current stable MonoGame release is 3.8.4.1 (June 2025); version 3.8.5 is available as a preview but is not final yet.

What you will build

A minimal but complete MonoGame game: a 800×600 window containing a sprite that moves when you press the arrow keys. That covers roughly 80% of what a 2D MonoGame game does — load an image, draw it every frame, and read input to change something. Collisions, animation, and sound are then extensions on this same loop.

Step 0 — Install the .NET SDK

MonoGame runs on .NET, so install the .NET 8 SDK (or newer) from dotnet.microsoft.com/download. Verify it works:

dotnet --version

That single command is all the setup MonoGame needs at the system level. The framework itself comes in through NuGet when you create a project — no separate installer required.

Step 1 — Create the project

MonoGame ships project templates as a NuGet package. Install them once:

dotnet new install MonoGame.Templates.CSharp

Then create a DesktopGL project — the cross-platform template that runs on Windows, macOS, and Linux:

dotnet new mgdesktopgl -o MyFirstGame
cd MyFirstGame

mgdesktopgl is the short name for the DesktopGL template. Other templates exist for specific targets: mgwindows (Windows DirectX), mgandroid, and mgios. For a first game, DesktopGL is the right choice because it runs everywhere you develop.

Step 2 — Understand Game1.cs

Open Game1.cs. The template generates a class built around four methods, and this is the game loop that every MonoGame game is built on:

public class Game1 : Game
{
    private GraphicsDeviceManager _graphics;
    private SpriteBatch _spriteBatch;

    public Game1()
    {
        _graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
        IsMouseVisible = true;
    }

    protected override void Initialize() { base.Initialize(); }
    protected override void LoadContent() { _spriteBatch = new SpriteBatch(GraphicsDevice); }
    protected override void Update(GameTime gameTime) { base.Update(gameTime); }
    protected override void Draw(GameTime gameTime) { }
}

The four methods run on a strict cycle, inherited from XNA:

MethodWhen it runsWhat you do here
InitializeOnce, at startupSet up non-graphics state, query services
LoadContentOnce, at startupLoad textures, fonts, sounds
UpdateEvery frameRead input, move objects, run game logic
DrawEvery frameRender everything to the screen

The cycle is UpdateDrawUpdateDraw, dozens of times per second. Keep logic in Update and drawing in Draw, and you have the structure of a game.

Step 3 — Load a texture

Add a PNG image to your project folder (for example, player.png, any small square image will do). MonoGame gives you two ways to load it. The fastest for a first game is to skip the content pipeline and load the file directly:

private Texture2D _playerTexture;
private Vector2 _playerPosition;

protected override void LoadContent()
{
    _spriteBatch = new SpriteBatch(GraphicsDevice);
    _playerTexture = Texture2D.FromFile(GraphicsDevice, "player.png");
    _playerPosition = new Vector2(400, 300);
}

Texture2D.FromFile reads the PNG at runtime with no build step. The alternative — the MonoGame Content Builder (MGCB) — compiles assets into optimized .xnb files you load with Content.Load<Texture2D>("player"). The pipeline is faster at runtime and is required for shaders and effects (.fx), but for a first project loading a PNG directly keeps the focus on the game loop. You can move to the pipeline later.

Step 4 — Draw the sprite

SpriteBatch is MonoGame's main 2D drawing tool. Every frame, open it, draw, and close it:

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);

    _spriteBatch.Begin();
    _spriteBatch.Draw(_playerTexture, _playerPosition, Color.White);
    _spriteBatch.End();

    base.Draw(gameTime);
}

Clear wipes the screen each frame (the classic XNA cornflower-blue background). Between Begin and End, SpriteBatch batches draw calls and sends them to the GPU efficiently — so put all your Draw calls for a frame inside one Begin/End pair.

Step 5 — Move with the keyboard

Now make the sprite respond to input. In Update, read the keyboard state and change the position:

protected override void Update(GameTime gameTime)
{
    var keyboard = Keyboard.GetState();
    float speed = 200f * (float)gameTime.ElapsedGameTime.TotalSeconds;

    if (keyboard.IsKeyDown(Keys.Left))  _playerPosition.X -= speed;
    if (keyboard.IsKeyDown(Keys.Right)) _playerPosition.X += speed;
    if (keyboard.IsKeyDown(Keys.Up))    _playerPosition.Y -= speed;
    if (keyboard.IsKeyDown(Keys.Down))  _playerPosition.Y += speed;

    base.Update(gameTime);
}

Two things matter here. First, Keyboard.GetState() returns the current state of every key, and IsKeyDown checks one. Second, movement is multiplied by ElapsedGameTime so the speed is per second, not per frame — your player moves at the same rate on a 60 Hz laptop and a 144 Hz monitor. Get this habit right from the first tutorial and you avoid the "my game runs too fast on a new PC" class of bugs forever.

Step 6 — Run it

dotnet run

A 800×600 window opens, the cornflower-blue background clears each frame, and your sprite moves with the arrow keys. You now have a working MonoGame game. The full Game1.cs is just the four methods above, fitted together.

Where to go next

From here, every 2D feature is an addition to the same loop:

  • Collisions — check the bounding rectangles of two sprites with Rectangle.Intersects inside Update.
  • Animation — store an array of source rectangles on a sprite sheet and advance the index on a timer.
  • Audio — load a SoundEffect through the content pipeline and call .Play().
  • Text — load a SpriteFont and draw strings with _spriteBatch.DrawString.
  • Shaders — write a .fx effect, compile it through MGCB, and apply it in SpriteBatch.

When you reach the point of needing a scene editor, tile maps, and visual logic rather than hand-written code, that is where a tool built on MonoGame becomes useful. See the 8 MonoGame tools pro developers use, the stories and lessons of MonoGame indie developers, and how to ship a MonoGame game solo.

Common mistakes

  • Mixing logic into Draw. Keep input and movement in Update. Draw should only render the current state.
  • Forgetting to multiply movement by elapsed time. Movement becomes frame-rate-dependent and runs at different speeds on different machines.
  • Opening multiple SpriteBatch pairs per frame. One Begin/End per batch is cheaper than many.
  • Calling Content.Load every frame. Load once in LoadContent and store the reference; loading each frame thrashes memory.
  • Using raw pixel coordinates as physics units. If you add a physics engine later, it expects meters, not pixels. See the 2D physics engine guide for why scale matters.

Where Egmatic fits

MonoGame gives you C#, .NET, full source access, no royalties, and the same reach as the stack behind Stardew Valley and Celeste — but you assemble every layer yourself. Egmatic is a 2D game editor and engine built on the MonoGame runtime, so the scene editor, content pipeline, and node-based logic are integrated rather than wired together by hand. You keep MonoGame's cross-platform reach and drop the integration work. If you want the power of the MonoGame stack without spending weeks stitching tools together, that is what Egmatic is for.

Conclusion

A first MonoGame game is four methods: Initialize, LoadContent, Update, and Draw. Install the .NET SDK, create a project with dotnet new mgdesktopgl, load a texture, draw it with SpriteBatch, and move it with Keyboard.GetState. That is the whole loop, and collisions, animation, and audio are all additions to it. Run it with dotnet run, and you have a window with a sprite you control — the honest starting point for every MonoGame game that comes after.

Related Posts