Sunday, September 8, 2013

Code Snippet : An Exercise with Multiplatform Programming

So today I'm going to address an issue I ran into while creating Snake Hunt.

Snake Hunt was built in Monogame, an open-source framework that wraps around XNA.  The reasons I chose to use Monogame are because it's simple and Monogame promises to be incredibly multi-platform.  With this in mind, and the thought that after programming once I'll be able to release on pretty much every device, I jumped into making Snake Hunt.

Visual Studio Monogame Templates

So I threw together a Windows Store Project, and started coding assuming I could worry about the other platforms later.  See, this is how I perceived Monogame worked:


So, I finished the game, had it running perfectly, and decided to try other platforms.  First thing I realized was the project I was using was designed to build only to Windows 8.  Fair enough!  So I made a new Windows Phone project.  Now I needed the code to be moved over. . . so I just made copies of all the files and tried to build the project and I got a boat-load of missing reference exceptions.

See Monogame isn't a 100% wrapper around anything you could write, it simply wraps around the XNA framework.  Though the majority of what's written in a game would be within that framework, there are certain things that aren't, that would still be platform specific.  An example of this would be in app purchases and ads, which are handled differently on every platform.  A more accurate diagram of this follows:

So Monogame isn't the whole game, just part of it.  The remaining parts can easily be programmed for each platform, but the problem comes with how the game refers to it.  Since every platform works differently, the game would need to reference different code for each platform you're building for.  At this point that means you'd need a separate copy of the code for each platform, and then change each copy to work on that platform.  The problem with this is it causes a LARGE amount of duplicated code, and whenever you change something in your game, you have to change it in multiple places.

The solution to this is to use something called an interface.  An interface is a programming construct which defines different methods, and then allows other 'classes' to implement it, promising that those implementations will also have those methods.  We can then take the game code, and refer to just the interface, which would have methods like "PerformInAppPurchase" or "ActivateApps" etc.  Then, in each platforms project, we use the same game code, but just pass in that platforms specific implementation of the interface.  What we end up with is the game referring to each platform in the same way, keeping one copy of the code, and each platform handling their own specific stuff.  Like this:



So, with these realizations, I'm starting work on another project.  I'll post more information later, but the goal of this project is to put this design to the test.  It'll be a simple game, with basic gameplay and art, but will (hopefully) work on any of the platforms Monogame supports.

Project Setup

Even with these discoveries, I was still having trouble keeping the code base shared between so many different projects.  Everytime I made a new class, I had to copy it as a link to all the other projects.  In searching for a solution to this, I discovered this magical chunk of xml:
<Compile Include="..\MonoGUI\CStudios\**\*.*"> 
 <Link>%(RecursiveDir)%(FileName)</Link>
 </Compile>
EDIT:  Working with this a little further, it turns out that using this with Content needs to be done a little differently.  Assuming that you've made a WindowsPhoneXNAContent project (the only type that works in Visual Studio 2012), you'd use the following:
<ItemGroup>
    <Content Include="..\ContentProject\ContentProject\bin\Windows Phone\Debug\Content\**\*.*">
      <Link>Content\%(RecursiveDir)%(FileName)%(Extension)</Link>
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
  </ItemGroup>
Where "ContentProject" would be the name of the Content Project you made.
The problem I was having was that the .xnb was being cut off of the extension, which stopped the ContentManager from loading it.  Using %(Extension) fixes this.
END EDIT


Used in conjunction with the following project layout:
Basically, we start off with a "Library" project.  Create a folder within that, where we put all the source code for the game.  It doesn't matter what platform we put as the build target, since that project will never really be built.  From there, we make a project for each platform, and place the chunk of xml within the project file (Replacing MonoGUI with the Library project name, and CStudios with the folder that contains the source).

From this point on, every project now contains a "Link" to all files in the library project.  Not only will the sub projects automatically add any files you add to the library, but it keeps them up to date.  By setting them as a "Link" Visual Studio doesn't copy them into the local project, but instead keeps a reference to them.  When changed in the library project, the sub projects immediately have that update.

There are other files that remain local to only certain platform-specific projects, such as entry points (each platform launches the game differently, but they should all call something in the library code to do so), and the "Platform Specific" interface implementation I referred to earlier.

Hopefully this helps someone figure out how to do multi-platform programming!  I'll be putting another post up soon about my next game, that will be implementing this style.

2 comments:

  1. Great post. I like the pictures you made. It really explains things well.

    ReplyDelete
  2. I think this concept is called an "application layer."

    The idea there is that you create a platform specific adapter that gives you all the OS services you need that has the same interface regardless of which platform you're building it for.

    Which is what your post describes. I'm just trying to connect the dots from all the things I read. Happy hunting!

    ReplyDelete