Tags – actionscript.

WWF – My World
I was glad to help London studio disturb media with a new World Wildlife Fund special 50th anniversary interactive site.
Year:
2011
Client:
disturb media
See it live at myworld.panda.org

Mobile AIR optimizations.

If you’re interested in optimization of Adobe Flash Platform, mainly AIR for mobile devices, you may be interested in the outline I compiled from several sources yesterday:

Device features

  • Multitouch
  • Accelerometer
  • Orientation
  • Microphone
  • Keyboard
  • GPS
  • Camera

General

  • Don’t write code to constructors, the JIT (Just In Time compiler) won’t optimize it. Code inside the constructor is not optimized by the Just-in-time compiler (JIT). To use JIT optimized code there is the possibility to call a function out of the constructor. The code inside that function is then optimized again. The reason why there are no test results here is that I could not be a real rule when it makes sense to do this or not. Usually you expect a faster code execution but if there is no difference because the JIT is not used at all which could happen you have an even slower code execution because of one extra function call which is your initializing function. (Joa Ebert: ActionScript 3 optimization techniques)

  • If you need to use an Array of Numbers, use ByteArray. It’s much faster.

  • Reuse everything, mainly DisplayObjects and URLLoader objects (Reusing Objects)

  • Use ScrollRect instead of masks.

  • Create DisplayObjects ahead of time to increase perceived speed of transitions.

  • Typing prevents runtime evaluation of method signatures which can slow down the VM

var p:Point = new Point();
p.x = Number(23);

This applies to web service JSON data too:

[
    {
        "_type_" : "RecordClass",
        "name" : "Joe"
    }
]

GPU vs. CPU

  • Flash attempts to keep bitmaps on the GPU allowing them to be quickly composited.
  • Images that don’t change across frames are ideal for GPU caching.
  • If The GPU cannot render an object, it is not displayed at all. There is no fallback to CPU rendering.
  • The following blend modes are not supported: layer, alpha, erase, overlay, hardlight, lighten, and darken.
  • Filters are not supported.
  • PixelBender is not supported.
  • Many GPU units have a maximum texture size of 1024×1024. In ActionScript, this translates to the maximum final rendered size of a display object after all transformations.
  • Adobe does not recommend the use of GPU rendering mode in AIR applications that play video.
  • GPU rendering mode is disabled for some devices on which the mode does not work reliably.
  • Limit the numbers of items visible on stage. Each item takes some time to render and composite with the other items around it.
  • Don’t overdraw. Use the background color as a background. Don’t layer large shapes on top of each other. There is a cost for every pixel that must be drawn.
  • Avoid shapes with long thin spikes, self -intersecting edges, or lots of fine detail along the edges. These shapes take longer to render than display objects with smooth edges.
  • Limit the size of display objects.
  • Enable cacheAsBitMap and cacheAsBitmapMatrix for display objects whose graphics aren’t updated frequently.
  • GPU rendering is more restrictive in mobile AIR apps created with the Packager for iPhone.
  • The GPU is only effective for bitmaps, solid shapes, and display objects that have the cacheAsBitmap property set
  • For objects that have cacheAsBitmap and cacheAsBitmapMatrix set, the GPU can effectively render objects that rotate or scale.
  • The GPU is used in tandem for other display objects and this generally results in poor rendering performance.
  • The Android’s GPU renderer results in far better performance when using copyPixel method, AND, as a bonus, it automatically smooths bitmaps that have been rotated.

Events

  • Don’t use bubbling. A quick way to deal with these issues is to not bubble your events when possible and when you do need to bubble, stop propagation when the events have reached a point that they don’t need to bubble anymore:
event.stopPropagation();

Mouse

  • Don’t use MouseEvent.MOUSE_MOVE. It is simply not there.

  • Check for mouse position in an interval. But no setInterval or Timer, see below.

  • One other thing you can do is prevent mouse events (that are set to bubble by default) from effecting children you weren’t intending. Try setting mouseChildren and mouseEnabled to false.

Filters & Blending Modes

  • Don’t use them.

Visibility

  • visible = false is not enough. Use removeChild().

  • But first stop the MovieClip.

Timers

  • Don’t use Timers: To improve performance avoid high rate timers, this will reduce the amount of events being fired and the quantity of code being executed. The second performance increase is to have one timer for the entire application. Think of it as a heartbeat for your application keeping the internal timing for all your functions. This will reduce the amount of timers you have and the amount of time calculations taken from the Flash Player environment.

  • Event.ENTER_FRAME is better than Timer.

  • Set frame rate to 4 when not animating, increase when needed. Monitoring your frame rate is very important as many flash designers want to ramp up the frame rate to 60 frames per second because it will make the animations “pretty”.

stage.frameRate = 4;

Use deactivation when the app is in background:

this.addEventListener(AIREvent.APPLICATION_DEACTIVATE, appDeactivate);

Grant Skinner wrote a brilliant class on FPS management.

Vectors

  • Draw vector content to Bitmap before it gets rendered to Stage.

  • For simple shapes that are not interactive, use Shape objects. For interactive objects that don’t need a timeline, use Sprite objects. For animation that uses a timeline, use MovieClip objects. Always choose the most efficient type of object for your application.

  • Use the getSize() method to benchmark code and determine the most efficient object for the task.

  • Avoid using the ActionScript drawing API (the Graphics class) to create graphics. When possible, create those objects statically at authoring time instead.

  • Pre-cache Vector animations as a Sprite Sheet for optimal load times. Instead of caching your vectors as bitmaps at runtime, you should convert your vectors into bitmaps before compiling your game. First, store your bitmaps in a tile-based sprite sheet. Then, export this image for action script as bitmap data. Finally, cache positions (parts of the bitmap data) you want to manipulate as rectangles and use the copyPixel method to pull the actual bitmap data off the sprite sheet. 8BitRocket’s website covers how to achieve all of these steps except for converting your MovieClip based animations into a single sprite sheet image. It so happens that I have developed a component that will automatically export a series of MovieClip animations into a PNG with transparency capability; I am willing to share this if I can get some support from other developers on this forum.

Bitmaps

  • Scale bitmap assets to the final size before importing them.

  • One other little used cache method is cacheAsBitmapMatrix. Similar to the cacheAsBitmap this will also cache the x, y, rotation, scale, skew properties. Keeping the Flash Player from having to redraw the object. Again, if you make any changes to alpha/color or the children sprites matrix the object will be redrawn and you will be spending more time and memory to cache an object that is constantly redrawn.

myBall.cacheAsBitmapMatrix = new Matrix();
  • Don’t forget that images split by the power of 2 are going to be more efficient on memory than odd sized images. This can’t always be avoided by it’s good to remember.

  • Make bitmaps in sizes that are close to, but less than, 2n by 2m bits. The dimensions do not have to be power of 2, but they should be close to a power of 2, without being larger. For example, a 31-by-15–pixel image renders faster than a 33-by-17–pixel image. (31 and 15 are just less than powers of 2: 32 and 16.)

  • If possible, set the repeat parameter to false when calling the Graphic.beginBitmapFill() method.

  • Runtime bitmap caching is impractical for mobile. Ideally, I’d like to pre-cache all of my vector based animations as bitmaps such that they are sized to the native resolution of my device at runtime (ex, iPhone has very small resolution, iPad large resolution, and Android, right in the middle). 8BitRocket’s tutorials explain how to achieve this effect by running a timer to loop through a time-line based vector animation to draw each frame as a bitmap and store it within an array as bitmap data for later use. However, I have discovered that attempting to cache a large number of vector frames as bitmaps within an array at runtime on any mobile device is utterly impractical. A single unit model with 300 unique animation frames at 100×100 pixels might take 15-20 seconds for a mobile device to cache. A game with twenty plus models would take 5+ minutes to load, and not even the most diehard players will be willing to wait that long.

Sprites

  • Write some method that splits your bitmap sheet image to single frames with copyPixels and store them to Vector. (and dispose the sheet).

  • Next start extending Bitmap class where you pass your vector and create MovieClip style methods like gotoAndPlay() and gotoAndStop in which you then assign correct BitmapData to your extended Bitmap depending from the function.

  • Also notice that events are extremely slow so you might want to push all your currently used display objects to some array and move/animate them on single Event.ENTER_FRAME outside your extended Bitmap.

  • Since Bitmap can’t use mouse events you also need to add that functionality to your extended bitmap class. Simply use hitTestPoint to determine if mouse was over the bitmap when clicked or dragged.

  • And you may also want to use object pooling (if you’re doing more fast paced games) since creating and removing instances on the fly is also sadly slow.

Text

  • Flash Player 10 and AIR 1.5 introduced a powerful new text engine, the Adobe Flash Text Engine (FTE), that conserves system memory. However, the FTE is a low-level API that requires an additional ActionScript 3.0 layer on top of it, provided in the flash.text.engine package.

  • For read-only text, it’s best to use the Flash Text Engine, which offers low memory usage and better rendering. For input text, TextField objects are a better choice, because less ActionScript code is required to create typical behaviors, such as input handling and word-wrap.

  • In GPU rendering mode, text fields are not always moved to a visible location when the virtual keyboard opens. To ensure that your text field is visible while the user enters text, do one of the following. Place the text field in the top half of the screen or move it to the top half of the screen when it receives focus.

Video

When encoding your video thing of the final endpoint. Most devices have a native codec (usually H.264) that will help improve decompression and playback on the hardware level rather than the software level. Make sure to target these native codecs and see the performance and battery life improvement.

Ant build script to help you compile to iOS AIR 2.6.

Yesterday Adobe announced AIR 2.6, which allows you to compile to iOS and is really fast.

Before you start, I recommend you to read (or at least bookmark) these articles:

John Lidquist wrote a brilliant batch script generator which helped me to start: AIR4AIR. Thanks John!

You definitely should to read John’s article Top 10 AIR 2.6 for iOS Questions.

It is somewhat painful to solve initial compilation steps, so I hope my Ant script will help you get through. Basically it allows you to compile both desktop AIR and an iPhone4 (retina) IPA file. All you need is an iOS developer certificate and provisioning profile. The script then creates the rest (folder structure, desktop AIR certificate). But please take a look at the source and edit it as needed – e.g. there’s only iphone-retina descriptor and settings, but it should be pretty straight-forward to add what you need.

Ant script

<?xml version="1.0" encoding="UTF-8"?>

<!--
  ~ Copyright (c) 2011 Vaclav Vancura (http://vancura.org)
  -->

<project default="1. Create directories" name="Test AIR 2.6 Ant script">
<!-- Edit me: Ant script name -->

    <tstamp>
        <format property="timestamp" pattern="yyyy-MM-dd_HH-mm-ss" />
    </tstamp>

    <!-- FLEX SDK configuration -->
    <property name="flex_sdk_dir" value="/Developer/SDKs/FlexSDK41" />
    <!-- Edit me: path to your Flex SDK with AIR 2.6 overlaid -->
    <property name="mxmlc_path" value="${flex_sdk_dir}/lib/mxmlc.jar" />
    <property name="adt_path" value="${flex_sdk_dir}/lib/adt.jar" />

    <!-- Application configuration -->
    <property name="app_name" value="Test Application" />
    <!-- Edit me: Name of the app -->
    <property name="main_source" value="src/Main.as" />
    <!-- Edit me: Main bootstrap class -->
    <property name="debug_dir" value="bin" />
    <property name="assets_dir" value="assets" />
    <property name="certificates_dir" value="certificates" />
    <property name="deploy_dir" value="deploy" />
    <property name="libs_dir" value="libs" />
    <property name="swf_filename" value="${debug_dir}/test.swf" />
    <!-- Edit me: compiled SWF filename -->

    <!-- Desktop certificate configuration -->
    <property name="desktop_certificate_org_name" value="Your Name" />
    <!-- Edit me: your name, probably not Your Name -->
    <property name="desktop_certicitace_org_unit" value="" />
    <!-- Edit me: organization unit -->
    <property name="desktop_certificate_name" value="your-name" />
    <!-- Edit me: your name (this should be a filename safe name) -->
    <property name="desktop_certificate_password" value="password" />
    <!-- Edit me: your certificate password -->
    <property name="desktop_certificate_country_code" value="CZ" />
    <!-- Edit me: country code -->
    <property name="desktop_certificate_key_type" value="2048-RSA" />

    <!-- Desktop configuration -->
    <property name="desktop_certificate_filename" value="${certificates_dir}/${desktop_certificate_name}.pfx" />
    <property name="desktop_air_filename" value="${deploy_dir}/${app_name}_${timestamp}.air" />
    <property name="desktop_descriptor" value="${assets_dir}/desktop-app.xml" />

    <!-- iOS configuration -->
    <property name="ios_target" value="ipa-test" />
    <!--
        Edit me: choose one from:
            * ipa-test - Quick publishing for device testing
            * ipa-debug - Quick publishing for device debugging
            * ipa-ad-hoc - Deployment - Ad hoc
            * ipa-app-store - Deployment - Apple App Store
    -->

    <property name="ios_provisioning_profile_path" value="/Users/Test/Library/MobileDevice/Provisioning Profiles/your-provisioning-profile.mobileprovision" />
    <!-- Edit me: your provisioning profile path -->
    <property name="ios_certificate_filename" value="/Users/Test/Documents/iphone-developer.p12" />
    <!-- Edit me: your iphone developer certificate, see http://j.mp/convert-profiles -->
    <property name="ios_certificate_password" value="password" /> <!-- Edit me: your certificate password -->
    <property name="ios_ipa_filename" value="${deploy_dir}/${app_name}_${timestamp}.ipa" />
    <property name="ios_iphone_retina_descriptor" value="${assets_dir}/iphone-retina-app.xml" />
    <!-- Add more descriptor files here -->


    <!-- Create required directories -->
    <target name="1. Create directories">
        <mkdir dir="${assets_dir}" />
        <mkdir dir="${deploy_dir}" />
        <mkdir dir="${debug_dir}" />
        <mkdir dir="${libs_dir}" />
        <mkdir dir="${certificates_dir}" />
    </target>


    <!-- Creating a digital ID certificate -->
    <target name="2. Create temporary desktop certificate">
        <java jar="${adt_path}" fork="true">
            <arg line="-certificate" />
            <arg line="-cn ${desktop_certificate_name}" />
            <arg line="-ou '${desktop_certicitace_org_unit}'" />
            <arg line="-o '${desktop_certificate_org_name}'" />
            <arg line="-c '${desktop_certificate_country_code}' ${desktop_certificate_key_type} ${certificates_dir}/${desktop_certificate_name}.pfx ${desktop_certificate_password}" />
        </java>
    </target>


    <!-- Compile SWF to build directory for desktop packaging -->
    <target name="3. Compile" depends="1. Create directories">
        <java jar="${mxmlc_path}" fork="true" failonerror="true">
            <arg value="-debug=false" />
            <arg value="-optimize=true" />
            <arg value="+flexlib=${flex_sdk_dir}/frameworks" />
            <arg value="+configname=air" />
            <arg value="-file-specs=${main_source}" />
            <arg value="-output=${swf_filename}" />
            <arg value="-l+=${libs_dir}" />
        </java>
    </target>


    <!-- Packaging the application to an air-file in the publish directory -->
    <target name="Package for desktop" depends="3. Compile">
        <java jar="${adt_path}" fork="true" failonerror="true">
            <arg line="-package" />
            <arg line="-storetype pkcs12" />
            <arg line="-keystore '${desktop_certificate_filename}'" />
            <arg line="-storepass '${desktop_certificate_password}'" />
            <arg line="'${desktop_air_filename}'" />
            <arg line="'${desktop_descriptor}'" />

            <!-- Add folders to be bundled in the AIR file here -->
            <arg line="'${swf_filename}'" />
            <arg line="'${assets_dir}/icons/icon16.png'" />
            <arg line="'${assets_dir}/icons/icon32.png'" />
            <arg line="'${assets_dir}/icons/icon48.png'" />
            <arg line="'${assets_dir}/icons/icon128.png'" />
        </java>
    </target>


    <!-- Compile SWF to build directory for iOS packaging -->
    <target name="Package for iOS (iPhone retina)" depends="3. Compile">
        <java jar="${adt_path}" fork="true" failonerror="true">
            <arg line="-package" />
            <arg line="-target ${ios_target}" />
            <arg line="-storetype pkcs12" />
            <arg line="-keystore '${ios_certificate_filename}'" />
            <arg line="-storepass '${ios_certificate_password}'" />
            <arg line="-provisioning-profile '${ios_provisioning_profile_path}'" />
            <arg line="'${ios_ipa_filename}'" />
            <arg line="'${ios_iphone_retina_descriptor}'" />

            <!-- Add folders to be bundled in the AIR file here -->
            <arg line="'${swf_filename}'" />
            <arg line="'${assets_dir}/icons/icon29.png'" />
            <arg line="'${assets_dir}/icons/icon48.png'" />
            <arg line="'${assets_dir}/icons/icon57.png'" />
            <arg line="'${assets_dir}/icons/icon72.png'" />
            <arg line="'${assets_dir}/icons/icon512.png'" />
        </java>
    </target>


</project>

Gisted here: https://gist.github.com/882808

Download

You may need to download all files including example descriptors, if you don’t have it yet. Here we go.

Moderatrix: Fast User Interface Widgets Without Flex.

In this series of articles I’d like to invite you to meet the library I’ve been working on during last few years: Moderatrix (with a small help from other support libraries).

It started long before I joined Falanxia, but Moderatrix made a good leap lately: it was heavily used in our recently launched Bzoonk Bar game.

So what’s going on here? Basically all you need to see is in the video screencast below – all the effects, animation and so on (it was used in other projects as well, but let’s stick with Bzoonk Bar). There’s a lot more under the hood I’d like to talk about.

You may ask why I didn’t use Flex (or any other user interface library). But first you need to know the requirements I needed to meet.

Everything started when I was in Cyprus for my one year trip to Sideshow. I just moved from AS2 to AS3 and I really needed a strong library to allow me fast prototyping, while not using Flash (you may know Flash IDE is nearly unusable on OSX).

I got to create a feature-packed video player for Involver, and there was a huge list of requirements I had to satisfy:

  • fast and easy GUI skinning and rendering,
  • skins should be built and used without rebuilding of the player core SWF file,
  • so the premium clients could build their own skins – without Flash, just using regular graphics app and a text editor,
  • color theming of the existing skins for regular users and not-so-premium clients,
  • advanced 3D animation.

The list was somewhat longer, you can check it out on my Involver portfolio page, but I think it’s now clear the requirements were pretty tough.

Actually it was even a bit worse: there were some other challenges I also needed to solve: data and processor consumption should be minimal.

So now you know why Flex was not an option for me:

  • Flex apps are usually pretty large (and the Involver player had to be compact; it has to load fast, start fast and act fast),
  • they are usually a bit sluggish, honestly,
  • it’s not really easy to deploy pixel-perfect graphics in Flex (I got a feeling this could be a reason for a flame war),
  • it’s not really easy to add animation, Flex components are too slow to animate smoothly,
  • in 2007 it was difficult to skin a Flex component, and if you needed a runtime skin, you needed Flash or Flex to build one,
  • no 3D possible – we needed to animate UI containers in 3D and it’s really not possible in Flex even now, few years later – Flex simply doesn’t think you’d ever need to put a subcontainer in a parent container (which may be rotated), but puts it on the stage again

I tried a few user interface libraries available at the time, but nothing was really useful for my scenario. So I decided to build my very own system. It started as a part of my vancura-as3-libs package, but when I started to work at Falanxia, I refactored the libraries into Falanxia suitable ones. And now the libraries are open again – so you can use it right now.

This series will show you how to use them. If you’re interested, please be sure to follow me on Twitter, so you know when a new article is out.

Bzoonk Bar
As a sequel to the Bzoonk game mentioned below the Falanxia team comes with a new social game Bzoonk Bar.
Year:
2010
Client:
Falanxia
See it live at apps.facebook.com