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 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.
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.
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.
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.
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.
texture.width == 320 (320 / 1)texture.width == 320 (640 / 2)Now we have all the tools we need!
contentScaleFactorI 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!
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.
Here 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.
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!
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.
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.
Let's say you want to support all iOS devices; then you'd have the following stage sizes:
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.
* Note: This was written prior to the newest generation of iPads, with a resolution of 1536×2048. You should use 768×1024 to achieve a scaling factor of 2 if you plan to support the newer iPad resolutions.
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;
With that prepared, all that's left to do is move the objects of your game according to the stage size, e.g. 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 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:
The difficult part is choosing the right stage size and assets. With the three resolutions mentioned above, it could work like that:
| 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 reasonably 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