F# and Monogame Part 4 – Content Pipeline

If you’ve used XNA in the past you’ll have an opinion on the Content Pipeline.

For some, it’s an amazing way to prebuild and optimize content, and detect problems before deployment.

For others, it’s a pain in the bum.

Personally, I flip between the 2 opinions every time I come to use XNA.

F# and XNA

Now even on windows, using XNA, F# and the content pipeline successfully together is a black art. A couple of years ago I managed to hack around fsproj files enough to make it work with Visual Studio 2010, and released it to the world via the Visual Studio Gallery. The extension creates a template project which has an F# project reference an XNA Content project for a unified build. Nice (note if I get enough requests I can update to VS2012 as I recently used this for London Gamecraft (entry here – team effort – Team mates included Phil Trelford, Quinton Coetzee, Malcolm Knight, Anton Semenov & Thomas Trelford)).

Monogame and the content pipeline

Now, before I go any further, I think it’s key to say I imagine the situation here will change VERY soon. The Monogame guys are working hard on an integrated XNA like experience, which doesn’t require Windows.

So for now, to build content for iOS, Android, Mac, etc, you’re going to need

If you want to use XNA with VS 2012 you’ll have to follow the instructions here.

So now you have everything installed, you have 2 options:

1: Use the Monogame C# templates and Visual studio.

2: Knock together (or copy and paste from this blog for the lazy/time-pressed of you) a couple of MSBuild scripts to build the content.

Option 1 is more idiot proof, and if you’re happy to do it, then that’s fine. You dont need to read the rest of this post (sorry for letting you get this far before telling you).

Option 2 suits me better – I dont need to waste space on empty C# projects to build content (remember I’m primarily targeting F# here). I can build the content when I want to, rather than when Visual Studio determines it should (which is very often wrong). Also, once the script is written, you can simply copy and paste it to new projects, rather than having to create even more empty C# projects.

Credit to Shawn Hargreaves for the original concept.

Unfortunately Shawn’s example is out-of-date (XNA 1!) and wont work for Monogame. So first lets bring it up to date to work for XNA 4 (not Monogame yet) – this will process images (jpg/png), sprite fonts, effect, mp3 and wav files – everything you need for a 2D game:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
 <UsingTask TaskName="BuildContent" AssemblyName="Microsoft.Xna.Framework.Content.Pipeline, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553" />
 <PropertyGroup>
 <XnaInstall>C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86</XnaInstall>
 </PropertyGroup>
<ItemGroup>
 <PipelineAssembly Include="$(XnaInstall)\Microsoft.Xna.Framework.Content.Pipeline.AudioImporters.dll" />
 <PipelineAssembly Include="$(XnaInstall)\Microsoft.Xna.Framework.Content.Pipeline.TextureImporter.dll" />
 <PipelineAssembly Include="$(XnaInstall)\Microsoft.Xna.Framework.Content.Pipeline.EffectImporter.dll" />
 </ItemGroup>
<ItemGroup>
 <Content Include="*.bmp;*.png">
 <Name>%(Content.Filename)</Name>
 <Importer>TextureImporter</Importer>
 <Processor>TextureProcessor</Processor>
 </Content>
<Content Include="*.wav">
 <Name>%(Content.Filename)</Name>
 <Importer>WavImporter</Importer>
 <Processor>SoundEffectProcessor</Processor>
 </Content>
<Content Include="*.fx">
 <Name>%(Content.Filename)</Name>
 <Importer>EffectImporter</Importer>
 <Processor>EffectProcessor</Processor>
 </Content>

 <Content Include="*.spritefont">
 <Name>%(Content.Filename)</Name>
 <Importer>FontDescriptionImporter</Importer>
 <Processor>FontDescriptionProcessor</Processor>
 </Content>

 <Content Include="*.mp3">
 <Name>%(Content.Filename)</Name>
 <Importer>Mp3Importer</Importer>
 <Processor>SongProcessor</Processor>
 </Content>
 </ItemGroup>
<Target Name="Build"> 
 <BuildContent SourceAssets="@(Content)" PipelineAssemblies="@(PipelineAssembly)" 
 TargetPlatform="Windows" TargetProfile="HiDef" RebuildAll="true"
 OutputDirectory="XnaGameContent/"/>
 </Target>
 </Project>

As you can see theres a lot going on. Let’s take a few chunks:

<UsingTask TaskName="BuildContent" AssemblyName="Microsoft.Xna.Framework.Content.Pipeline, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553" />
 <PropertyGroup>
 <XnaInstall>C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86</XnaInstall>
 </PropertyGroup>
<ItemGroup>

This block defines a couple of important parts – the path to the XNA install (which may differ on 32 bit OSes) and a task to build XNA content.

<ItemGroup>
 <PipelineAssembly Include="$(XnaInstall)\Microsoft.Xna.Framework.Content.Pipeline.AudioImporters.dll" />
 <PipelineAssembly Include="$(XnaInstall)\Microsoft.Xna.Framework.Content.Pipeline.TextureImporter.dll" />
 <PipelineAssembly Include="$(XnaInstall)\Microsoft.Xna.Framework.Content.Pipeline.EffectImporter.dll" />
 </ItemGroup>

This chunk let’s MSBuild know to load the relevant assemblies for building XNA content.

<Content Include="*.bmp;*.png">
 <Name>%(Content.Filename)</Name>
 <Importer>TextureImporter</Importer>
 <Processor>TextureProcessor</Processor>
</Content>

The content tag’s get all the files (in the same folder) of a specific to process by a pair of importer’s/processors. The cool part is the %(Content.Filename) which will save each file to a file with the same name (with an xnb extension).

So now we have an msbuild file we can call like so

msbuild /t:Build xnacontent.build

Winnng!! But this is only half the story. As you’ve bypassed the XNA Content pipeline you’ll need to add all the xnb files to your project and have them copy to the output folder.

Building Monogame assets

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
 <UsingTask TaskName="BuildContent" AssemblyName="Microsoft.Xna.Framework.Content.Pipeline, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553" />
 <PropertyGroup>
 <XnaInstall>C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86</XnaInstall>
 </PropertyGroup>
<ItemGroup>
 <PipelineAssembly Include="$(XnaInstall)\Microsoft.Xna.Framework.Content.Pipeline.AudioImporters.dll" />
 <PipelineAssembly Include="$(XnaInstall)\Microsoft.Xna.Framework.Content.Pipeline.TextureImporter.dll" />
 <PipelineAssembly Include="$(XnaInstall)\Microsoft.Xna.Framework.Content.Pipeline.EffectImporter.dll" />
 <PipelineAssembly Include="$(MSBuildExtensionsPath)\MonoGame\v3.0\MonoGameContentProcessors.dll" />
 </ItemGroup>

 <PropertyGroup>
 <MonoGamePlatform>iOS</MonoGamePlatform>
 <XnaCrossPlatformGroupID>ee774cf1-18a1-4b6e-8b5c-adcc535bad2a</XnaCrossPlatformGroupID>
 </PropertyGroup>
<ItemGroup>
 <Content Include="*.bmp;*.png">
 <Name>%(Content.Filename)</Name>
 <Importer>TextureImporter</Importer>
 <Processor>MGTextureProcessor</Processor>
 </Content>
<Content Include="*.wav">
 <Name>%(Content.Filename)</Name>
 <Importer>WavImporter</Importer>
 <Processor>MGSoundEffectProcessor</Processor>
 </Content>
<Content Include="*.fx">
 <Name>%(Content.Filename)</Name>
 <Importer>EffectImporter</Importer>
 <Processor>MGEffectProcessor</Processor>
 <Defines>SM3</Defines>
 </Content>

 <Content Include="*.spritefont">
 <Name>%(Content.Filename)</Name>
 <Importer>FontDescriptionImporter</Importer>
 <Processor>MGSpriteFontDescriptionProcessor</Processor>
 </Content>

 <Content Include="*.mp3">
 <Name>%(Content.Filename)</Name>
 <Importer>Mp3Importer</Importer>
 <Processor>MGSongProcessor</Processor>
 </Content>
 </ItemGroup>
<Target Name="Build"> 
 <Exec Command='SETX MONOGAME_PLATFORM "$(MonoGamePlatform)" > NUL'/>
 <BuildContent 
 BuildConfiguration="$(MonoGamePlatform)"
 SourceAssets="@(Content)" PipelineAssemblies="@(PipelineAssembly)" 
 TargetPlatform="Windows" TargetProfile="HiDef" RebuildAll="true"
 OutputDirectory="iOSGameContent/"/>

 <Exec Command='SETX MONOGAME_PLATFORM "" > NUL'/>
 </Target>

 </Project>

Highlighted above are the differences to build content for iOS. Nothing too exciting – stick “MG” in front of the Processors is the headline, and some environment variable setting down the bottom. Dont forget to include the MonogameContentProcessors and the top too.

Like the XNA version you can run the file from the command line like so:

msbuild /t:Build monogamecontent.build

Now this builds the content for iOS. I’ll give you 3 guesses what you need to change to make it build for Android (hint: Set MonoGamePlatform as Android). You may find it handy to parameterize the msbuild file so you don’t have to maintain versions for each platform – I’ll leave that as an exercise for the reader.

Tying it all together

Back in Xamarin-land, you’re going to need to add the resultant xnb files to your iOS/Android projects.

For iOS drop them in a folder and set the build action to “BundleResource”. You’ll have some mp3’s if you built Songs – you’ll need to add those too. Make sure you set the content folder in the Game class to match.

For Android drop them in a folder in the Assets folder. Add the content to the project and set the build action to AndroidAsset. Make sure you set the content folder in the Game class to match the name of the new folder (not including the Assets part of the path).

BUT…….!

After all that – you may find that a lot of the above isn’t even necessary for your project! The nice thing about Monogame is that you CAN load in unprocessed content (unlike XNA) easily, with common formats such as jpg, png (and depending on the platform) even mp3 (I think!). If you don’t need extensive content support there’s a reasonable chance Monogame might just work for you without the need for any of the above!

Monogame is an awesome platform that builds on the success of XNA 4, and is possibly the easiest way to do cross-platform development of games. The Gamecraft entry was ported to iOS (from Windows XNA) in 10 minutes. Hopefully with this short series I’ve exposed some of the difficulties of using F# and Monogame together, and shown how to overcome those problems effectively.

Part 1

Part 2

Part 3

About thedo666

Software developer trying to learn a new language - English!
This entry was posted in Android, F#, iOS, Mono, Monogame. Bookmark the permalink.

3 Responses to F# and Monogame Part 4 – Content Pipeline

  1. Pingback: F# Weekly #33 2013 | Sergey Tihon's Blog

  2. Pingback: F# on Android

  3. Pingback: Building a game in a day

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s