I recently ported my XBLIG title to Windows Phone 7. I mainly did this as an effort to educate myself about the phone app marketplace, considering that I spend most of my time developing for PC & Xbox normally. I learned a number of interesting lessons in the process, specifically around app publishing, ad support, and XNA porting.
Porting XNA Code
I opted for a Windows Phone target because I already had a working codebase for XNA games, although that codebase was only functional on PC & Xbox. The effort of porting to WP7 was relatively straightforward although not without pains. Creating a “Windows Phone Copy” of the project did most of the basic work, setting up build targets and duplicating the code project. I found that I needed to also duplicate the content project because I needed to tweak the content for the phone deployment, mainly focused on removing shaders and some large textures, and tweaking fonts for the small screen.
The first challenges for coding came along when trying to support the “Reach” target for XNA, which removes support for custom shaders and a few other features. I easily replaced most of the shaders with pre-made pseudo-fixed-function ones like DualTextureEffect, although I had to write a custom content processor to invent new texture coordinates for the second texture, but the EnvironmentMapEffect (which I use in bullet-cam mode to reflect the environment on the bullet) posed a bigger challenge. At first it looked like it was working alright with my render-to-cube-texture approach, but after doing some playtesting it was apparent that the environment map wasn’t quite right: it was mirrored along the X-axis, due to the fundamental differences between the XNA coordinate system and the DirectX cube-map texture format, and in a custom shader you’d simply invert the X value of the texture coordinate when calling texCUBE. Suffice to say that I spent considerable time investigating this issue and trying a variety of fixes, but ended up settling on a custom projection matrix to mirror along the X-axis, combined with a mirroring flag propagated through the rendering system to reverse the clockwise-culling behavior on meshes.
The second challenges for coding involved the drastic change of input systems, from analog thumbsticks and triggers to a flat touch-screen. I ended up implementing a custom button system and dual-touch tracking to support two thumbs pressing on each side of the screen. The buttons support a few “events” such as touch-down, touch-up, and touch-drag; touch-drag is the interesting one because it allows for use as an analog input. I tried two different thumbstick input schemes: absolute positioning, similar to a real thumbstick where the touch position relative to the button center indicates a constant vector; and relative positioning, where a stationary touch imparts no movement but a moving touch does impart movement. The relative positioning approach seemed much more stable and intuitive to me and others. I also made the “trigger” button into an analog system similar to the implementation on the Xbox: you must start at the top and drag downwards, and the trigger will actuate at a “random” but consistent vertical position. Based on marketplace reviews, it seems this input scheme is either perceived as awesome or terrible, so I’ll consider it a success overall.
The marketplace for apps and games on WP7 are distinct, and the games marketplace is dominated by premium Xbox Live titles. When viewing the catalog of games, you are given four “views” of the available content, with some overlap between them, in order: Xbox Live, Top, Free, New. “Xbox Live” is self-explanatory, but due to its prominent placement and the knowledge that those games bear the fruit of Achievements, the games on that list also dominate the “Top” list, which I assume is populated only by non-free games according to units sold, such that you can scroll for pages and not see anything other than Xbox Live games. The “Free” list is fairly straightforward, there are a few free Xbox Live games but the don’t dominate this list, and again I assume the list is populated according to download numbers over time. The “New” list is a wasteland of un-vetted randomness, and I doubt many people even look there due to the extreme noise-to-signal ratio. As a result of this analysis, I concluded that any attempt at making a paid-download game would be a lost cause, and that publishing a free game is the only option. I’d rather not do work totally for free, and I’m also interested in the burgeoning ad-supported ecosystem of apps & games, so I decided to publish my game as free with integrated ads.
My experience with publishing my app into the marketplace was relatively good but a little frustrating. The submission process is reasonably well-documented and the requirements can be evaluated before you submit anything. The submission turns into a black box once it enters certification, and you won’t know if it passes or fails for a few days. Assuming certification goes smoothly (which it did in my case), the app should be ready to “publish” immediately, but the actual publishing process will take a couple more days to propagate to marketplace visibility. This is another reason the “New” list in the marketplace is worthless – it appears that the apps are propagated in batches or possibly at random, and so your app may debut on the “New” list several pages below the top.
Using XNA makes the choice of ad network pretty much a no-brainer: Microsoft offers an ad control linked with PubCenter which is bundled in the WP7 API. Google offers ads on WP7, but only for Silverlight apps. Integrating the PubCenter ad control is relatively easy, you need only create a couple of objects, set up an account on the website to manage your ad units, and plug in the appropriate ID values in your app code. Unfortunately, your ad network of choice may not always have an ad to display, which leaves a large hole in your UI layout. As a result, most larger apps choose to integrate a secondary ad network or even a whole slew of alternatives and fallback options, and I chose to use AdDuplex as my fallback. AdDuplex acts as an app-ad exchange, where you show ads for other apps on the network inside your own app, and in return you are able to create an ad for your own app which will be shown inside other apps. In my experience, AdDuplex also offers additional value in the form of analytics over PubCenter, since it displays metrics for “active installs”.
Making money on ads is the intent, but it’s a somewhat chaotic return on investment. Selection of ad categories, behavior of your customers when interacting with the ads, and the turbulence of ad buy-ins all impact the daily revenue you might see. In addition, if the PubCenter ad control fails to deliver an ad then it will not automatically try again during that session, so you’ll be stuck with whatever fallback you have; in my case, AdDuplex provides no revenue but should at least garner mind-share and additional downloads via its own ad placements, which should bolster future ad monetization. Ad performance is typically measured in eCPM (effective cost per 1000 impressions) which is then multiplied with your total impressions for an estimated revenue amount; in reality each impression may bear a different value which will be summed independently for a final revenue value. The eCPM values I’ve observed in my game have varied from $0.01 during development, up to $5.00 at initial launch, down to $0.90 after a week, and then fluctuating around $1.50 for a while. Typical daily revenue is about $10-20, although I’ve seen it break $30 on weekends. My total ad revenue since launch (over a month ago) so far is about $570. The App Hub details page tells me I’ve had 37,000 downloads, although I’m not sure if it counts update deployments as separate downloads, and I’ve published 2 updates since release.
After release, I was quite disappointed with the amount of detail available in the data provided by App Hub and Pub Center. The App Hub download numbers are significantly delayed (perhaps as much as a week), and PubCenter only displays total impressions and eCPM but doesn’t display interesting information such as failed ad-load attempts. As a result, I chose to implement a telemetry system in an update for my game, which records interesting data points during each gameplay session and sends them to a server for database storage. My implementation sends the data during the app shut-down event, and uses an HTTP web request with the URL query string encoded with name-value pairs of the data points; typically this string is relatively short, about 200 characters; the app code doesn’t care whether the HTTP response is received, simply hitting the web server with the URL is sufficient to record the data. I record information such as anonymous unique user ID, locale of the device, hardware model number of the device, ads successfully loaded versus failed, which ad unit is being used (since I randomly select between 2 for experimentation), how long the gameplay session was, number of games started, number of games finished, unlock progression, etc. All this information is aggregated in a database and allows me to analyze the performance of my ad system and the behaviors of players, and potentially respond with gameplay balance fixes or other design changes. So far I haven’t implemented anything significant based on telemetry data, but the value is clear and I plan to integrate a similar degree of telemetry into all future apps if possible.
I would say that working on this project was a worthwhile investment of time, although I have yet to see a reasonable return on investment since I probably spent about 40 hours doing the work of porting the game. The return would be downright depressing if I’d written this game from scratch in much more time. Admittedly, this particular game has a relatively low polish level and targets a fairly niche audience rather than the typical casual gamer you might expect to find on the phone platform, but that may actually be a benefit for the gamer who wants a less casual experience. Learning the nuances of the WP7 marketplace and ad-support performance was educational and may influence my development decisions in the future.