Porting From Mobile To PC with MonoGame
This week I was supposed to have started on the initial gameplay testing for Jetboard Joust but I came up against a rather nasty snag.I develop using Xamarin Studio on a Mac and had been using the GenyMotion Android emulator for my main testing platform. This probably seems strange given that my main target platform is PC, but GenyMotion generally runs extremely well and I don’t want to have to buy another machine just for development purposes. Unfortunately I discovered a problem with GenyMotion in that it seems to just ‘miss’ some keyup/keydown events. The problem is intermittent but bad enough to make serious gameplay testing on the platform impossible – no response from their support either.
That means I need another platform for testing. The iOS simulators are hopeless for graphics performance (and don’t respond to keyboard control as far as I’m aware), Google’s stock Android emulators take an age to launch/install builds and the Xamarin Android Player, though fast, is still pretty flaky. That left Xamarin.Mac as the only ‘native’ Mac option but there’s a hefty additional licence charge for that (or at least there used to be – I couldn’t quite work out what’s going on with Xamarin.Mac since the Microsoft buyout).
As a result of this tragic state of affairs (remember when Apple used to take x-platform development seriously in the initial OSX days?) I decided the only option would be to ditch Mac native and move to running Windows under VMWare Fusion (at least for any development that requires serious gameplay testing). Quite a change. I’ve done this before for ‘Attack Of Giant Jumping Man’ though so was optimistic that it should be a workable solution, plus I’d have to do the PC port anyway at some point – may as well get on with it.
So I started with a brand new Windows 8.1 VM and a fresh installation of Visual Studio 2015. I’ve been using MonoGame 3.2 up to this point but this was as good a time as any to update to 3.5. Installation of the various components was a breeze. I chose the DesktopGL target as it was most similar to the target I’d worked on for ‘Attack Of Giant Jumping Man’ (so hopefully the few bits of platform-specifc code I’d had to write could be re-used) and it didn’t take too long to get my project to compile. The only problem I ran into was that references to the Microsoft.Xna.Framework.GamerServices namespace couldn’t be resolved. For some reason the reference to the assembly that contains these wasn’t included in the MonoGame template project and had to be added manually (Add Reference->Extensions and then choose the appropriate MonoGame.Framework.Net assembly for the platform you are targeting, its a bit confusing as all the assemblies are named the same in the list so you have to click on each one to see more info).
I’m using the ‘shared folder’ feature of VMWare Fusion to share my source code directory between Mac and Windows – if I import the source files as links then both my Xamarin Studio projects on MacOS and my Visual Studio projects on windows both remain perfectly in sync – nice!
Next step is to import all the content – unfortunately I can’t figure out a way to keep all these files in sync as importing a file as a link from a shared folder doesn’t seem to work in the MonoGame pipeline tool. This is a bit of a bummer but not to much of an issue at the moment – hopefully I can figure something out eventually.
Only issue with the content was that I was getting an error when compiling my custom shader files due to a missing DirectX component (‘d3dcompiler_43.dll’) despite having DirectX 11 installed. I followed the instructions to fix this here (using the second method, not the full install) and all was fine.
So now everything would compile and run. Imagine my joy when, on launching, all I got was the garbage you can see in the GIF on the right. Complete gobbledegook. Spirits totally crushed. What. The. Hell.
I had absolutely no idea what was going on here and no idea where to start debugging. Nothing I thought of initially had any effect. Jetboard Joust runs on MEAT, my own (originally Java-based) 2D gaming platform that has been over ten years in development. MEAT is another layer of abstraction above MonoGame and fairly complex making it difficult to strip things down to MonoGame basics and do a few simple tests but this is clearly what I needed to do.
I decided to run a few simple MEAT tests first and see if I could get anything up and running…
1. Load image and draw sprite
2. Load image and draw sprite with clipping (as if from sprite sheet)
3. Load image and draw sprite with crop (MonoGame ScissorRectangle)
4. Load image, render to offscreen buffer (RenderTarget2D) and then to screen.
…all of these worked fine which was encouraging to an extent but didn’t get me any closer to a solution. However the next test produced some very strange results.
One of the MEAT classes is a graphical font class – a bitmap font and associated metrics data are stored in a single file which can be used to easily render bitmap text to screen. When I tried a test render using one of these graphical fonts the text would appear OK and then mysteriously disappear after around 30 seconds on screen. Bizarre. This mysterious disappearance only happened when my game template code (that handles all the main menus and stuff) had been executed at startup, ie at least 30 seconds before the problem occurred.
So all I could do was to comment out chunks of the game template code, launch the app, and then run a timer for approx 45 seconds to see if the font disappeared – an incredibly tedious process reminiscent of debugging on J2ME handsets. Eventually I narrowed it down to the line of code that was causing the problem – I was reassigning the property originally assigned to the graphical font that was drawn to screen to a different graphical font. Even though this was a mistake on my part there is absolutely nothing ‘wrong’ with this and it wasn’t causing a problem on any other platform. I had to test and retest several times to convince myself that this line of code was the problem but it was – as soon as I didn’t reallocate the property once the font was drawn to screen the test font didn’t disappear and the entire game ran perfectly!
All I can think of is this had something to do with garbage collection of graphics memory. Reallocating the property meant that the garbage collector (incorrectly) thought the memory should be freed which resulted in some kind of graphics meltdown. This would explain why it took around 30 seconds for the problem to appear – it only happened when the garbage collector kicked in. I create the font images using Texture2D.FromStream() rather than the Content.Load() methods in MonoGame which is slightly weird and could be something to do with it as well – I doubt this is as well tested as the Content.Load() methods.
Anyway, one can’t really blame the MonoGame team for missing such an obscure issue and even with the amount of time I wasted over this it was still a pretty fast cross-platform port so kudos to them. Android/iOS to PC in around a day with about 99% of the codebase consistent – not to be sniffed at! Nice to see the issues with the XBox controller fixed in MonoGame 3.5 too!
Dev Time: 1 days
Total Dev Time: approx 32.5 days
previous | next