This post (probably the first in a series) will describe my experiences trying to convert an existing XNA project to MonoGame. My intention is to port my (previously WP7) game to iOS, while at the same time keeping a Windows version working. I don’t really care about the Windows Phone version anymore.
My current understanding is that to accomplish the above, I’ll need 3 projects:
- The original VS2010 XNA project (or perhaps just the content project?)
- A VS2010 MonoGame project for Windows
- A project for MonoDevelop on my Mac
I use SVN for source control, and this is likely how I’ll keep files in sync between my PC and my Mac.
My worry is the work required to maintain all these projects through changes. If I’m on iOS and I need to add a texture or something, the workflow will look like:
- On the PC, add the texture file to the XNA content project and rebuild
- Add the resulting xnb file to the Windows MonoGame project
- Commit to SVN
- On Mac, update the project to the latest version on SVN
- Add the new xnb file to the iOS project in MonoDevelop
- Rebuild an deploy to iPad
This is a pretty ridiculously inefficient workflow. It’ll be almost as much work adding a new code file, except that at least I don’t need to leave the Mac to do that. I should be able to add it and push it back up to my repository However, I’m worried about how adding new files on the Mac will work with VisualSVN (I use VisualSVN (a VS plugin), which automatically adds any project files to the repository. It’s great!)
There are conflicting descriptions of the necessary prerequisites on the MonoGame site, but this is what I’m trying (I already have VS2010 and XNA 4.0 installed): Windows Phone 7 SDK (needed for content building in MonoGame!) and OpenAL (an audio layer), and of course the MonoGame libraries and VS templates themselves.
I would kind of like to just modify the XNA project files and convert them into a MonoGame project, but I don’t see anyone doing that. The recommended method seems to be to create a new MonoGame project and re-add all your code files manually.
The first issue I encountered is that the MonoGame VS templates didn’t show up, so I could not create a new MonoGame project. This turned out to be a Visual Studio bug, where it was looking the wrong Documents folder (I moved my Documents folder from it’s default location when I set up my machine 6 months ago. Visual Studio still hasn’t realized this).
New MonoGame project
I created a new blank MonoGame (Windows OpenGL) project and successfully built it.
Next, I manually added links to all my game files to it. I’m lucky that this is a very small game, or else this would be really complicated. As it stands, I still have 7 subfolders for my game files, all of which needed to be added manually. Very tedious.
Next, I had to delete the Game1.cs that was added by default to the MonoGame project and add a link to the real Game1.cs. Then I had to modify Program.cs slightly in order so it used the correct namespace.
I only had a single build error after this, so that’s pretty promising. Apparently MonoGame adds an Input class to Microsoft.Xna.Framework.Input, which resulted in an easy-to-fix name conflict.
Just for kicks, I ran the MonoGame version to see if it would make it as far as the content loading, and it did! Promising so far.
Time to add content. The MonoGame template has a Content folder in it by default. I also added subfolders under that in order to mimic the file structure in my XNA content project.
Then I added all my content xnb files as links. These are of course files generated by building the original XNA project, and exist under the bin\x86\Debug or bin\x86\Release output directories there. I chose to add the xnb files generated in the Debug version. This whole thing is kind of messy, but I believe the MonoGame folks are working on a fix for this.
I’m also going to do some extra work to ensure all these files get added to the subversion repository, but I’ll worry about that later (once I try to get stuff running on Mac/iOS). Needless to say, it will result in some manual labor every time I add a file.
Next, as per some tutorial I saw, I set the properties of all the content file links to Build Action: Content and Copy to Output Directory: Copy if newer. You can select multiple files at once to do this, so it isn’t too bad.
Then, I built and ran the project, and it “worked”!
Well it worked insofar as it didn’t crash. The immediate obvious issues were that some audio didn’t work, and some of the graphics blend modes (and z-order?) seemed to be incorrect.
At first it looked like SoundEffect.Play was broken, but SoundEffectInstance was fine (since the only sound that worked was the one played through SoundEffectInstance). But upon further investigation, it seems to be dependent on the particular wav file.
The broken ones (which is all but one of my sounds) have a size of 0 in the SoundEffect.Size, despite SoundEffect._data having apparently valid-looking data.
After about an hour of poking around trying to figure out what was unique about the one sound that worked, I stumbled upon it. It was built using “Best” for the compression settings. So it looks like MonoGame doesn’t work with compressed wav files.
I’ve found some mention that you need to use MonoGame-specific content pipeline processors (MonoGame.ContentPipeline.Processors.dll), but there is no such assembly included in the MonoGame install.
Next it was time to tackle the graphics issues. I was hoping perhaps it was just some uninitialized state of mine, but unfortunately it’s related to the stencil buffer.
In short, MonoGame doesn’t appear to support a stencil buffer for the back buffer. I’m able to create a RenderTarget2D with DepthFormat.Depth24Stencil8, but not the back buffer. I don’t know if this is a bug, or some limitation of OpenGL. To work around it I had to render to an intermediate render target and then to the back buffer. That’s pushing a lot of extra pixels though (a problem when I port to iOS).
An additional bug (or different behavior to XNA) is that the GraphicsDevice.PresentationParameters retain the default settings for BackBufferWidth and BackBufferHeight at the time of the Game.Initialize() method (480 x 800). They only get set to the actual back buffer size after the call to base.Initialize().
After dealing with those issues, things were still not quite right.
The game elements were no longer showing where I told them not to draw (thanks to the stencil buffer working), but the Perlin screenwipe I’m using still wasn’t functioning properly.
A little bit of investigating showed that it was BlendState.ColorWhiteChannels that was essentially being ignored. Again, I’m not sure if this is an OpenGL limitation, an unimplemented MonoGame feature, or a bug. I was able to work around the problem – but, like before, at the expense of pushing more pixels.
In my next post, I’ll talk about my experience getting it up and running on iOS. Should be interesting!