Thursday 28 January 2016

Porting Pixels

2016 GameJam #1 - PortCrunch


So on the weekend of the 15th of January 2015 - Dean and I worked on another game with a target of getting it working on Android, iOS, Windows, MacOS in one weekend.

Anyone who follows our antics will know that typically we try to create a game or at least a prototype in a weekend. Sometimes we attend organized game jams but always, we create something new ourselves.

Apart from this time.

This time it wasn't one of ours.



Introducing Pixel-Blocked! A sweet puzzle game by Daniel Truong (of Daniel Makes Games) which oozes innocence until you realize that underneath those cute and colorful pixels beats a sinister heart.

It's one of those game which has a novel concept, well thought out controls and a nice difficulty curve - easy enough to lure you in and difficult enough to keep you coming back.

It also has a set of awards and unlockables for those who relish mastering a game instead of simply playing it.


He's also made bunch of other games which can be found on iTunes.
https://itunes.apple.com/ca/developer/daniel-truong/id915310760


We figured it would take us a weekend, so we made a GameJam out of it.  We wanted to do a good job which did the original game justice and have fun with it. We'd have our work cut out for ourselves for sure.

This wouldn't be a gamejam, it would be a ...

#portcrunch

First steps

On Friday evening we created the project structure to get us going and played the Windows Phone version to see how much work would be ahead of us.

The game was originally built using XNA with C#, which meant a port to MonoGame would be pretty straight forward.

Because Xamarin and MonoGame work really well with shared code projects, it's reasonably easy to get a cross platform game up and running.

This meant that the majority of the porting work would be dealing with the differences in hardware, not operating system.

Obviously there are a few gotchas when porting, however for the most part, it's plain sailing.


Shared Code FTW

You have a common code project which contains the bulk of the game code and individual platform specific projects which contain the platform specific stuff - usually, this is very little, just the launcher and icon sets but it also has a content pipeline file.

Building the content pipeline.

The MonoGame content pipeline allows you to compile your content for specific platform and place them into the project. They allow for platform specific conversion to be done automatically so there's less to worry about as you add content to your game and make changes. It handles audio, graphics, shaders, fonts... and a lot more, making sure the content is properly compiled and put into the right place.

Shaders 

Pixel-Blocked! uses shaders for some things, so it was important to get these compiled for the correct platform. Once again, the content pipeline to the rescue. By making a few changes to the base shader code, they could be compiled for any of the supported GPUs.

There are some gotchas with shaders which  you may need to be aware of. More on that later.

Nothing is perfect...

While porting the shaders, we found a bug in most recent Stable MonoGame - Dean has since fixed it so it should be available in a short time for everyone. It's handy having someone who's really hands on with the MonoGame code-base.

Screen Resolutions.

The game was written to support a fixed resolution. After-all, the graphics are intentionally blocky so scaling is not going to be a problem, however getting the game to look good on different screen resolutions is always painful. Coupled with the fact that the came adjusts itself for Portrait of Landscape depending on how you're holding the phone... multiply those problems.

Render Target or Scaled co-ordinates?

The initial desktop version of the game drew its graphics to a render target which could be scaled. A sensible approach and one used by many developers.

This is a great way to achieve a constant look to your game - also an approach we've taken with one of our unreleased games; unfortunately we encountered problems with the Amazon Fire device with Pixel-Blocked! so we had to change it and adopt a more traditional maths based co-ordinate scaling technique.

There are pros and cons with any scaling technique when dealing with different screen sizes.
You see the problem is of aspect ratio. You can't simply fill the screen or things start to look weird.

Aspect Ratio?

For those who don't know what that is, it's the ratio between the width and the height of the screen.
Most computer screens are based on a 4:3 ratio, so 640x480, 800x600, 1024x768. This ratio has been used by PC games for a very long time. Then came the wider screen formats 16:9 which are used for most TVs 845x480, 1280x720, 1920x1080. These have started to gain a lot of popularity with PC games as people opt for the wider screen.

That's simple right?

Well, not so much - you see the phone and tablet ecosystem provides a rich and varied set of resolutions which are great for the customer giving them a lot of choice; however it's a nightmare for the developer where you have to make sure your game or app looks nice on all of them.

So you have 2 choices. Use a render target and scale to fit but keep the aspect ratio and letterbox any gaps, or manually position your elements based on some scaling factors.

It's a minefield without an easy route through. But with persistence, you'll make it. Just choose which will work best for your game.

The Windows phone version we ported used a scaled co-ordinate system, so we continued along the same lines.

As the day progressed, 

things were coming together nicely. The game ran on Android albeit with controls too small on the larger resolution devices and with a couple of issues with screen rotation. However for the most part, things were working.

Dean had a little trouble on iOS and made a change which broke the renderer... so I killed him.

Just kidding.. we've been friends for 20 years, he's not that easy to kill.

We worked through the day and most of the evening to get the game in a working state and to make sure the input systems were working correctly.

We also had some issues with screen rotation. For some reason when the orientation was switched from Landscape to Portrait and vice versa, the viewport dimensions were not reflected.

It appears that at the time of writing, this is a bug in MonoGame.
Given that MonoGame is open source, we'll probably fix it and submit a patch.

The next day

Dean re-broke the iOS build, then fixed it, the broke it again and then fixed it for good.

We had an issue which only manifested on iOS where the render state was being corrupted. So Dean went bug hunting.

Found it.

Squashed it.

Apparently calling GetData on a texture is a bad idea. Reading data back from texture memory is slow and we found that it caused corruption of the render state. Dean has promised to write an article about this shortly. When he does, I'll put a link in here.


I spent the majority of the day slogging through the screens to scale each element..
Making sure that text was scaled properly and that things lined up no matter what the resolution was.
Some screens were harder than others, a couple were quite tricky given the complexity of the UI they provided. But in the end, the screens looked good on pretty much everything.

Dean spent a little time speeding up the loading time on Android devices.
Sometimes it's OK to load everything up front however when there are lots of graphics, some of which require additional processing - not all devices were able to handle this in a timely manner.

Testing


Introducing our glamorous testing team.

Seulki, Jenny and Katrina - Our glamorous testers.



The girls were given all of the tools they needed:

Wine,
iPads,
Galaxy Tabs
and various Phones...
more wine...

They were tasked with playing the game on all of the devices to make sure everything worked. They should pay attention to the closest details and report any crashes.
Everything was going fine until things started getting competitive. It seems that they were determined to beat each other's score.

So following 2 days of solid porting, it was really good to see the game being enjoyed by the testing team.

The wine probably helped a little, but the game is a lot of fun.


There are a ridiculous amount of Android devices in existence. One of our tasks was to test the game on as many devices as we could. Although we don't have access to the entire spectrum of devices, we do have a set which covers most bases.

One of the great things about developing for the iPad and iPhone is that you're sure that the game will run on all devices (until they change the OS, but that's a post for another day)

Incidentally, Xamarin offers a service called Xamarin Test Cloud to run your app or game on a large array of devices and monitor the results. It's certainly worth considering.


Finishing up

It took a little bit longer than we anticipated - these were mostly down to strange glitches in MonoGame which we had to address, as well as strange differences in behaviour (Amazon Kindle handles rotation differently to any other devices)

But in the end, we have a working game which runs really nicely on all the platforms we've thrown at it.