May
23
2011

Building mobile games in Adobe AIR - techniques used in Jumping Droid

Yes... building my first cross-platform and multi-screen mobile game was quite a challenge. At the time when I started this project, the Adobe AIR for mobiles (2.5) was quite immature and the best source of solutions and workarounds to new problems (often bugs) was the Adobe's Flash Community forum. I'm going to share here some techniques I learnt and used in Jumping Droid game. I encourage you to try them, experiment and mix with others, to achieve better optimisation and better performance.

LAST UPDATE: September 2012 (TIP 17: using Vibration Native Extension)

jumpingDroid Play Jumping Droid from the Android Market

jumpingRobot Play Jumping Robot from the Apple AppStore

 

TIP 1: GPU or CPU?

This question should be answered before you start your app/game development. When the first version of AIR for mobile was released, many developers (including me) just took one of their web - Flash project and published it with AIR to see how it would performed. If the project had many animations, the performance was usually very poor in both CPU and GPU modes. It was bad in CPU because mobile processors couldn't handle complex mathematical calculations involved in handling vector objects and animating them (and AIR wasn't well optimised as well). It was bad in GPU because usually the code taken from Flash projects wasn't optimised to use bitmaps efficiently. But in the meantime, Adobe has done many improvements to the CPU mode, especially for iOS (AIR 2.7 is really faster than older releases) and also developers have learnt how to properly work with GPU.

Generally, when looking for answer to question which mode is good for a game/app, you need to specify if you are going to use many static elements like buttons, flat graphics, text boxes and overall very little animation or rather there is going to be plenty of animated objects moving and interacting with each other across the screen.

In the first scenario the CPU will do the job. The main pros of the CPU mode is that you can use all filters (drop shadow, glow etc), masks, all blending modes, which are limited when using GPU. Only in CPU mode it really makes sense to use ActionScript drawing (graphics class of Display Objects), ie to create graphs and graphical simulations. And more importantly, you just take all vectors created by a graphic designer and use them as they are without converting them to any other format. With AIR 2.7 your app will easily perform in 60 fps if there is not too much going on on the screen.

In the second scenario it would make more sense to relieve the CPU and delegate all the graphical work to the GPU. But it is not enough to just "switch" to the GPU, you have to plan and develop your game to properly utilize this mode. GPU can work only with bitmaps, so you need to create / convert all your graphical objects as / to bitmaps beforehand or convert all your vector objects at run time. In the first approach, developers usually use png files (as they support transparency) and usually import them directly to the FLA's file library during development or include them in the SWC file or probably most often load them at runtime (loading when they are really needed - to conserve memory). In the second approach there are two options, you can use cacheAsBitmap and cacheAsBitmapMatrix properties of Display Object that convert their vector representation to bitmaps automatically or manually take a snapshot of Display Objects using the draw method of BitmapData and use it with bitmap objects. I'm using the second method and talk about it more in tip #2. Bu be careful, there is a catch when using the GPU mode though, converting to bitmaps at runtime is a very processor intensive process, it takes time. And forget about converting to bitmap at every new frame, it will just kill your app's performance. Developers beginning with AIR are often surprised that the frame rate drops to extremely low values when they "cache as bitmap" movie clips containing frames of animations. This basically means that at every new frame this movie clip has to be converted to bitmap (and sent to GPU) because it just changed (another frame of animation). In my opinion cacheAsBitmap should throw an error when used on movie clips with more frames then one!

 

TIP 2: Resize MovieClips to match device's screen size and convert them to bitmaps

If you want your game to correctly scale and fit on every mobile device, from smartphones to tablets, then you need to rescale all your assets like backgrounds, hero, enemies, buttons either up or down. But on the other hand you probably already know that if you want smooth animations and keep high frame rate you need to use GPU and bitmaps rather than vector representations of your graphical objects. One easy solution is to resize all objects and then use cacheAsBitmap and cacheAsBitmapMatrix properties to convert to bitmaps. But this technique doesn't produce the best quality graphics, you will notice some strange things going on on converted curves or circles... this wasn't good enough for me. Many developers actually use pre-created bitmaps, usually sprite sheets in form of PNG files. This is the fastest option from the game performance point of view as there is no conversion from vectors to bitmaps (what can take significant amount of time) but again you can't (shouldn't) rescale bitmaps as this would lower their quality. If you develop for Apple devices then you can create separate pngs for iPad, iPhone and iPhone 4 - you know all the types of resolutions already: 1024x768, 480x320, 960x640. But you don't know all resolutions of Android devices, they may slightly vary, in example Galaxy Tab has 1024x600 and other 7 inch tablets 800x480, bigger tablets 1024x768 and smartphones may have 800x480, 960x540...

I decided to do that another way. First I rescale all sprites and movie clips according to a detected screen resolution, then convert them all to bitmapDatas and use as bitmaps. This way I have the best quality graphics, smooth animation and I can reuse the same bitmapData on many bitmap object (one bitmap in memory and many instances of it on the screen).

In Jumping Droid you can see dynamically resized objects and also different backgrounds layout on different screen sizes (last two screenshots are extreme cases). I stick city image to the bottom and clouds at the top:

Jumping Droid 480 x 320

Jumping Droid 720x 600

Jumping Droid 720 x 320

The code snippet would look something like this:

//all graphic objects were created for 800 x 480
//so I save this details as my base resolution
public static const GAME_ORG_WIDTH:uint = 800;
public static const GAME_ORG_HEIGHT:uint = 480;
 
//later in my init function (handler for Event.ADDED)
function init(e:Event):void
{
   stage.scaleMode = StageScaleMode.NO_SCALE;
   stage.align = StageAlign.TOP_LEFT;
   stage.addEventListener(Event.RESIZE, setUpScreen);
}
 
function setUpScreen(e:Event):void
{
   stage.removeEventListener(Event.RESIZE, setUpScreen);
 
   if (stage.fullScreenWidth > stage.fullScreenHeight)
   {
      gameStageWidth = stage.fullScreenWidth;
      gameStageHeight = stage.fullScreenHeight;
   }
   else
   {
      gameStageWidth = stage.fullScreenHeight;
      gameStageHeight = stage.fullScreenWidth;
   }
 
   rescaleRatio = gameStageWidth / GAME_ORG_WIDTH;
 
   //I use ratio to rescale every object, ie:
   hero.scaleX = hero.scaleY = rescaleRatio;
}

 

For bitmap backgrounds which fill the whole screen you will need to crop some part of it (width or height) depending one a device screen size, unless your background has some repeating pattern or you want to break proportions (no - don't do that!). My backgrounds for game levels are not cropped actually. I resize them and if there are white borders I just use bitmapData's copy pixel method to fill them. I literary take the first column of a background image pixels to fill the left white gap and last column of pixels to cover the right gap. Similarly first row for top gap and last for bottom.

Now when all your sprites and movie clips are resized, you are ready to convert them to bitmaps. I created a simple class with public static functions that takes as a parameter a reference to a movieclip, create a sprite container so that movieclips transformations like rotation or scaling are going to be preserved (they would be lost if I worked on display objects directly, unless I would use transformation matrix but this method is just simpler) and then go frame by frame to create bitmap data snapshots of each frame and save it to a vector typed for BitmapDatas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static function MovieClipToBitmapData(mc:MovieClip, xscale:Number = 1, 
                                        yscale:Number = 1):Vector.<BitmapData>
{
   var bitmapDataVector:Vector.<BitmapData> = new Vector.<BitmapData>();
   spriteContainer.addChild(mc);
 
   //loop through each movie clip frame, 
   //rescale it if necessary and create bitmap snapshot
   for (var i:uint = 1; i <= mc.totalFrames; i++)
   {
      var spriteContainer:Sprite = new Sprite();
      mc.gotoAndStop(i);
      mc.scaleX = xscale;
      mc.scaleY = yscale;
      var bd:BitmapData = new BitmapData(spriteContainer.width, 
                                  spriteContainer.height, true, 0x00000000);
      bd.draw(spriteContainer);
      bitmapDataVector.push(bd);
   }
   spriteContainer.removeChild(mc);
   return bitmapDataVector;
}

 

Then in your game you could use it like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//get bitmap data snapshots of enemy movieclip frames
var enemy1Vector:Vector.<BitmapData> = MovieClipToBitmapData(mc:MovieClip);
var enemy1TotalFrames:uint = enemy1Vector.length;
var enemy1Index:uint = 0;
var enemy1Bitmap:Bitmap = new Bitmap();
//create sprite container so the enemy will respond on mouse events
var enemy1:Sprite = new Sprite();
enemy1.addChild(enemy1Bitmap);
 
//then later in the game
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
 
function enterFrameHandler(e:Event):void
{
   if (enemy1Index == enemy1TotalFrames)
      enemy1Index = 0;
   else
      enemy1Index++;
   enemy1Bitmap.bitmapData = enemy1Vector[enemy1Index];
}

 

Of course if you have more enemies/objects, then you would probably need to create a global vector of bitmapData vectors.


TIP 3: Pause game when it is moved to background (when user switches to other app or receives a call)

This is (or should be) a feature of every action game. If your game doesn't pause when it is moved to the background, it may be rejected at submission process to AppStores which review apps. Just imagine what would happen if Jumping Droid wasn't going into pause mode when the game loses focus as a result of incoming call or because the user pressed the hardware Home button that brings to a phone desktop. If the user managed to get to one of the top floors in the game, he/she would need to start from the ground floor again when coming back to the game as the droid guy would fall on the lowest floor in the meantime. You would also still hear sounds from the game. But more importantly, running app would heavily use CPU, what would obviously have impact on other applications performance and overall energy consumption. To remedy this situation, just listen for the Event.ACTIVATE and Event.DEACTIVATE events and create function to pause and unpause your game.

Jumping Droid paused screen.

In Jumping Droid I left the users with option to pause when pressing the hardware Menu key on Android or the Menu screen button on iOS. At the pause state, the frame rate is dropped and I unregister the main ENTER_FRAME event handler so all animation and other related processes are stopped. If the game was moved to the background and is back to the foreground, the user sees the pause screen and options menu and can either continue the game or quit.

Sample code below:

//somewhere in my init function 
NativeApplication.nativeApplication.addEventListener(Event.ACTIVATE, 
                                      handleActivate, false, 0, true);
NativeApplication.nativeApplication.addEventListener(Event.DEACTIVATE, 
                                      handleDeactivate, false, 0, true);
 
function handleActivate(e:Event):void
{
   //This code is executed when application gets back focus / goes to foreground
   //I do nothing here and leave it up to the user to press 
   //the "back to game" button in pause/options menu.
}
 
function handleDeactivate(e:Event):void
{
   //This code is executed when application looses focus / goes to background
   stopGame(); //this also changes frame rate to 10
   showPauseScreen();
   showOptionsMenu();
}

 

Note that the NativeApplication events are working only in AIR (they aren't available in standard web Flash).

 

TIP 4: Make object's frames switching independent of current frame rate

I set Jumping Droid frame rate to be 100 fps. Of course that kind of speed is only achievable on desktop computers at the moment :) But the game works 60 fps on iPhone 3, 56 fps on Samsung Galaxy and 30 fps on HTC Desire, so as you can see I'm fully utilising all those devices. The problem is that normally that kind of dynamic frame rate would affect objects animations, in example if game was designed to work with 30 fps but runs with 60 fps, animation would be two times faster (when saying animation I mean switching frames of these objects). We don't want our objects to animate slower on some devices and faster on others. That would look ridiculous if enemy was moving slowly but its legs would suggest that it's actually running couple times faster. My aim was to animate all objects the same way on all devices.

Animations of running enemies were created for 30 frames per second so this is going to be my base value. The code below switches bitmap's bitmap data only after specified amount of time, which is 1/30 s in my case, rather than every at enter-frame event:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//define original game frame rate - this is a frame rate
//for which you created original animation
public static const GAME_ORG_FRAME_RATE:Number = 30;
 
var frameTime:Number = 1000 / GAME_ORG_FRAME_RATE;
var _time:Number = 0;
var _elapsedAnimation:Number = 0;
var _animate:Boolean;
 
//than later in your game in enterFrame handler
function animationMaker(e:Event):void
{
   _time = getTimer();
 
   //animate only if 1/30s elapsed since last check
   if (_time - _elapsedAnimation > frameTime)
   {
      _elapsedAnimation = _time;
      _animate = true;
   }
   else
   {
      _animate = false
   }
 
   //this could be a for loop to animate more than one objects/enemies...
   if (_animate)
   {
      if (enemy1BitmapFrameCounter == enemy1Vector.length)
         enemy1BitmapFrameCounter = 0
      else
         enemy1BitmapFrameCounter++;
 
      enemy1Bitmap.bitmapData = enemy1Vector[enemy1Index];
   }
}

 

TIP 5: Make object's movement independent from current frame rate using time-based application

Same as with objects frame switching, we don't want our hero or enemies moving faster across the screen on some devices and slower on others. So we can't just use ENTER_FRAME event to control x and y of screen objects. Instead I'm using a technique called time-based animation. Using it, it doesn't matter what frame rate game is running, objects will move the same speed as their position is calculated based on how much time has passed since the last step. First I save current time, then at a next ENTER_FRAME or TIMER_UPDATE update I check current time and calculate a difference between them. The bigger the difference is, the bigger the distance that object will make.

1
2
3
4
5
6
7
8
9
//240 is calculated from 8 * 30 
//   where 8 is number of pixels I wanted an object to move
//   when frame rate was 30
public static const VELOCITY_ORG_HORIZONTAL:Number = 240;
 
private var _lastTimeCheck:Number = 0;
 
//then later I calculate the new speed depending on a screen size
var velH:Number = VELOCITY_ORG_HORIZONTAL * rescaleRatio;

 

I recalculate the velocity to be slower on small screens and faster on bigger screens using my rescaleRatio variable from the tip #2. This way the game won't be easier for owners of bigger devices.

1
2
3
4
5
6
7
8
9
10
11
addEventListener(Event.ENTER_FRAME, updateObjects);
//you could also use Timer and TimerEvent
 
private function updateObjects(e:Event):void
{
   var timeDifference:Number = (getTimer() - _lastTimeCheck) * 0.001;
   _lastTimeCheck += timeDifference;
 
   //move my all objects, this could be a for loop
   hero.x += velH * timeDifference;
}

 

Now you can set your game fps in Flash to be even 100. If game will be running 20 fps, animation won't be that smooth as on faster phones but objects will move at the same speed as on device performing 80 fps.

 

TIP 6: Dynamically reduce frame rate when high frame rate is not necessary

Sure, game animation looks much better when running 60 frames per second. Everything looks smooth and objects movement isn't jittery. But the drawback is the fact that the high frame rate consumes more power. There is nothing wrong if the frame rate is high at the game play. But it is not necessary when the user paused the game or moved to one of the menu screens. You can then dynamically change the frame rate to 10 or even lower - as it is sufficient for buttons animation and will save battery.

Jumping Droid dynamic frame rate

First I save the original frame rate value. I define 10 frames per second as a constant. Using both values I dynamically switch the frame rate depending on what state the game is currently in.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//define low frame rate
public static const LOW_FRAME_RATE:uint = 10;
 
//somewhere in my init function:
//remember initial frame rate
initialFrameRate = stage.frameRate;
 
//somewhere in the code depending on the game's current state:
switch (gameState)
{
   case STATE_PAUSE:
      //...some other stuff
      stage.frameRate = LOW_FRAME_RATE;
      break;
   case STATE_MENU:
      //...some other stuff
      stage.frameRate = LOW_FRAME_RATE;
      break;
   case STATE_PLAY:
      //...some other stuff
      stage.frameRate = initialFrameRate;
      break;
}

 

According to the documentation, the mobile versions of AIR (2.5) and Flash Player (10.1) have built - in functionality to drop the frame rate to 4 fps and skip rendering step when application goes to the background. This applies to a Flash Player content that loses focus, ie. if user scrolled a web page and Flash content is not visible anymore. You may be thinking why to 4 fps not zero? In Flash Player, instructions are only executed at frame change and that minimal frame rate allows to keep net streams and net or socket connections if they are created.


TIP 7: Don't let users wait too long, split heavy tasks into chunks

Bitmap caching is a very expensive process, if your game has many levels with different backgrounds and you want to cache them all to bitmapDatas as a first thing after game is loaded, then don't do that. Jumping Droid has 20 levels and it takes about 10 - 15 seconds to cache all of the backgrounds on iPhone 3. I think that 15 seconds is enough time for the user to get bored or think that your game just hanged or crashed. So it is better to split this task into parts and do them separately. I cache every next-level bitmap just after the user completes a current level. The whole game stops, the droid guy says congratulations and after a second or two the game moves to the "next level" screen. I think that the user think this pause is actually intentional, just to give her/him some time to rest :) but in fact I'm just caching another bitmap (if it wasn't cached already)! Caching process is so heavy that your app may even freeze at that time of doing this (multithreading would be a great cure for that). Remember to store your bitmapData in a Vector and reuse it - one bitmapData can be used on many bitmap objects.

You should also avoid creating (instantiating) objects at game-play, as this may result in frame drops, animation may become choppy. It is better to create all objects that may be used beforehand, when generating a new level, make them invisible and don't "plug" them to any listeners or functions that will change their x, y, rotation, listeners or other properties, until they are enabled. Then when you need them, just turn their visibility on.

When launching your game on iPhone you can also use Default.png splash screen to have something on the screen (like you company logo or game image) when game is loading into memory. There is no such option for Android at the moment.

If you game is big, you could actually split it into multiple swfs, package them together and load these external assets when necessary from your main "shell" file. Just remember that on iOS no ActionSctipt code will be executed from any external swf. To be more precise, if you try to load a swf with AS code or even a class assigned to some DisplayObject, the loading process will fail silently... But remember, you can still load graphic assets (they can have instance names), sound clips or videos and you can use your code from the main swf (loader) to work with these assets.

 

TIP 8: Allow users to install their apps on SD card (Android only)

I'm surprised that Adobe still haven't added any tick box to set that in Publish settings in the Flash Professional! Even Adobe evangelists never mention how to use this very important feature. Just look at the comments of most of the AIR for Android games or apps, the users mostly complain about lack of "Move to SD" option. While Motorola and Samsung phones have plenty of internal memory, HTC Desire, still very popular Android 2.2 phone, has very limited space. If the users installed Adobe AIR (17 MB) and Adobe Flash (12 MB) that can't be moved to SD for obvious reasons (if they were on SD and SD was removed or device was plugged as a disk drive then these both wouldn't be available along with all apps that require them), they won't be happy to keep other apps on their internal memory if they are running out of space. But there is nothing stopping you to make your app movable to SD. Remember, if the user is struggling with "out of space" messages, she/he will move your app to SD rather than remove it.

Read this article to learn more: http://sierakowski.eu/list-of-tips/78-how-to-make-air-for-android-app-movable-to-sd-card.html.


TIP 9: TextFields and objects receiving mouse events are slowing down performance

Especially when used in GPU mode. TextFields are like vectors, it seems like they are recalculated at every frame if something overlaps them or if they overlap something else, especially animated objects. I tried to embed fonts, used native font and always their existence had some impact on performance. What I did and what other developers do is create bitmaps of each character or digit that might be used. In case of Jumping Droid, I have the current score and the high score fields, which include digits and 4 letters HI and SC. I convert sprites with text fields containing all possible values to bitmaps and then get rid of all text fields. It is a little bit of work, but as a result, performance is exactly the same as if I would add few more bitmaps, there are no vectors on the stage.

Situation complicates when you want to have some input text fields, but just one or few fields will not totally kill your game's performance.

I recently found that TextFields don't have that negative impact in AIR 2.7 when using CPU mode.

If you have objects that aren't reacting on MouseEvents, you should disable them as the FlashPlayer is still bubling events through them "thinking" they may receive this events what obviously have impact on performance:

mouseEnabled = false;
mouseChildren = false;

 

 

TIP 10: Control your memory and use garbage collector

When testing your game on actual device, it is good to use some tool that shows memory usage and fps. Some people use the standard AIR debugger and trace these values, others third party solutions like Monster Debugger. Or you can just display this information on screen. I initially used MrDoobs Hi-ReS-Statsbut quickly adjusted (simplified) it to meet my needs. Using this performance monitor, I learned what has impact on fps, why it was dropping sometimes and realised that from time to time game kept too many objects in memory:

Jumping Droid paused screen.

In my version of performance monitor, I display the minimum, current and maximum memory used and a current frame rate (if you want to check it in JumpingDroid, go to the main screen, then to the about screen and press the "turn fps info on" button). When testing the game I found that some objects from previous levels still occupied memory. Generally garbage collector removes all unused objects when there is some spare time between rendering frames (if game isn't too busy). If there are many calculations, like if there are many objects on the screen and you have some heavy collision detection algorithm, it should not fire up. But sometimes it does and you can see it in some frame drops. What I do is manually call garbage collector when a level is finished at the congratulations screen - there is no animation at that time, so the user won't notice any slow down.

flash.system.System.gc();

 

Generally the best practise is to do not create and remove the same objects at every level but rather reuse them (ie by using object pools). But if there are different objects used on each level then it is fine to "collect garbage" like unnecessary enemies etc.

 

TIP 11: Keep game awake

Or I should rather say prevent the system from dropping into an idle mode. This one is important as if you forget to make your game be constantly awake, device's screen will just dim off (turn black) when there is some inactivity time. This will happen when the user don't tap screen or hardware keys for some time. This time is obviously different depending on OS or user settings. Tilting device when using accelerometer is not counted as activity.

To prevent screen from dimming and keyboard from being locked use the following code:

NativeApplication.nativeApplication.systemIdleMode = SystemIdleMode.KEEP_AWAKE;

 

On Android version, add the following permission to the application descriptor file:

<uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>

 

TIP 12: Save game state

Imagine user's frustration if just after winning some battle or getting almost to the end of a level, an incoming call quit the game. This is no brainer - game state has to be saved. You can use the DEACTIVATE event from the tip #3 to call function saving game settings and progress. But it is also a good practice to save them also when something important happens like beating some difficult monster or just do auto save every minute as there might be some extraordinary situation when phone hangs or resets without event notifying running apps (and you won't receive DEACTIVATE event).

There are few good ways of saving your state and you can chose one depending on how much data you want to save. You can use old good Shared Object aka flash cookies, you can save to a file (AIR can do that), you can use built-in into AIR SQLLite or even send data to a remote server.

In Jumping Droid I decided to only save the high score. This is very low amount of data so Shared Objects seemed to be the best choice:

private var _so:SharedObject;
 
//in your init function
_so = SharedObject.getLocal("JumpingAndroid/scores", "/");
if (_so.data.highScore == undefined) _lastHighScore = _so.data.highScore;
 
//and later on game over:
if (_highScore > _lastHighScore)
{
  _so.data.highScore = _lastHighScore = _highScore;
  _so.flush();
}

 

On Android version add this permission to the application descriptor file:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

 

TIP 13: Terminate or quit when exiting?

On iOS you can't make application quit programmaitically. Only the user may do that by pressing the hardware Home button what would result in suspending or quitting the application.

On Android if the user presses hardware Home or Back button it will bring her/him to the Android home screen and application will be suspended. Then when the user comes back to the app (if the device wasn't restarted or app wasn't killed from programs like Advanced Task Killer or Android settings panel) it continues from where it was quit.

So on both systems you can either quit your game or suspend it (by default they are suspended).

To quit on iOS, add UIApplicationExitsOnSuspend to the iPhone InfoAddition section of you application descriptor file:

<iPhone>
    <InfoAdditions>
      <![CDATA[
    <key>UIApplicationExitsOnSuspend</key><string>YES</string>
    ]]>
    </InfoAdditions>
</iPhone>

 

To quit on Android, use exit() method of nativeApplication property of NativeApplication class in your game code:

NativeApplication.nativeApplication.exit();

 

You should add this code to your DEACTIVATE event handler from tip #1 and you can also create a quit button and add this code to a Click handler.

 

TIP14: Use subversioning to save yourself hassle

I can't even remember how many times something didn't work in the game the way I wanted or the performance wasn't good enough and I had to come back to a version from two weeks before. Not to mention that something corrupted in the FLA file and I had to revert back to last stable version. Really I can't imagine working on bigger projects without Subversioning. Since Flash CS5 you can save uncompressed FLA files which are just folders with xml files containing information of each library object and settings. So now I'm not only subversion ActionScript files but also FLAs. There are many tutorials on internet explaining how to use Git or SVN so if you haven't use them yet - ow is the best time to start :) (I'm thinking of making some quick tutorial on using SVN with Flash soon).

 

TIP15: Use Remote Debugging over WiFi to see traces from your app running on a device

Making your app sending traces remotely to your computer is very easy and will help you finding any problems that couldn't be detected when testing on your computer. There are three things that are involved to accompilish this:

1. You need to publish your swf including the debug code from Flash IDE
In Flash CS5 and CS5.5 go to Publish Settings, Flash tab and check Permit Debugging in Advanced section.

2. You need to use adt tool with -target ipa-debug
You can also predefine IP address of your debugging computer instead of typing it manually at application launch on a device: adt -package -target ipa-debug -connect 192.168.1.4

3. Your device and computer must be in the same wifi network and you computer's firewall should allow TCP traffic on port 7935. To start debugging session in Flash IDE select Debug -> "Begin Remote Debug Session" -> ActionScript 3.0

Rember to start debugging in Flash IDE before launching your app on a device, otherwise you will be asked to provide IP of the computer even if you provided -connect parameter to adt. If everything is done right you should see traces from your app running from you phone or tablet in the Flash output panel as if it was running on your computer!

 

TIP16: Test how much CPU your app is using on actual devices

I don't know how to do that on iOS but it is very easy on Android with android-sdk. All you need to do is plug your Android device to a computer in Debug mode. You do that on your phone or tablet by going to Settings -> Applications -> Development and selecting "USB debugging". Then on your computer open command prompt (cmd), go to you Android SDK folder, platform-tools folder and run this command:

adb shell top -m 5

 

The -m 5 you define that you are going to see first top 5 processes sorted by CPU usage. When running this command you should see a list of processes refreshed every coumple of seconds:

cpu android

 

TIP17: Use Native Extensions to get extra functionality not built into AIR

There is plenty of documentation on how to use Native Extensions in Flash Builder or in the latest version of Flash Professional CS6. I normally use Flash Builder but I developed Jumping Droid in Flash Professional CS5.5 so I will share here how I added the vibration support to the game.

1. Download Vibration extension from the Native Extensions catalogue on Adobe website: http://www.adobe.com/devnet/air/native-extensions-for-air/extensions/vibration.html

2. Unzip archive and get the swc and ane files from the ReadyToUseExtenstion folder.

3. In Flash IDE, open ActionScript settings and in Library path add a new SWC file by browsing to VibrationActionScriptLibrary.swc. After that keeping the swc selected click on the "i" icon what opens the "Library Path Item Options". Make sure the "Link Type" is set to External.

4. Now in your code first import the Vibration class:

import com.adobe.nativeExtensions.Vibration;

 

5. And then add the code to check if device allows Vibration and run it:

var vibe:Vibrator;
if(Vibration.isSupported)
{
     vibe = new Vibration();
     vibe.vibrate(500)
}

 

6. When you compile your swf you should get an error message that extension isn't supported on this platform which is fine.

7. In your application descriptor add the extension as below:

<extensions>
    <extensionID>com.adobe.Vibration</extensionID>
</extensions>

 

8. And then also request permission for vibration:

<![CDATA[<manifest>
    <uses-permission android:name="android.permission.VIBRATE"/>
</manifest>]]>

 

9. Now create the "extensions" folder in the same place where you keep your swf, descriptor and icons and place the com.adobe.extensions.Vibration.ane file in there.

10. Add below to your batch script that you use to publish your AIR app with ADT. This will include the ane file as a native extension.

1
-extdir extensions

 

FINAL TIP: Share your experience with others on forums! Only this way we can learn from each other and improve our apps.

If you have different experience with any of the things in this article, please share it with us through the comments!

Comments 

 
0 #38 Sun 2014-03-17 06:00
I have a question about the tip#2.
So why did you convert everything(imag es?) from movieclip to bitmap?
what about sprite? do you convert sprite types to bitmap as well?

Thanks in advance!
Quote
 
 
+1 #37 Wade 2013-12-02 22:28
I had thought about the tip for using ACTIVATE and DEACTIVATE for knowing when my app was interrupted by a phone call, but there is 1 flaw in relying on those two events. If the user is using a headset to talk and switches back to the app in the middle of the call, you could be attempting to resume audio playback on top of the phone call audio. I have been doing endless searches of a way to pause audio in my Audiobook Player app I am making(since AIR mutes all audio during an incoming phone call and while on a call) and it looks like I will have to just make an ANE using native Android Java code to detect phone call states. I also tried checking the SoundMixer.soundTransform.volume during an ongoing phone call to see if it changed to "0" and hoped that was how AIR was "muting" audio, but that was not the case.
Quote
 
 
+1 #36 xiaoMa 2013-11-14 07:15
Quoting Marcos:
Hi... Im working in an AIR project for Android. I did step by step everything you say in this post but I still cannot make the phone vibrate. I have an error when I try to debug directly to device:
"An implementation for native extension 'com.adobe.Vibration' required by the application was not found for the target platform'

I dont know exactly what it means... can you help me??

(Sorry about my english)...

Regards...

ANE path is wrong
Quote
 
 
0 #35 Germán 2013-07-03 16:23
Thanks!

In case of using a set of images for popular resolutions and then scale them.

Do you know if the usual practice is to embed all of these sets, even if just only one of them is used? Is there an efficient way to do it?

Regards

Quoting sigman:
Hi German,

Yes, every asset is vector. Android animation is actually a vector movie clip and in AS code I go through each frame, resize it and take bitmap snapshots of resized frames.

Good luck.

Quoting Germán:
Hi,

Great article, just one question. I didn't quite understood if you use vectors even for your animations (i.e. little droid in game), resized them dynamically and the converted them to bitmap. Or if these animations were already sprite sheets in bitmap format and you scale them dinamically loosing some quality? Thanks
Quote
 
 
0 #34 sigman 2013-07-01 19:30
Hi German,

Yes, every asset is vector. Android animation is actually a vector movie clip and in AS code I go through each frame, resize it and take bitmap snapshots of resized frames.

Good luck.

Quoting Germán:
Hi,

Great article, just one question. I didn't quite understood if you use vectors even for your animations (i.e. little droid in game), resized them dynamically and the converted them to bitmap. Or if these animations were already sprite sheets in bitmap format and you scale them dinamically loosing some quality? Thanks
Quote
 
 
0 #33 Germán 2013-07-01 17:11
Hi,

Great article, just one question. I didn't quite understood if you use vectors even for your animations (i.e. little droid in game), resized them dynamically and the converted them to bitmap. Or if these animations were already sprite sheets in bitmap format and you scale them dinamically loosing some quality? Thanks
Quote
 
 
+1 #32 YopSolo 2013-04-22 12:13
great tip collection !
Quote
 
 
+2 #31 Horacio 2013-02-19 00:31
I just learned the hard way most of this making my first android app, didn´t take me too long but things that I didn´t know will make me do it more easy, great article!
Quote
 
 
+2 #30 Marcos 2013-02-12 00:18
Hi... Im working in an AIR project for Android. I did step by step everything you say in this post but I still cannot make the phone vibrate. I have an error when I try to debug directly to device:
"An implementation for native extension 'com.adobe.Vibration' required by the application was not found for the target platform'

I dont know exactly what it means... can you help me??

(Sorry about my english)...

Regards...
Quote
 
 
0 #29 Sean P Smith 2013-01-24 22:38
Just FYI for anyone trying to get the vibration working, and Sierakowski, there is an error in the code, when you init the var vibe you type it as vibrator and then create a new instance of Vibration(). It should just be var vibe:Vibration... that was the least of my troubles getting that to work. Anyone trying to use ADT for the first time, make sure to put all your paths (full paths to your files) in quotes... whew!
Quote
 

AS3 Tips

Follow me on Twitter

ankara escort ankara escort ankara escort ankara escort