Converting a Microsoft XNA 3.1 Game to MonoGame on .NET 8: A Step-by-Step Porting Journey
In this article, Andrew Lock details the process of porting a classic Microsoft XNA 3.1 game from 2009 to MonoGame running on .NET 8. He covers technical steps, challenges, and solutions encountered during the migration.
Converting a Microsoft XNA 3.1 Game to MonoGame on .NET 8: A Step-by-Step Porting Journey
Author: Andrew Lock
Introduction
This article explores the process of porting a Microsoft XNA Framework 3.1 game—originally written in 2009—to MonoGame on .NET 8. Andrew Lock shares his hands-on experience, documenting the steps, issues, and solutions involved in bringing an old game back to life with modern tooling.
Background: Why Port an XNA Game?
Andrew was inspired by a Merge Conflict podcast episode discussing MonoGame, which implements Microsoft XNA APIs for contemporary platforms. Reminiscing about his own XNA-based clone of the game “Trash” (itself inspired by Nintendo’s Dr. Mario), Andrew wondered how feasible it would be to run his old codebase on modern frameworks. The original code targeted XNA 3.1 and .NET Framework 3.5, while MonoGame is built around XNA 4.0 and latest versions of .NET.
Approach Overview
The upgrade plan involved:
- Creating a MonoGame sample application.
- Replacing sample code with original project files.
- Iteratively resolving build and runtime issues.
The focus was to make the game run on current technology, not to refine or distribute it.
Detailed Porting Steps
1. Setting Up the New Project
- Used the updated
.slnx
solution format for .NET projects. - Created a cross-platform MonoGame desktop app using
mgdesktopgl
template. - Added the project to the solution and verified that the default app (the classic Cornflower Blue screen) runs.
# Create and convert solution
dotnet new sln
dotnet sln migrate
rm *.sln
# Create MonoGame project in Trash subfolder
dotnet new mgdesktopgl --output Trash
dotnet sln add ./Trash/
2. Transferring Game Files
- Copied all C# code files and content (such as .wav and .png assets) except:
- The legacy
.csproj
project file (used MonoGame’s instead). - The old
AssemblyInfo.cs
(attributes handled by SDK now).
- The legacy
Compiling: Version Issues
- Only one initial build error: Use of
GraphicsDevice.RenderState
not found in MonoGame/XNA 4.0.- Solution: Replace
RenderState
withRasterizerState
as per XNA 4.0 changes. -
Example fix:
// XNA 3.1: bool defaultUseScissorTest = spriteBatch.GraphicsDevice.RenderState.ScissorTestEnable; // MonoGame/XNA 4.0: bool defaultUseScissorTest = spriteBatch.GraphicsDevice.RasterizerState.ScissorTestEnable;
- Solution: Replace
-
MonoGame maintains impressive compatibility with XNA APIs, enabling mostly seamless compilation.
- Notable limitation: MonoGame omits some XNA namespaces (like
.Storage
and.Net
), for cross-platform reasons.
3. Content Pipeline Migration
Handling Audio Content (XACT to MonoGame)
- On first run, the game crashed due to missing
.xgs
(XACT Game Studio) files. - Instead of porting XACT directly (unsupported by MonoGame), switched to MonoGame’s sound APIs:
- Used
SoundEffect
,SoundEffectInstance
, andSong
types for audio. - Relied on the MonoGame Content Builder (MGCB) to import and process assets.
- Used
-
Ran the MGCB content editor:
dotnet mgcb-editor
- Most assets (e.g., .wav, images) processed fine, but .xap (XACT Audio Project) was unsupported.
-
Examined .xap content manually, rewrote usage to work with direct sound files.
SoundEffect effect = Content.Load<SoundEffect>("file.wav"); effect.Play();
4. Solving Font Issues
- Build failed on a missing font:
Narkisim
, referenced in a SpriteFont. - Discovered
Narkisim
is part of the Windows Hebrew language pack. - Solution: Installed the required font using Windows Update, restoring successful content pipeline builds.
5. Handling RasterizerState and Scissor Test Changes
- The next runtime error: attempting to set
ScissorTestEnable
on the defaultRasterizerState
object, which is immutable in XNA 4.0/MonoGame. -
Correction: Create a new
RasterizerState
object and assign it toGraphicsDevice
.spriteBatch.GraphicsDevice.RasterizerState = new RasterizerState { ScissorTestEnable = true };
-
Additional fix: Must set the
RasterizerState
before callingSpriteBatch.Begin()
, or better yet, pass it as a parameter.spriteBatch.Begin(rasterizerState: new RasterizerState { ScissorTestEnable = true });
6. Verifying the Final Result
- With all above issues resolved, the game launched and functioned correctly in MonoGame under .NET 8, including proper scissor testing and working gameplay.
- The source was made available on GitHub, though Andrew notes it’s not actively maintained or polished.
Summary and Takeaways
Porting an old Microsoft XNA Framework game (targeting .NET Framework 3.5 and XNA 3.1) to MonoGame running on .NET 8 proved surprisingly straightforward. Key migration challenges revolved around breaking changes in the XNA API, content pipeline differences, sound file handling, and font requirements. MonoGame’s strong compatibility with the XNA structure enabled most original code to work with limited updates. The project highlights the durability and adaptability of Microsoft’s developer platforms over the years.
Resources
- MonoGame
- Microsoft XNA Framework
- GitHub repository for “Trash” game by Andrew Lock
- Merge Conflict Podcast Episode #457
- MonoGame Content Pipeline Documentation
This post appeared first on “Andrew Lock’s Blog”. Read the entire article here