Converting an XNA4 Game to FNA
This tutorial will walk you through converting an XNA4 project to the open source FNA library. By the end of this tutorial you will have your XNA project running on Windows, Mac and Linux.
What is FNA?
FNA was originally called MonoGame-SDL2, a branch of MonoGame that uses SDL2 under the hood. This branch is managed by Ethan Lee, who used it to port games such as FEZ, Dust: An Elysian Tail, Capsized, Gateways, Escape Goat and more.
Eventually the branch contained several major rewrites that made it difficult to merge back into the mainline version of MonoGame, thus FNA was born. FNA focuses solely on accurate XNA4 behavior for desktop platforms (Windows, Mac, Linux).
In the past, something I’ve struggled with understanding is the open source nature of MonoGame. From what I’ve seen, a lot of people seem to fork from the mainline, and then patch it up to work for their game. There’s nothing wrong with this, but as someone who doesn’t want to spend a lot of time on underlying tech, I’ve been looking for something that “just works” with my XNA4 code. FNA has been that solution for me!
This tutorial will walk you through the process of porting your XNA4 game over to FNA.
Tips and Disclaimers
1. Download the latest version of FNA here. If you don’t have Git installed, you can press “Download Zip” on the right side of the screen.
Make sure SDL2#, TheoraPlay# and MonoKickstart are also downloaded and placed in their respective folders under ThirdParty (if you download the zip you’ll have to get these manually).
Note: We are about to prebuild the FNA library for use in our new project, but you can also add the .csproj files from FNA into your new project so you can edit the library and look at the changes a lot quicker than rebuilding the files everytime.
2. Open the FNA solution and press F5 to build the library (preferably in Release mode). This will spit out three .dll files in the bin folder called “MonoGame.Framework.dll”, “SDL2-CS.dll” and “TheoraPlay-CS.dll”. Essentially, these files contain the code needed to replace the XNA libraries in your project! If you get an error message saying the libraries cannot be launched, that means they were built successfully (probably the only time an error message is a good thing)!
3. Now we need to download the native libraries used by FNA. Ethan has provided some of them here. The other ones you need are SDL2, SDL2_Image, OpenAL Soft, and SDL2_Mixer. You'll want to download the 32bit runtime binaries.
For now we'll be using the Win32 libs provided from Ethan's site. If you're targeting Mac and Linux, those libs are also in the file we downloaded, but we won't use those until later on in the tutorial.
4. Make a copy of your project to use for the FNA version. Copy the .dlls from steps two and three next to your .csproj file. In Visual Studio, right click your project name and go to "Add-> Existing Item..." and add all of these .dlls to the root of your project.
It's easiest to just add all of the libs and move to the next step, but you really only need to add the libs your project requires at runtime. For example, if you don't use Song/Video, you'll only need SDL2 and OpenAL Soft. SDL2_mixer/libvorbisfile are used for Song, TheoraPlay is used for Video, and libogg/libvorbis are used for both Song and Video.
5. In Visual Studio, select all of the.dlls we just added. Under Properties, change Copy to Output Directory to "Copy if Newer". If you don't know what this does, whenever you build your game this will move the .dlls to the bin folder so the .exe can find them!
1. In Visual Studio, look under References and remove any reference that contains the words "Microsoft.Xna".
2. Right click References and click "Add Reference". Now go to the browse tab and add “MonoGame.Framework.dll”, “SDL2-CS.dll” and “TheoraPlay-CS.dll”. The other .dlls we downloaded do not need to be referenced from here.
3. At this point, you should be able to run your game with FNA! My game did not immediately run because I was using the namespace Microsoft.Xna.Framework.GamerServices, which isn't in FNA since it's mainly for Xbox 360 development. There might be a few minor things like this you have to edit, but it shouldn't be anything too bad!
Note: If your project relies on Microsoft.Xna.Framework.GamerServices or Microsoft.Xna.Framework.Net, there's a separate assembly called MonoGame.Net.dll that's been introduced to help standardize the netcode for the library. If you're interested, there's more reading here.
Note Two: In FNA, songs are Ogg Vorbis, Videos are Ogg Theora. Keep the XNB files, throw out any WMA/WMV files if applicable.
4. If you're using custom shaders (.fx files), your game will crash when loading them (we're about to fix that). If you're not using custom shaders, there's a good chance you're done porting for Windows at this point! Yay!!
Using Custom Shaders
Like MonoGame, FNA uses a new file type called MGFX for shaders. Here's the reasoning for it from the MonoGame documentation:
"For MonoGame we have the burden of supporting stock and custom effects for desktop GLSL, mobile GLSL, DirectX HLSL, and custom formats like that of the PlayStation Mobile. There currently is no effect system or shader language that supports all the platforms we require, forcing us to build a new custom effect system."
If you don't understand the difference between HLSL and GLSL, the TL;DR version is OpenGL/GLSL is supported on many platforms, whereas DirectX/HLSL is mainly used on Microsoft platforms. Since FNA supports Windows, Mac and Linux it uses OpenGL underneath.
Thankfully for us, there's a tool called 2MGFX by Tom Spilman that will convert our .fx files over to this dandy new format! But before we convert our files, we'll need to make some edits to our shaders:
1. Inside each of your .fx files, you will need to change how you compile your pixel shader and vertex shader: it will need to compile for shader model 4. To help keep your shader working in both XNA4 and FNA, you can do something like this:
PixelShader = compile ps_2_0 PixelShaderFunction();
PixelShader = compile ps_4_0_level_9_1 PixelShaderFunction();
PixelShader = compile ps_3_0 PixelShaderFunction();
PixelShader = compile ps_2_0 PixelShaderFunction();
The "compile ps_4_0_level_9_1" line is the important part, you'll need to add that to any pixel shader you have. For vertex shaders use "compile vs_4_0_level_9_1". Once you do this, your shaders should be ready for conversion.
2. Open up the FNA repository you downloaded earlier and look in the Tools folder for 2MGFX. Once you find it, open the solution!
3. Open Program.cs. Everywhere you see "return 0" or "return 1" add this line of code right before that call: System.Threading.Thread.Sleep(5000); 2MGFX is a console application, and as soon as you run it, it will close. Since it displays helpful error messages to us, calling Thread.Sleep will keep the window open a bit so we can read any messages before it closes.
4. Run 2MGFX in Release and then go to the bin\Release folder where 2MGFX.exe is located. Now copy all of the .fx files you want to convert to this location, right next to the .exe.
5. Go to the start menu and open Run. In the Open line, run this:
C:\Path To Your Release Folder\2MGFX.exe ShaderName.fx ShaderName.fxg /Profile:OpenGL
You will need to fill this out to work on your machine. The first parameter is the path to 2MGFX.exe, the second parameter is the name of the .fx file you want to convert and the third parameter is the new MGFX file that will be spit out. Since FNA uses OpenGL, the flag at the end tells 2MGFX we're targeting that profile; other flags you could use here are "DirectX_11" or "PlayStation4".
Repeat this step for all of your shaders.
6. Return to Visual Studio and remove your old .fx files, replacing them with the new .fxg files we just created. Once added, select all of the shaders and change the Build Action property to "None" since we're not going to run these through the normal content pipeline!
After this, you should have no problem loading in and using your effects - your game should now be running in Windows! *fireworks go off*
Note: Ethan is currently planning on removing the dependency on MGFX in future versions of FNA. He intends to make it where the original XNB shader files can be used without recompilation. I will try to update the tutorial when this happens.
Building Your Game for Mac
Remember the native libs we downloaded earlier for Mac and Linux? It's time to use those!
1. Inside the FNA folder you downloaded, go to ThirdParty\MonoKickstart. Dig through this until you find the folder named "precompiled", this folder contains some precompiled .dll files from Mono (an open source implementation of .NET) so we won't have to require a system install of Mono for Mac and Linux versions of our project. These files will be needed for all non-Win32 targets (Mac and Linux)! Also located here are some additional folders that we need to drop the downloaded libs into. The file you downloaded should already have the libs separated into their respective folders, you just need to copy them over. The Linux libs go into the "lib" and "lib64" folders, and the Mac libs go into the "osx" folder.
2. Rename "Kick", "kick.bin.x86", "kick.bin.osx" and "kick.bin.x86_64" to the name of your .exe instead of the word "kick". For example, if your exe name is MyGame.exe it would look like "MyGame.bin.osx" and so on.
3. Open up the file now called "MyGame". Inside this file, everytime you see the word "kick" replace it with the name of your game's .exe, similar to what we just did in the last step.
4. Now we'll need to put together the "app shell" to distribute to Mac players. The shell is just a folder organized a certain way, so we can easily put this together on Windows. To make this super easy, you can download an example shell here. (Thanks to Nick Gravelyn, developer of Shipwreck for providing this!)
5. Open the example shell and go to Contents\MacOS. You can think of this folder as the working directory for your game. Copy and paste your game's exe, Content folder and managed .dll files to this location.
6. From the precompiled folder, copy all of the .dlls and the files called "MyGame" and "MyGame.bin.osx" into the Contents\MacOS folder. You will also need to copy the files in the precompiled\osx folder into the Contents\MacOS\osx folder, and all of the files in the precompiled\mono folder into the Contents\MacOS\mono folder.
7. Replace and rename all remaining information that belongs to Shipwreck with your own game's info. To create an icon for your game, you can use this website to generate the file.
8. We now need to package the .app folder so we can distribute it on Mac. Something worth noting is that the .app folder is technically a folder, but OS X treats this as a file! We'll need to treat the app bundle as if it's a file and have the folders organized correctly - if you followed the example shell format, you shouldn't have anything to worry about. Use the program called "Disk Utility" found in OS X to package the .app folder (File -> New -> Disk Image From Folder).
For both OS X and Linux, the bin files in addition to the renamed "Kick" file must be marked as executable! If you just zip up the .app folder from Windows it's possible that you can lose these permissions. You should now be ready to test your project on OS X, and if it works properly, distribute it on OS X!
Building Your Game for Linux
1. You will need to be running in Linux to complete this step.
2. We'll need to make an installer for Linux, Ethan has provided a program to do this here.
3. Replace all of the examplegame information with your own game's info.
4. Run ./build.sh YourGameName and a Linux installer will be spit out! Be sure to test your project on Linux before celebrating!
I hope this was useful for people that would like to continue using XNA code in the future. I ran into a few hiccups getting my game working 100% accurately, but I probably got up and running on Windows in under eight hours. I'm personally pretty impressed with that!
- Michael Hicks (@michaelartsxm)