Multi-Resolution Development

When you are creating a game for the browser, it is normally displayed as part of a bigger web page, so you can create it in a fixed resolution. On most mobile devices, however, this is not possible: the game may run on devices with all kinds of different screen resolutions, and it will always fill the complete screen. How can we develop such a game with Starling?

The Starling download package contains a scaffold project for mobile devices. It was optimized for the resolutions of the iPhone screens (since that is the most popular gaming platform), but runs perfectly fine on iPad and Android, as well. The article below explains the theory behind this scaffold, as well as alternative approaches you can use.

iPhone

The iPhone is still the most popular mobile platform for casual games. Since the resolutions of the classic and retina versions are so similar, it's also the easiest platform to develop for. That's why we'll start with this platform.

iPhone models may have one of two display resolutions: the older models (up to 3GS) come with a 320×480 screen, the newer models with a “Retina” display of 640×960 pixels, which is exactly twice the lower resolution.

The iPhone 5 introduced a new aspect ratio, making everything a little more complicated. Let's look at the old models first.

We're beginning with the low resolution device. Your stage size will be the same as the Starling viewport: 320×480. That's nothing extraordinary: it works just as if you'd create a browser game with the same plugin size.

starling = new Starling(Game, stage, new Rectangle(0, 0, 320, 480));

So far, so easy. Now let's continue with the retina display.

iPhone Retina

Upscaling

The easiest solution would be to just scale the game up to the full resolution. That's simple when you remember that you can set Starling's viewPort and stageWidth/stageHeight properties independently from each other.

  • The viewPort decides into which area of the screen Starling renders into. It is always specified in pixels.
  • The stage size decides the size of the coordinate system that is displayed in that viewPort. When your stage width is 320, any object with an x value between 0 and 320 will be within the stage, no matter the size of the viewPort.

With that knowledge, upscaling is trivial:

var screenWidth:int  = stage.fullScreenWidth;
var screenHeight:int = stage.fullScreenHeight;
var viewPort:Rectangle = new Rectangle(0, 0, screenWidth, screenHeight)
 
starling = new Starling(Game, stage, viewPort);
starling.stage.stageWidth  = 320;
starling.stage.stageHeight = 480;

Note that the viewPort is dynamic, depending on the device the game is started on, while the stage size is hard-coded to 320×480. If you start up your game now, it looks like that:

A little, blurry, right? That's because the textures we used were created with the low resolution in mind.

HD textures

The solution for that problem is to provide special textures for the high resolution. Depending on the screen size, we will use either the low- or high-resolution texture set.

There's only one problem, though: bigger textures will return bigger values for “width” and “height”. Remember, your stage always has a width of 320 (points). In such a stage, a texture with a width of 160 pixels will fill half of the screen. A corresponding HD texture would have a width of 320 pixels, and would thus fill the complete stage. But we want it to take up the same space as the SD texture!

That's where Starling's contentScaleFactor comes in handy. With the setup shown above, print out Starling's contentScaleFactor:

trace(starling.contentScaleFactor); // -> 2

The contentScaleFactor returns the viewPort width divided by the stage width. On a retina device, it will be 2, on a non-retina device, it will be one.

To make this really useful, textures should have a scale factor, too. And that's just the way it is!

var scale:Number = starling.contentScaleFactor;
var texture:Texture = Texture.fromBitmap(bmp, true, false, scale);

If you create a texture with a scale factor like this, it will take it into account when you query its width or height.

  • SD Texture (width ⇒ 320 pixels, scale factor ⇒ 1): texture.width == 320 (320 / 1)
  • HD Texture (width ⇒ 640 pixels, scale factor ⇒ 2): texture.width == 320 (640 / 2)

Now we have all the tools we need!

  1. Create your game with view port and stage size set up like shown above
  2. Depending on the screen size, load either HD or SD textures
  3. Set the texture's scale factor to Starling's contentScaleFactor

I recommend you create one class that handles your game's assets. That class will save the scale factor and return the appropriate textures when you need them. (Look at the scaffold project for a sample of such a class.)

This is it: now we've got a crisp-looking retina game!

To create those textures in multiple resolutions, just start with the highest resolution you want to support and use a tool like TexturePacker to create your texture atlases in multiple sizes. Here's a tutorial that shows you how to use TexturePacker in combination with Starling.

Other resolutions

All in all, the tasks above were really easy, right? It was that easy because the iPhone's retina resolution is exactly twice the SD resolution. (Apple didn't choose those resolutions by accident, after all.)

It becomes a little more difficult when we want to support a device with a resolution that is not just a multiple of our SD resolution. One such device is the iPad, others are a plethora of Android phones.

Below are some strategies you can use when you want to support all kinds of display resolutions out there. It uses the iPad as sample device, but it works the same with any other device.

When you create a universal app for iOS, this cheat sheet with the resolution data for all iOS models comes in really handy: iOS Resolution Quick Reference

Strategy 1: Stretching the stage

Actually, this is not a strategy — it's just an example of what we don't want to do. Please don't go this route: it will produce really ugly games.

This shows what happens when you simply scale up your game, no matter the size of the screen. The distortion is already visible on the iPad, and it can get even worse on other devices. That's definitely not what we're looking for!

Strategy 2: Letterbox

There's a simple solution that looks much better: center the game on the screen and add a black frame around it. All you have to do is to modify the viewport:

starling = new Starling(Game, stage, new Rectangle(64, 32, 640, 960));

This centers the viewPort on the iPad's screen and gives it the exact same size (in pixels) as on the retina iPhone. That's all you have to do; the rest of the code stays the same.

With that solution, the game stays just as crisp and sharp as on the iPhone; furthermore, you are safe from any aliasing issues. The downside: you don't use the full screen any longer.

An alternative of this strategy is to “zoom” the viewPort up to the biggest size that is possible without cropping anything away.

The game becomes a little more blurry, but it's acceptable. It may also happen that you run into a few aliasing issues. Nevertheless: this is probably the most pragmatic solution. It allows your game to run in an acceptable quality on all available display resolutions, and you don't have to do any extra work other than setting the viewPort to the right size.

The latter can be done very easily with the “RectangleUtil.fit” method. To “zoom” your viewPort up, just create the Rectangle with this code:

var viewPort:Rectangle = RectangleUtil.fit(
    new Rectangle(0, 0, stageWidth, stageHeight), 
    new Rectangle(0, 0, stage.fullScreenWidth, stage.fullScreenHeight), 
    ScaleMode.SHOW_ALL);

The “Scaffold” project that is part of the Starling download package uses this strategy. When you use this project as the basis for your game, it will run on all available platforms, without you having to do anything special.

Strategy 3: Smart Object Placement

On the other hand, it would be nice if we could actually use the additional space. In many games, this is not as difficult as it sounds: we just have to align our user interface at the sides of the game area instead of hard-coding their positions in the 320×480 range.

In other words: you have to support different stage sizes.

iOS

Let's say you want to support all iOS devices up to iPhone 4S; then you'd have the following stage sizes:

  • 320×480 for iPhone devices
  • 384×512 for iPad devices

Where does that strange resolution for the iPad come from? Shouldn't it be 768×1024?

Remember, we want to have a contentScaleFactor of 2, because we need to display the HD textures, just like on the retina iPhone. That's why we use half of the iPad resolution for our stage. The viewPort, on the other hand, will stay at 768×1024.

On a retina iPad, this is taken even one step further: a resolution of 1536×2048 is exactly four times our desired stage size. So we will have a view port that's exactly the iPad resolution, but still a stage size of 384×512 — and a scale factor of 4.

starling = new Starling(Game, stage, new Rectangle(0, 0, screenWidth, screenHeight));
 
var isPad:Boolean = (screenWidth == 768 || screenWidth == 1536);
starling.stage.stageWidth  = isPad ? 384 : 320;
starling.stage.stageHeight = isPad ? 512 : 480;

To support the iPhone 5 with its changed aspect ratio, you also need to use this strategy. Work with a stage size of 568×320 and a content scale factor of 2.

With that prepared, all that's left to do is move the objects of your game according to the stage size, i.e. to align them at the sides instead of hard-coding their positions. Depending on the game, this may or may not be quite some work. The end result, though, will look perfect on all iOS devices.

Android

Android devices are a little more difficult to handle than their iOS counterparts, because Android phones feature lots of different screen resolutions. In principle, however, the technique will be the same as what you have learned above.

This means that a valid approach for multi-platform development would be the following:

  • Create your assets in two or three resolutions that are multiples of the lowest one:
    • e.g. 240×320 (LD), 480×640 (SD), 720×960 (HD)
  • The viewPort size will always be the full resolution of the screen, while
  • the stage size will be the screen size divided by a whole-number factor (1, 2, 3).
  • choose one set of those assets for a given resolution and place your objects according to the stage size — just as we did it above for the iPad.

The difficult part is choosing the right stage size and assets. With the three resolutions mentioned above, it could work like this:

Screen size Asset type Factor Stage size
240×320 LD 1 240×320
320×480 LD 1 320×480
480×640 SD 2 240×320
480×800 SD 2 240×400
640×960 SD 2 320×480
720×1280 HD 3 240×426
768×1024 HD 3 256×341

As you can see, these settings will give you a stage size between 240×320 and 320×480, with all those different screen sizes. This is a size delta that is reasonable enough to distribute your objects over the screen while keeping a consistent look and feel on all devices.

Be careful with the last two listed resolutions, though: they don't produce a whole number when divided by three. I recommend you set the viewPort to 720×1278 and 768×1023, respectively. This will leave a small black border at the side, small enough not to be noticed.

Granted, this approach is quite burdensome. It's advantage is that all graphics will look perfectly sharp on any device, and you will have only minimal aliasing problems.

I leave it up to you to decide if it's worth the effort — strategy 2 is definitely easier.


Next section: Constrained Stage3D Profile

  manual/multi-resolution_development.txt · Last modified: 2014/11/24 15:23 by daniel
 
Powered by DokuWiki