Core Loop & States

Anatomy of a MonoGame Project

A new MonoGame DesktopGL project looks small, but every generated file has a job. This tutorial walks through the entry point, the Game1 class, the project file, content building, generated folders, and how assets flow from your source files into the running game.

Download the runnable sample project

Use the verified ZIP bundle for this article to review the extracted example files, run the sample host, and repackage the sources without bin or obj folders.

The generated project tree

When you create a MonoGame DesktopGL project from the modern .NET templates, the starting structure is intentionally simple. The names may vary slightly depending on your project name, but the responsibilities are the same.

MyGame/
  MyGame.csproj
  Program.cs
  Game1.cs
  Icon.ico
  app.manifest
  Content/
    Content.mgcb
    bin/
      DesktopGL/
        Content/
          *.xnb
    obj/
      DesktopGL/
  .config/
    dotnet-tools.json
  bin/
    Debug/
      net8.0/
  obj/
    Debug/
      net8.0/

The important split is between source files you edit and generated files the tools recreate. You edit Program.cs, Game1.cs, MyGame.csproj, Content/Content.mgcb, and your art, audio, font, or effect files. The bin and obj folders are build outputs.

💡 Beginner rule: if the file describes your game or an asset you created, keep it. If it is under bin or obj, it is usually generated and can be rebuilt.

⚠️ Caution: Do not delete Content/Content.mgcb. It is not a generated output; it is the source content project that tells MonoGame which assets to process.

Program.cs: the entry point

Program.cs is where the application starts. In the template it creates one instance of your game class, runs it, and then disposes it safely when the game exits.

using var game = new Game1();
game.Run();

using var means the Game1 object is disposed automatically at the end of the scope. game.Run() hands control to MonoGame, opens the window, starts the game loop, and keeps running until Exit() is called or the window closes.

⚠️ Caution: Avoid putting game logic in Program.cs. Keep it as the launcher. Your loading, input, update, and drawing code belong in Game1.cs or classes called by Game1.

Game1.cs: your Game subclass

Game1.cs contains a class that inherits from Microsoft.Xna.Framework.Game. That base class owns the window, timing, lifecycle, and main loop. You override methods to configure your game, load content, update state, and draw frames.

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

public class Game1 : Game
{
    private readonly 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)
    {
        if (Keyboard.GetState().IsKeyDown(Keys.Escape))
            Exit();

        base.Update(gameTime);
    }

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

💡 Rename Game1 later if you like, but rename it consistently in the class declaration, constructor, filename, and Program.cs.

The .csproj file

The .csproj file is the .NET project file. It says what kind of application you are building, which .NET target framework to use, and which NuGet packages provide MonoGame and the content builder.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <RollForward>Major</RollForward>
    <ApplicationManifest>app.manifest</ApplicationManifest>
    <ApplicationIcon>Icon.ico</ApplicationIcon>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.2.1105" />
    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.2.1105" />
  </ItemGroup>
</Project>

TargetFramework chooses the .NET runtime your project builds against. MonoGame.Framework.DesktopGL supplies the cross-platform DesktopGL framework. MonoGame.Content.Builder.Task integrates content building into dotnet build, so assets listed in Content.mgcb are compiled before the game runs.

⚠️ Caution: Keep MonoGame package versions aligned. Mixing framework and content-builder versions can cause confusing build errors, especially after updating templates or restoring an old project.

Content.mgcb and the content pipeline

Content/Content.mgcb is the MonoGame Content Builder project. Open it with the MGCB Editor to add textures, sprite fonts, effects, sounds, songs, and other supported assets. The editor stores importer, processor, and build settings in the .mgcb file.

#----------------------------- Global Properties ----------------------------#
/outputDir:bin/$(Platform)
/intermediateDir:obj/$(Platform)
/platform:DesktopGL
/profile:Reach

#-------------------------------- References --------------------------------#

#---------------------------------- Content ---------------------------------#
#begin player.png
/importer:TextureImporter
/processor:TextureProcessor
/build:player.png

#begin Fonts/DefaultFont.spritefont
/importer:FontDescriptionImporter
/processor:FontDescriptionProcessor
/build:Fonts/DefaultFont.spritefont

The content pipeline converts editable source files into runtime-friendly .xnb files. For example, player.png might build to Content/bin/DesktopGL/Content/player.xnb during content building and then be copied beside your compiled game.

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

public class Game1 : Game
{
    private SpriteBatch _spriteBatch;
    private Texture2D _playerTexture;
    private SpriteFont _defaultFont;

    public Game1()
    {
        Content.RootDirectory = "Content";
    }

    protected override void LoadContent()
    {
        _spriteBatch = new SpriteBatch(GraphicsDevice);
        _playerTexture = Content.Load<Texture2D>("player");
        _defaultFont = Content.Load<SpriteFont>("Fonts/DefaultFont");
    }
}

Content.RootDirectory = "Content" sets the runtime root. Content.Load<Texture2D>("player") looks for a compiled asset named Content/player.xnb. Content.Load<SpriteFont>("Fonts/DefaultFont") maps to Content/Fonts/DefaultFont.xnb. You do not include the .xnb extension in Content.Load.

⚠️ Caution: Loading uses the asset name from the content project, not necessarily the original file extension. Use Content.Load<Texture2D>("player"), not Content.Load<Texture2D>("player.png").

💡 If an asset fails to load, check three things first: it is added to Content.mgcb, it builds successfully to .xnb, and the string passed to Content.Load matches the built asset path without an extension.

Generated files and folders

MonoGame and .NET create several folders while building. These folders are useful for the tools, but they are not the source of truth for your game.

It is normally safe to close the game and delete bin and obj folders if a build gets into a strange state. The next dotnet build recreates them.

app.manifest describes Windows application metadata used by the generated executable. Icon.ico is the application icon. The .config/dotnet-tools.json file records local .NET tools used by the project or template, such as MGCB-related tooling, so another developer can restore them with dotnet tool restore.

⚠️ Caution: Do not edit files inside obj to fix source problems. Those files are regenerated. Fix the original .cs, .csproj, or .mgcb file instead.

DesktopGL vs WindowsDX

The DesktopGL template uses MonoGame.Framework.DesktopGL and OpenGL through a desktop backend. It is the usual choice when you want one project that can target Windows, Linux, and macOS with the same MonoGame API.

The WindowsDX template uses the Windows DirectX backend. It is Windows-focused and can be useful when you specifically want DirectX behaviour or compatibility with Windows-only graphics expectations. Your Game1 code is usually very similar, but the framework package, platform setting, native dependencies, and content platform differ.

💡 For beginners, start with DesktopGL unless you have a Windows-only reason to choose WindowsDX. It keeps the project portable while you learn the MonoGame lifecycle and content pipeline.

Build, content-build, run flow

A normal run is a short pipeline. The .NET SDK restores packages, compiles C# code, MonoGame builds the content project, and then the game starts. If content is already up to date, the content-build step may finish quickly.

dotnet tool restore
dotnet restore
dotnet build
dotnet run

# If generated output looks stale, clean and rebuild:
dotnet clean
dotnet build
  1. Restore downloads NuGet packages and local tools described by the project.
  2. Compile checks your C# files and emits the game assembly.
  3. Content-build reads Content/Content.mgcb and converts assets to .xnb.
  4. Copy output places compiled content under the runtime Content folder beside the game output.
  5. Run executes Program.cs, which creates Game1 and calls Run().

Next steps

Need larger tap targets or higher contrast? Toggle enhanced spacing and high contrast in the header. Your preference is saved in session storage.

🎓 Test Your Knowledge

Check your understanding of the key concepts from this tutorial.

What class does your MonoGame game's main class inherit from?

What is the purpose of the ContentManager?

Which folder contains compiled content assets ready for the Content Pipeline?

When is GraphicsDeviceManager created?