Targeting Win32 and WinRT/Metro at the Same Time

I’ve seen some developers assume that if you write an App for WinRT/Metro, you have to write it exclusively in C++/CX, a variant of C++ with lots of Microsoft-specific extensions. In reality, you only really need C++/CX to interface with WinRT, but everything under that layer can be clean ISO C++! In this post I’ll explain how I’m designing my Ogre game to run inside Windows’ Metro UI while still supporting other platforms such as Win32, Android and iPhone on the same code base.

A good start is to look at what’s different between a normal desktop application and a Metro application and then develop a design concept where these differences are abstracted or wrapped so the core game code does not need to know about them.

WinRT vs. Win32

ActionDifferences
Creating the game’s window

In a Metro app, you cannot create any kind of windows. Instead, your app provides an IFrameworkView which will be handed a system-created window via IFrameworkView.SetWindow().

I’ll move the window creation part into the platform-specific code and my core game code will just manage Ogre’s scene graph, assuming that a window is already there.

Querying Input Devices

Metro apps are given notifications when the user presses a key, moves a pointer (mouse, pen or finger) and so on. Being WinRT events, these notifications can only be subscribed to from a C++/CX class.

There’s already an input library for Ogre called OIS that abstracts input devices and Ogre’s WinRT sample browser has some adapter classes to feed the OIS input devices from WinRT callbacks. In this case, however, I decided to port my Nuclex.Input library to C++ because its design takes care of several other porting issues.

Running the game loop

Metro apps need a game loop, too, but the message pump looks a bit different and there are additional exit signals.

Here, I’ll simply follow the scheme that has already served me well in XNA: instead of running its own game loop, the game class will simply provide two methods, Update() and Draw() that it expects to be called. The platform specific code can then execute whatever game loop is appropriate and call these methods.

Exiting your game

Metro apps can be closed at any instant. The application is immediately put in the background and may ask for up to 5 seconds additional time to save its state before it is forcibly terminated.

This concept cannot be isolated from the game, but it can be embraced: I’ll design my game so that it can be closed at any moment and will save its state. It’ll be a nice feature for casual players on the desktop.

Accessing the file system

Metro apps may not access the system’s local files at all. There are two locations you do have access to: you can read from the app’s install directory and all subdirectories thereof and you are given another directory where your app can store its state and other data.

Not too troublesome – my game just needs to be told where to put its saves and where it is installed.

Consequences

Given the window creation and main loop differences, it’s clear that I need a shell around my core game that deals with the platform-specific code for starting up and shutting down the app. This shell can also set up and provide abstractions for querying input devices, accessing files and any other things that might still come up.

As a way to enforce these abstractions, I will put my actual game code into a DLL and load it from the wrapper applications. That reduces the risk of accidentally mixing platform dependent code with the core game code that should stay platform independent. I’ll also try to most development on WinRT, the more restrictred of the two platforms.

UML class diagram showing how I plan to make IslandWar platform-independent