Managing multiple iOS resolutions with Starling – real world example – Part 2: iPhone 5 and iPad

One of the most interesting thing you can do with Starling is to target all kind of resolutions in a single project. In the first step of this tutorial I showed you the basic principles of this method publishing for retina and non-retina iPhones, using high resolution textures on retina devices and low resolution textures on non-retina iPhones.

Now it’s time to complete the tutorial showing you how to create full screen iPhone 5 Apps as well as iPad, iPad mini, and Retina iPad Apps, every device with its own resolution and with proper fullscreen support.

This is the final result of using this technique on the upcoming Circle Chain sequel, you should already have seen this photo if you are one of my Facebook fans:

You can see an iPhone 3GS, an iPhone 4, an iPhone 5, an iPad and an iPad 3 all with their proper textures, working in fullscreen – even the “I have a weird resolution” iPhone 5.

Know your enemy

Before we start, we have to know all iOS resolutions, which are, in landscape mode:

iPhone 3G and 3GS: 480×320

iPhone 4 and iPhone 4S: 960×640

iPhone 5: 1136×640

iPad, iPad 2 and iPad mini: 1024×768

iPad 3 and iPad 4 (called iPad with retina display): 2048×1536

Working with full screen height and width will mean to work with different stage sizes: so the first thing is to define such stage sizes. You can simplify the whole process this way:

480×320: iPhone 3G, iPhone 3GS, iPhone 4 (with textures rendered at twice the resolution) and iPhone 4GS (with textures rendered at twice the resolution).

568×320: iPhone 5 (with textures rendered at twice the resolution).

512×384: iPad (with textures rendered at twice the resolution), iPad 2 (with textures rendered at twice the resolution), iPad mini (with textures rendered at twice the resolution), iPad 3 (with textures rendered at four times the resolution) and iPad 4 (with textures rendered at four times the resolution).

This way we grouped 10 devices with 5 different resolutions in just three stage sizes.

This is the main class, look how I am setting Starling view port to full screen width and height while keeping stage width and height in a lower resolution:

package {

	import flash.display.Sprite;
	import starling.core.Starling;
	import flash.geom.Rectangle;

	public class Main extends Sprite {
		private var _starling:Starling;
		public function Main() {
			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);
			switch (screenWidth) {
				case 1136 :
					_starling.stage.stageWidth=568;
					_starling.stage.stageHeight=320;
					break;
				case 1024 :
				case 2048 :
					_starling.stage.stageWidth=512;
					_starling.stage.stageHeight=384;
					break;
				default :
					_starling.stage.stageWidth=480;
					_starling.stage.stageHeight=320;
			}
			_starling.showStats=true;
			_starling.start();
		}
	}
}

Now, Game class should include all images in all different resolutions, loading the proper textures according to device type.

package {

	import starling.display.Sprite;
	import starling.display.Image;
	import starling.textures.Texture;
	import starling.animation.Tween;
	import starling.core.Starling;
	import starling.display.Button;

	public class Game extends Sprite {

		[Embed(source="assets/mainbg.png")]
		private static const MainBG:Class;

		[Embed(source="assets/mainbg_hd.png")]
		private static const MainBGHD:Class;

		[Embed(source="assets/mainbg_hd5.png")]
		private static const MainBGHD5:Class;

		[Embed(source="assets/mainbg_ipad.png")]
		private static const MainBGIPad:Class;

		[Embed(source="assets/mainbg_ipadhd.png")]
		private static const MainBGIPadHD:Class;

		[Embed(source="assets/gametitle.png")]
		private static const GameTitle:Class;

		[Embed(source="assets/gametitle_hd.png")]
		private static const GameTitleHD:Class;
		
		[Embed(source="assets/gametitle_hd4.png")]
		private static const GameTitleHD4:Class;

		[Embed(source="assets/gridedition.png")]
		private static const GridEdition:Class;

		[Embed(source="assets/gridedition_hd.png")]
		private static const GridEditionHD:Class;
		
		[Embed(source="assets/gridedition_hd4.png")]
		private static const GridEditionHD4:Class;

		[Embed(source="assets/normalmode.png")]
		private static const NormalMode:Class;

		[Embed(source="assets/normalmode_hd.png")]
		private static const NormalModeHD:Class;
		
		[Embed(source="assets/normalmode_hd4.png")]
		private static const NormalModeHD4:Class;

		[Embed(source="assets/timeattackmode.png")]
		private static const TimeAttackMode:Class;

		[Embed(source="assets/timeattackmode_hd.png")]
		private static const TimeAttackModeHD:Class;
		
		[Embed(source="assets/timeattackmode_hd4.png")]
		private static const TimeAttackModeHD4:Class;

		private const IPHONE3:Number=1;
		private const IPHONE4:Number=2;
		private const IPHONE5:Number=3;
		private const IPAD:Number=4;
		private const IPAD3:Number=5;
		private var deviceType:Number;
		private var centerOffset:Number;

		private var splashSprite:Sprite;

		// buttons
		private var normalModeButton:Button;
		private var timeAttackModeButton:Button;

		// textures
		private var backgroundTexture:Texture;
		private var gameTitleTexture:Texture;
		private var gridEditionTexture:Texture;
		private var normalModeTexture:Texture;
		private var timeAttackModeTexture:Texture;

		public function Game() {
			setupDevice();
			addBackground();
			showGameTitle();
		}

		private function setupDevice():void {
			switch (Starling.current.viewPort.width) {
				case 480 :
					centerOffset=0;
					deviceType=IPHONE3;
					break;
				case 960 :
					centerOffset=0;
					deviceType=IPHONE4;
					break;
				case 1024 :
					centerOffset=16;
					deviceType=IPAD;
					break;
				case 1136 :
					centerOffset=44;
					deviceType=IPHONE5;
					break;
				case 2048 :
					centerOffset=16;
					deviceType=IPAD3;
			}
		}

		private function addBackground():void {
			switch (deviceType) {
				case IPHONE3 :
					backgroundTexture=Texture.fromBitmap(new MainBG());
					break;
				case IPHONE4 :
					backgroundTexture=Texture.fromBitmap(new MainBGHD(),true,false,2);
					break;
				case IPHONE5 :
					backgroundTexture=Texture.fromBitmap(new MainBGHD5(),true,false,2);
					break;
				case IPAD :
					backgroundTexture=Texture.fromBitmap(new MainBGIPad(),true,false,2);
					break;
				case IPAD3 :
					backgroundTexture=Texture.fromBitmap(new MainBGIPadHD(),true,false,4);
					break;
			}
			var backgroundImage:Image=new Image(backgroundTexture);
			addChild(backgroundImage);
		}

		/*
		
		*****************
		* SPLASH SCREEN *    
		*****************
		
		*/

		private function showGameTitle():void {
			splashSprite=new Sprite();
			addChild(splashSprite);
			showCircleChain();
		}

		private function showCircleChain():void {
			switch (deviceType) {
				case IPHONE3 :
					gameTitleTexture=Texture.fromBitmap(new GameTitle());
					break;
				case IPHONE4 :
				case IPHONE5 :
				case IPAD :
					gameTitleTexture=Texture.fromBitmap(new GameTitleHD(),true,false,2);
					break;
				case IPAD3 :
					gameTitleTexture=Texture.fromBitmap(new GameTitleHD4(),true,false,4);
					break;
			} 
			var gameTitleImage:Image=new Image(gameTitleTexture);
			splashSprite.addChild(gameTitleImage);
			gameTitleImage.x=111+centerOffset;
			gameTitleImage.y=-100;
			var gameTitleTween:Tween=new Tween(gameTitleImage,0.7);
			gameTitleTween.moveTo(111+centerOffset,50);
			gameTitleTween.delay=1;
			gameTitleTween.onComplete=showGridEdition;
			Starling.juggler.add(gameTitleTween);
		}

		private function showGridEdition():void {
			switch (deviceType) {
				case IPHONE3 :
					gridEditionTexture=Texture.fromBitmap(new GridEdition());
					break;
				case IPHONE4 :
				case IPHONE5 :
				case IPAD :
					gridEditionTexture=Texture.fromBitmap(new GridEditionHD(),true,false,2);
					break;
				case IPAD3 :
					gridEditionTexture=Texture.fromBitmap(new GridEditionHD4(),true,false,4);
					break;
			} 
			var gridEditionImage:Image=new Image(gridEditionTexture);
			splashSprite.addChild(gridEditionImage);
			gridEditionImage.y=90;
			gridEditionImage.x=481+centerOffset;
			var gridEditionTween:Tween=new Tween(gridEditionImage,0.7);
			gridEditionTween.moveTo(276+centerOffset,90);
			gridEditionTween.onComplete=showButtons;
			Starling.juggler.add(gridEditionTween);
		}

		private function showButtons():void {
			switch (deviceType) {
				case IPHONE3 :
					normalModeTexture=Texture.fromBitmap(new NormalMode());
					timeAttackModeTexture=Texture.fromBitmap(new TimeAttackMode());
					break;
				case IPHONE4 :
				case IPHONE5 :
				case IPAD :
					normalModeTexture=Texture.fromBitmap(new NormalModeHD(),true,false,2);
					timeAttackModeTexture=Texture.fromBitmap(new TimeAttackModeHD(),true,false,2);
					break;
				case IPAD3 :
					normalModeTexture=Texture.fromBitmap(new NormalModeHD4(),true,false,4);
					timeAttackModeTexture=Texture.fromBitmap(new TimeAttackModeHD4(),true,false,4);
					break;
			} 
			normalModeButton=new Button(normalModeTexture);
			splashSprite.addChild(normalModeButton);
			normalModeButton.x=186+centerOffset;
			normalModeButton.y=170;
			normalModeButton.scaleWhenDown=0.95;
			normalModeButton.alpha=0;
			var normalModeTween:Tween=new Tween(normalModeButton,0.7);
			normalModeTween.fadeTo(1);
			Starling.juggler.add(normalModeTween);
			timeAttackModeButton=new Button(timeAttackModeTexture);
			splashSprite.addChild(timeAttackModeButton);
			timeAttackModeButton.x=167+centerOffset;
			timeAttackModeButton.y=230;
			timeAttackModeButton.scaleWhenDown=0.95;
			timeAttackModeButton.alpha=0;
			var timeAttackModeTween:Tween=new Tween(timeAttackModeButton,0.7);
			timeAttackModeTween.delay=0.2;
			timeAttackModeTween.fadeTo(1);
			Starling.juggler.add(timeAttackModeTween);
		}
	}
}

You can also see how I am placing the assets in different positions according to device resolution. setupDevice function (lines 90-112) tells us which device we are dealing with, then the whole function is not that different than the one I showed you in the first step of this tutorial and everything becomes something like making an HTML page with a fluid layout. You shouldn’t work with absolute, hard-coded assets placement, but rather rethink the layout according to the current screen size.

iPhone 5 fullscreen bug

Well, I don’t really know if it’s a bug for real, but actually this code won’t work on iPhone 5 because its resolution won’t be recognized, unless you include its App launch image.

It’s basically a 640×1136 png image (in portrait mode even if you are working on landscape mode) you will call Default-568h@2x.png and include in your application files.

And finally you will be able to have your App running in full screen on ALL iOS devices.

Next time I will show you how to load textures dynamically rather than embedding all of them (even unnecessary ones) at once, and will release the source code.

Get the most popular Phaser 3 book

Through 202 pages, 32 source code examples and an Android Studio project you will learn how to build cross platform HTML5 games and create a complete game along the way.

Get the book

215 GAME PROTOTYPES EXPLAINED WITH SOURCE CODE
// 1+2=3
// 100 rounds
// 10000000
// 2 Cars
// 2048
// A Blocky Christmas
// A Jumping Block
// A Life of Logic
// Angry Birds
// Angry Birds Space
// Artillery
// Astro-PANIC!
// Avoider
// Back to Square One
// Ball Game
// Ball vs Ball
// Ball: Revamped
// Balloon Invasion
// BallPusher
// Ballz
// Bar Balance
// Bejeweled
// Biggification
// Block it
// Blockage
// Bloons
// Boids
// Bombuzal
// Boom Dots
// Bouncing Ball
// Bouncing Ball 2
// Bouncy Light
// BoxHead
// Breakout
// Bricks
// Bubble Chaos
// Bubbles 2
// Card Game
// Castle Ramble
// Chronotron
// Circle Chain
// Circle Path
// Circle Race
// Circular endless runner
// Cirplosion
// CLOCKS - The Game
// Color Hit
// Color Jump
// ColorFill
// Columns
// Concentration
// Crossy Road
// Crush the Castle
// Cube Jump
// CubesOut
// Dash N Blast
// Dashy Panda
// Deflection
// Diamond Digger Saga
// Don't touch the spikes
// Dots
// Down The Mountain
// Drag and Match
// Draw Game
// Drop Wizard
// DROP'd
// Dudeski
// Dungeon Raid
// Educational Game
// Elasticity
// Endless Runner
// Erase Box
// Eskiv
// Farm Heroes Saga
// Filler
// Flappy Bird
// Fling
// Flipping Legend
// Floaty Light
// Fuse Ballz
// GearTaker
// Gem Sweeper
// Globe
// Goat Rider
// Gold Miner
// Grindstone
// GuessNext
// Helicopter
// Hero Emblems
// Hero Slide
// Hexagonal Tiles
// HookPod
// Hop Hop Hop Underwater
// Horizontal Endless Runner
// Hundreds
// Hungry Hero
// Hurry it's Christmas
// InkTd
// Iromeku
// Jet Set Willy
// Jigsaw Game
// Knife Hit
// Knightfall
// Legends of Runeterra
// Lep's World
// Line Rider
// Lumines
// Magick
// MagOrMin
// Mass Attack
// Math Game
// Maze
// Meeblings
// Memdot
// Metro Siberia Underground
// Mike Dangers
// Mikey Hooks
// Nano War
// Nodes
// o:anquan
// One Button Game
// One Tap RPG
// Ononmin
// Pacco
// Perfect Square!
// Perfectionism
// Phyballs
// Pixel Purge
// PixelField
// Planet Revenge
// Plants Vs Zombies
// Platform
// Platform game
// Plus+Plus
// Pocket Snap
// Poker
// Pool
// Pop the Lock
// Pop to Save
// Poux
// Pudi
// Pumpkin Story
// Puppet Bird
// Pyramids of Ra
// qomp
// Quick Switch
// Racing
// Radical
// Rebuild Chile
// Renju
// Rise Above
// Risky Road
// Roguelike
// Roly Poly
// Run Around
// Rush Hour
// SameGame
// SamePhysics
// Save the Totem
// Security
// Serious Scramblers
// Shrink it
// Sling
// Slingy
// Snowflakes
// Sokoban
// Space Checkers
// Space is Key
// Spellfall
// Spinny Gun
// Splitter
// Spring Ninja
// Sproing
// Stabilize!
// Stack
// Stairs
// Stick Hero
// String Avoider
// Stringy
// Sudoku
// Super Mario Bros
// Surfingers
// Survival Horror
// Talesworth Adventure
// Tetris
// The Impossible Line
// The Moops - Combos of Joy
// The Next Arrow
// Threes
// Tic Tac Toe
// Timberman
// Tiny Wings
// Tipsy Tower
// Toony
// Totem Destroyer
// Tower Defense
// Trick Shot
// Tunnelball
// Turn
// Turnellio
// TwinSpin
// vvvvvv
// Warp Shift
// Way of an Idea
// Whack a Creep
// Wheel of Fortune
// Where's my Water
// Wish Upon a Star
// Word Game
// Wordle
// Worms
// Yanga
// Yeah Bunny
// Zhed
// zNumbers