How To Make a Simple iPhone Game with Flash CS5

This is a tutorial by iOS Tutorial Team member Russell Savage, the founder of Dented Pixel, a creative development company. He recently released its first app – Princess Piano – and he is currently hard at work on several new projects. I originally got into Objective C reluctantly. I had been holding out because I […] By Ray Wenderlich.

Leave a rating/review
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

Gratuitous Sound Effects

Awesome, we have some great puck hitting mechanics now, but you know what would really spice things? Gratuitous sound effects of course!

Download the resources for this project, which includes a ricochet effect we’ll use (puckRicochet.wav). Now import the effect by selecting your Flash project and going to File->Import->Import to Library.

You will see the wav file in your library pane:

A sound file in the flash library

But to make it available to your code you need to give it a ActionScript Linkage name. A quick way to do this is to double click the area next to the asset and name it there. Give it the name “PuckRicochet”.

Naming a sound in the Flash library

Now in your hit detection routine, add the following code to play the sound effect:

var puckRicochet:Sound = new PuckRicochet() as Sound;
puckRicochet.play();

And import the class you need to do this at the top of the file:

import flash.media.Sound;

Well that sound effect is fun but you know what would really spice this up? Some 8-bit polka music (courtesy of Kevin MacLeod)!

In the resources for this project, you’ll find an MP3 file. Import the file in the same way and name it PixelPolka.

Then add the following code in your constructor to make it play and loop:

var pixelPolka:Sound = new PixelPolka() as Sound;
pixelPolka.play(0, int.MAX_VALUE);

You can’t make it loop indefinitely, but by passing the max value for an integer as the second parameter we can make sure the user will get their fill of polka. Unless they love polka so much they listen to it more than 2147483647 times! :]

Optimization

Optimizing your game is quite important in any language but it is particularly important in Air for iOS because it will not run as smoothly out of the box as it would if you had programmed it in Objective C.

One of the easiest wins for better performance is to set the stage quality to low. This setting is mostly used for rendering vectors in flash and because most of your objects should be converted into a Bitmap anyways for best performance it really won’t effect the visual quality of your app too much. To do this add this line in the constructor:

stage.quality = "low";

Low quality images

As you can see in the image above setting of the stage quality to low effects the quality of the images produced (Notice the jagginess around the edges of the paddle and puck). These defects do not translate to the device though, the GPU will produce the same crisp images, but the performance will be better.

Now as I mentioned before it is important for your objects to be rendered as bitmap objects. If the object is a bitmap Air for iOS is smart enough to use the iPhone’s GPU to store this object in memory causing redraws of this object to be substantially faster than if it had to be drawn to the screen every time. Add these lines at the end of the constructor:

hockeyPaddle.cacheAsBitmap = true;
hockeyPuck.cacheAsBitmap = true;

Also it is recommended to add cacheAsBitmapMatrix property as well, this gives similar GPU performance increases but while cacheAsBitmap only helps for x, y translations of the object, cacheAsBitmapMatrix will work for any transformation of the object including rotation, alpha, and more complicated Matrix transformations like skewing or flipping.

hockeyPaddle.cacheAsBitmapMatrix = new Matrix();
hockeyPuck.cacheAsBitmapMatrix = new Matrix();

For this to work, you also have to add an extra import:

import flash.geom.Matrix;

I would warn developers to not use cacheAsBitmapMatrix too often. While in most literature it is recommended as a performance boost, in some cases I have seen it to have the exact opposite effect of slowing down performance. So this may take some trial and error to see if it will help performance in your game. But a general rule would be only to use it on a handful couple of objects.

In order to really test the performance of your game you need to run it on a device. The performance on the computer is not a good indication of how it will run on the device, it could be either better or worse, but most of the time it is worse (it is a mobile device after all). Take a look at my publish settings for the app:

Flash publish settings for iOS

It’s important to set the resolution to high so that it can support the retina display. It’s also worthwhile to make it a universal binary (supporting the iPhone and iPad) as it is relatively easy to resize the flash assets particularly if you are just scaling down.

To make sure the assets look ok on the older 3GS (non-retina display phone) let’s half the size of the assets if the resolution is below a certain threshold (it’s worth noting that Air for iOS does not support the older 3g and 1g phones).

if(stage.stageWidth<=320){
    hockeyPaddle.scaleX = 0.5;
    hockeyPaddle.scaleY = 0.5;
    hockeyPuck.scaleX = 0.5;
    hockeyPuck.scaleY = 0.5;
}

Deploying to a Device

Unfortunately one of the most painful steps is still left to get your app to run on the phone. Setting up the certificates in the deployment tab can be a bit of a challenge particularly if you have not dealt with Apple’s system of provisioning profiles before.

This process has been covered in great depth on other blogs so instead of duplicating the effort, I will provide you with a list of helpful resources to installing your app:

Furthering the Game

While we have a good start, right now there isn't any point to hitting the puck around!

So let's make the goal how many gold coins you can pick up within 30 seconds.

First we will add the count-down timer. Add this to your constructor:

// Add count-down timer textfield
timeLeft = new TextField();
timeLeft.text = "30";
timeLeft.setTextFormat( new TextFormat("Arial", stage.stageWidth * 0.15, 0x777777, "bold") );
timeLeft.x = stage.stageWidth * 0.8;
timeLeft.y = 0;
this.addChild( timeLeft );

currentScore = new TextField();
currentScore.text = "0";
currentScore.setTextFormat( new TextFormat("Arial", stage.stageWidth * 0.1, 0x000000, "bold") );
currentScore.x = stage.stageWidth * 0.8;
currentScore.y = stage.stageHeight * 0.1;
this.addChild( currentScore );

levelTimer = new Timer(1000, 30); // repeat every 1000 milliseconds (1 second), 30 times (30 seconds)
levelTimer.addEventListener(TimerEvent.TIMER_COMPLETE, levelComplete, false, 0, true);
levelTimer.addEventListener(TimerEvent.TIMER, updateLevelTimer, false, 0, true);
levelTimer.start(); // timers have to be told to start, they don't start automatically

Also add the variables for these:

var timeLeft:TextField;
var currentScore:TextField;
var levelTimer:Timer;
var levelScore:Number = 0;

And the imports:

import flash.text.TextField;
import flash.utils.Timer;
import flash.text.TextFormat;
import flash.events.TimerEvent;

These functions are going to be controlled by the timer. They will keep track of when the level is over and how much time you have left.

private function updateLevelTimer(e:Event){
    timeLeft.text = String(Number(timeLeft.text) - 1); // subtract one from current time
    timeLeft.setTextFormat( new TextFormat("Arial", stage.stageWidth * 0.15, 0x777777, "bold") );
}

private function levelComplete(e:Event){
    levelTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, levelComplete);
    levelTimer.removeEventListener(TimerEvent.TIMER, updateLevelTimer);

    var completeMessage:TextField = new TextField();
    completeMessage.width = stage.stageWidth;
    completeMessage.multiline = true;
    completeMessage.text = "Level Complete!\nYour Score:"+levelScore;
    var textFormat:TextFormat = new TextFormat("Arial", stage.stageWidth * 0.1, 0x00CCFF, "bold");
    textFormat.align = "center"; // this center aligns the text
    completeMessage.setTextFormat( textFormat );
    completeMessage.y = stage.stageHeight*0.5 - completeMessage.textHeight/2;// to center the textfield I am subtracting half of the height of the text field's content
    this.addChild( completeMessage );
   		 
    this.removeEventListener(Event.ENTER_FRAME, update);
}

Notice we are removing the listener for the ENTER_FRAME to suspend gameplay once the level is complete, we are also removing the Timer listeners.

Hit command enter and you can see this code in action! You should see a countdown to 0, and then a final score message.

Adding a countdown timer

Contributors

Over 300 content creators. Join our team.