I just recently finished porting my iOS game, SimplePhysics, to Windows 8 Metro WinRT. SimplePhysics is primarily written in C++, with just a pinch of Objective-C to get it working on the iPhone. Fortunately, the new WinRT platform is written in C++/CX, so in theory I should be able to reuse almost my entire code base!
This was true for the most part, but there were still some significant hurdles that I had to overcome to get my app running on Windows 8.
Learning the Framework
The first problem was the fact that I knew nothing about WinRT. It’s a completely new framework written to replace the age-old C-based Winapi. Whenever I encounter something that I know nothing about, I turn to my secret weapon: Pluralsight. If you haven’t heard of Pluralsight, then I highly recommend that you check them out. They make extremely high quality training videos that can get you up to speed on a new technology faster than any other method. They are now my #1 stop when learning a new tech. It does require a paid subscription, but they have a free 10 day trial, so you can watch the training video on WinRT for free. This video was particularly good, because it was done by Kate Gregory, who has written over a dozen books and is amazingly knowledgeable about the platform.
No Support for OpenGL
You have two options when making an app for the new Windows Store: DirectX or XAML. OpenGL is not supported, and likely will never be supported. This is very unfortunate when the vast majority of indie game developers are using OpenGL. So now you have to rip all that OpenGL code out and replace it with DirectX…unless there is another way.
Instead of ripping out my OpenGL code, I looked into using an OpenGL to DirectX wrapper library. The idea is that you just link with this wrapper library that implements a version of the OpenGL API that routes your OpenGL calls to their DirectX equivalents. Quite a few wrappers exist, but I couldn’t find any that support DirectX 11 (which is what you’ll need to use on WinRT). So, I set out to write my own wrapper library. It wasn’t too painful in my case, because the drawing code in SimplePhysics is very basic and I was only using around 25 methods from the OpenGL API.
I put the code up on Codeplex, so feel free to download it and check it out. It’s licensed under the MIT License, so you can do whatever you wish with the code. It also includes a simple Windows 8 application project that demonstrates how to use XAML based UI and OpenGL graphics together.
Asynchronous File I/O
This was the biggest hurdle for me. WinRT really enforces the idea that if you want to do some long running operation, you must do it asynchronously. This is understandable. They just want their apps to feel more responsive to users and really this is something that all devs should strive for anyways. However, in some cases it is quite a pain to convert working synchronous code to its asynchronous equivalent.
For example, nearly every game ever written persists the user’s settings to a file. In SimplePhysics, this is just a handful of saved blueprints and is only a few kilobytes in size at most. This is hardly worth converting to asynchronous code because (a) it is only done on start-up, and (b) modern hardware can read a few kilobytes lightning fast. We’re talking milliseconds.
Unfortunately, in WinRT, all file I/O operations are done using the IAsyncOperation class. For instance, if you want to read a file, you’ll have to do something like this:
void ReadFile(Platform::String^ fileName) { using namespace Windows::Storage; using namespace Concurrency; // First get the file...asynchronously auto folder = ApplicationData::Current->LocalFolder; task getFileTask(folder->GetFileAsync(fileName)); // Then read the file...asynchronously auto readBufferTask = getFileTask.then([] (StorageFile^ f) { return FileIO::ReadBufferAsync(f); }); // Then convert the buffer into something usable, like // a std::vector......asynchronously readBufferTask.then([requestId] (Streams::IBuffer^ b) { auto a = ref new Platform::Array(b->Length); Streams::DataReader::FromBuffer(b)->ReadBytes(a); _data.clear(); for (uint i = 0; i < b->Length; i++) { _data.push_back((char)a[i]); } // Now set a flag to indicate that _data vector is ready _dataIsReady = true; }); }
The code in this method isn’t too complex. WinRT actually has some nice ways to handle async operations, as seen with the “then” method, but it can still have a significant impact on an existing, working, code base that is using synchronous file I/O. Initially, I started down this path of re-factoring SimplePhysics to use async file I/O, but it was just taking too long. At the end of the day, I found that even though WinRT doesn’t expose any synchronous file I/O, good old fopen, fread, and fwrite still work just fine. The trick is to know where to read and write your files. If you try to read or write from the wrong location, your fopen call will fail. I stored all of my user settings files in the LocalFolder, which is the “root folder in the local app data store.”
Here’s some code that demonstrates how to write to a file, synchronously, using fwrite.
auto localFolderPath = Windows::Storage::ApplicationData::Current->LocalFolder->Path; auto filePath = localFolderPath + "\\test.txt"; FILE* file; _wfopen_s(&file, filePath->Data(), L"w"); if (file) { unsigned char data[] = { 'a', 'b', 'c' }; fwrite(data, 1, 3, file); fclose(file); }
In Conclusion
Other than that, the port from iOS to WinRT was relatively straight forward and mostly painless, and it’s exciting to see SimplePhysics in the Windows 8 Store. I hope these tips will help some of my fellow indie devs, and if you have any questions feel free to contact me.
Recent Comments