Do you like my tutorials?

Then consider supporting me on Ko-fi

Talking about Actionscript 3, Flash, Game development, iOS and Starling.

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.

Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.