Last week, Survival Run with Bear Grylls was released on the Windows Phone marketplace. While we're all extremely happy to finally bring the game to new players on a different platform, it took a lot of work to get it into a releasable state. This post serves as a brain dump for what we did in order to get the game onto the marketplace.
- Windows 8 OS
- Unity 4 License
- WP8 Testing Devices
- Visual Studio
We (sadly) upgraded one of our stations from Windows 7 to Windows 8. Visual Studio was also installed onto the Windows 8 machine. It's needed to build the final .xap file for the marketplace, and also serves as a good IDE for writing WP8 plugins for Unity. We also put one of our Unity 4 Pro licenses onto it and cloned out the Survival Run project on the Asset Server. While the iOS & Android builds of Survival Run share the same codebase, using preprocessors for platform specific functionality, it was decided that the Windows build would have its own codebase. This is primarily because we had to upgrade the project from Unity 3.5.7 to Unity 4.3 but also because we had to modify much of the underlying framework in order to provide a good experience on the Windows platform. For testing purposes, we bought a Lumia 520. It's not only on the low end in terms of performance but it also dominates the Unity hardware charts (http://stats.unity3d.com/mobile/device-wp.html).
With our Windows 8 station setup, the project was ready to be worked on. The first thing to handle was the Unity 3 to Unity 4 upgrade. We used EZGUI for the menu system in Survival Run, and after upgrading the project we noticed that there were a lot of weird input bugs happening. Our version of EZGUI had preprocessors to distinguish between Unity 3 and older versions of Unity. Since Unity 4 didn't fall into any of the Unity 3 preprocessors, it was being built using old and deprecated code for various things such as touch input. Instead of upgrading to the latest version of EZGUI and potentially spending lots of time updating our code base to match any changes within the EZGUI system, we simply replaced Unity 3 preprocessors with Unity 4 preprocessors. Definitely not pretty or recommended, but it was quick, efficient, and we had all input back to normal in very little time. Outside of EZGUI, there wasn't much else we had to handle in order to get the game up and running in Unity 4. There were a few small setActive() bugs but those were squashed relatively easily.
Having solved our Unity 4 problems. We pushed a debug build onto our Lumia 520. After a load time of 100+ seconds we finally saw the title screen. A few seconds later- crash. But hey, at least the game loaded on our first try! It's all about small victories. So the first problem to take care of was the crash. We assumed it was a memory problem but in order to confirm, we did some performance debugging in Visual Studio. With a default Unity project, the memory profiler in VS isn't available. However you can change that by following this post. A minor note: replace 'release' in that post with '$(Configuration)', otherwise the 'Development Build' text will always appear at the bottom of your app- even if you publish a Master build.
Using the memory profiler, we were able to confirm why the game was crashing: our background loader that loads the games environments was eating up too much memory. The loader loads all of the environments into memory and keeps them there for the duration of the games lifespan. However, the Lumia 520 couldn't handle all of that without crashing. In order to fix this, the loader had to be modified to only keep a maximum of 3 environments loaded at a time (the forest, which is our starting area, the current environment the player is running in, and the next environment that is being loaded). With the new loader implemented, the player could run through all environments and the game would not crash due to memory constraints. If the new loader failed to work, we also had the option to disable low end devices from downloading our game. However since the Lumia 520 seems to be an extremely popular phone according to Unity's hardware statistics, it seemed like a much better idea to try and make the game compatible with it. Note: Make sure to enable ID_FUNCCAP_EXTEND_MEM in your manifest if you're running into memory issues on lower end devices. If you want to dis-include phones with low memory, set ID_REQ_MEMORY_300. Reference.
Load Time Problems
As I mentioned above, the first time we built the game, it took over 100 seconds to load. With a master build, that time was cut down to 70. In order to pass certification, the app must be responsive to user input within 20 seconds. It also must render a splash screen within the first 5 seconds of launching the app.
The Secondary Splash Screen
We tackled the easy problem first- displaying a splash screen within the first 5 seconds. I think the latest version of Unity will generate a secondary splash screen for you already but if not they have a step-by-step guide on how to do so. The benefit to this secondary splash is the ability to show a progress indicator so that the user knows the game is loading. Unfortunately, we failed to have any success with getting a progress indicator to smoothly display. As far as I'm aware, the progress bar has no way of knowing the actual percent complete of the games load. The guide recommends updating the progress bar using a timer and having the bar hit 100% at the longest time you think it would take your app to load on a device. This seems a bit misleading to players but luckily there's a way to mark your progress bar as 'indeterminate' which changes the bar into an animated series of dots. While we thought this would be the best solution, it turns out that when loading the game, the dots fail to animate smoothly and instead seem to pause until the load is complete. No matter what we tried we couldn't fix this, and we also noted this problem existed in other Unity games already out on the marketplace. For now, we left the indeterminate loader in since it seemed like a better solution than the fake progress bar.
Reducing load times by 350%
So, with that 'simple' problem out of the way, we next had to get our load time from 70 seconds down to 20. Easy, right? The first thing we did was use Unity's built-in profiler to see what scripts were taking the longest time to execute. We actually noticed that script execution started fairly early in the load process, but everything that was initializing on frame 1 was what was killing our load time. One of the big things we discovered was that our localization scripts were eating up a lot of time. These scripts updated all our SpriteText and even swapped out font textures depending on the games language. With the WP8 build, we had no plans to localize for individual regions and so we stripped out the localization code. This instantly had a huge effect and brought our load time down by quite a bit. We also made several small changes to how and when EZGUI SpriteText initialization code is run in order to offload some of the execution time from frame 1. After that we spent a lot of time using the profiler to see which scripts were taking up the most execution time on frame 1 and figuring out how to cut them down.
It was a lot of tiny battles and little victories. Shaving small seconds off here and there. Eventually we got it down to 30 seconds and that seemed like it was as good as it was going to get. Since certification requires 20 seconds, we only had one option left- put the games store into a separate scene. Our store screen is composed of lots of items, each with numerous game objects, sprite texts, and more. By choosing to put the store in a separate scene we were able to get the load time under 20 seconds. When the player first clicks the store, a loading message appears while it's loaded into the background. After it's been loaded once, it's quickly accessible for the rest of gameplay. Honestly this really sucks and it was disappointing that we had to resort to a load screen for the store, but there was nothing else to be done. I'd rather have a load screen than have to dis-include potential players who own a low end Windows Phone. Our final load time on the Lumia 520 clocks in at around 17.5 seconds- not too bad, all things considered.
With the game no longer crashing, and the load time under 20 seconds, a new problem appeared. The frame rate was constantly dipping and causing input problems. There's nothing more frustrating than not having a gesture register and falling to your doom during a high score run. We made some minor adjustments here and there to improve things, but the biggest change was removing the lightmaps. This let the game run at a solid 30 FPS on low end devices and allowed for less dropped input. In the future, we'd like to do some device detection in order to provide lightmaps for higher end devices, similar to what we do on iOS, however currently we have lightmaps disabled for all Windows Phones.
In addition to the lack of lightmaps, the game currently lacks fog. This causes for some ugliness, especially in the gullies and during base jumps. We investigated several options for implementing fog (as have other Unity community members). However none of them allowed us the performance we needed. Unity 4.5 is supposed to bring improvements to WP8 development as well as fog implementation, so hopefully we will be able to add it into the game in the near future.
Because we're terrible people, at the last minute we finally turned on the sound to experience the game in all it's glory. What we got was a stuttery, laggy mess of music and delayed sound effects. Luckily, building with the 'Master' configuration fixed all that. It's all important to note that if you updated your VS project in order to use the memory profile, you have to make sure those lines are using $(Configuration) instead of 'release' or else the stuttering music problem will persist.
Using Visual Studio, we built 2 separate plugins for Survival Run. The first was for in-app purchasing using Fortumo, a system that allows players to use carrier billing to pay for in-app purchases. The other plugin was for the standard Windows IAP system, using the Wallet. After writing numerous Android plugins in Unity 3, I actually liked the Windows implementation of plugins. Since the plugins are written in C#, it's much easier to communicate back and forth between Unity and your plugin. All variable types are the same and you don't have to muck about with SendMessage(), you can directly call a method and have it return a value.
Plugins in the Editor
One of the minor nuisances of writing Windows plugins for Unity, is that they throw compile errors while in the Editor. In order to get around this, you have to build two versions of your plugin. The first version is the actual plugin, a .dll that gets placed into the Plugins/WP8 folder of your project. This is what will be included in the project when you build it to Visual Studio. The second version is essentially a template of your plugin- it has all the same methods, only they are empty or return default values. This is the editor plugin. It's placed in the Plugins/ folder and is what will be run when you play the game inside the Editor. It's not a terrible setup, and it's nice that you can run test cases in the Editor by modifying your editor plugin, however I can imagine it being a bit tedious to deal with when working with larger plugins. The ones that I wrote only required a few methods.
App Thread vs. UI Thread
The other thing to consider when writing plugins is what thread each method gets called on. All Unity methods should be run on the App thread. Any plugin methods that invoke a dialogue, display something, or modify the UI must be called on the UI thread. Calling a method on the wrong thread will usually result in your app crashing. Dealing with multiple threads might seem intimidating but the porting guide provided by Unity shows how to create a Dispatcher class that lets you call your methods on either the App or UI thread.
In-App Purchase Testing
In order to test IAPs with Windows system, Microsoft provides you with 3 options
- Create a Beta build of your app and put it in the store
- Add a mock in-app purchase library to your project
- Setup your own mock in-app purchase system using IIS
Out of these 3 options, we decided the best route to go would be to create a beta build of our app. The beta build seemed to be the closest to an actual release build and would provide us with (hopefully) similar response and processing times as the actual thing. There were however a few downsides to this option. First, you can't upload a 'beta' .xap under your app in the dev center. Instead you must create an entirely new app in the dev center and mark it as beta. This also means that every IAP in your app must be created twice, once for the beta build and once for the real build. Furthermore, IAP product IDs must be unique in the dev center, even cross app. So you must either delete all your beta IAPs when you are done with them and recreate them as real IAPs, or give your beta IAPs a different product ID. We decided to append '.beta' to the end of all our product IDs when testing.
Once you've created your beta app and beta IAPs, you must upload your .xap to the store. It will then take several hours for that .xap to go live. Once live, whichever Microsoft accounts you have approved for the beta, can follow a link to go and download the Beta build.
Submitting the final build to the marketplace was an interesting experience. You don't create a application id, or sign the application using a keystore or developer certificate. Instead, you upload your .xap and the dev center will automatically change your publisher id and product id for you. You also must specify your version number after uploading the .xap, as the version number you set in VS will be overriden.
It's also interesting to note that Microsoft asks for developers to complete multiple rating systems in order to sell your app in different countries. Android and Apple both have one rating system, however for WP8, we filled out an ESRB rating, a PEGI rating, and a few other lesser known rating systems in order to have our game distributed in as many places as possible.
Finally, make sure you upload a .xap that was built with the 'Master' configuration. Some users on the Unity forums seem confused since Microsoft documentation ask that you upload a 'release' version of the game. This doesn't mean that you must publish with the 'Release' configuration in VS. It simply means that you must upload a build that has all debug stripped out and is ready for your player base. Publishing with the Master configuration will strip out all debug symbols and provide the best performance for your Unity game.
Once you've uploaded a .xap, the dev center claims it will take 5-7 days for your app to be approved. I'm not sure how accurate that is or if we just got luckily, but we were approved within 1 day, however it took another 24 hours for the game to actually appear on the marketplace.
Overall, it was an interesting experience porting Survival Run to Windows. The game is now available for a new audience and we learned a lot about the process of porting Unity games to WP8. It took longer than I initially expected but with everything we now know, future projects should be much quicker to port. I hope this rambling post helps some people with their ports to WP8. Mainly I wanted to write this all down so that I don't forget everything we did for this port- but if it helps even one person, then cool.
Disclaimer: This was written based on what I remember while working on the project. Please correct me or ask questions if anything I stated seems incorrect. I'm on twitter! @matthewvroman