Saturday May 2, 2009

Learning Flash with haXe

A short tutorial to open source Flash

By Colin Lieberman

Contents

There's a lot of material in this article, so here's some contents to help you skip around.

The Story so Far...

My good friend of nearly two decades and workmate at Yahoo!, Robert, asked me about doing a project to generate and animate 18 piece burr puzzles.

This is a cool extension of a project we started in the mid ’90s in a dimly lit student apartment at the University of Oklahoma (the now-bulldozed Parkview, for those who might care).

The idea is to animate the puzzles using Flash. I’ve been toying with the idea of learning Flash for a long time, and now I have an excuse.

Flash is a vector graphics platform, and its programming language is ActionScript, which itself is an extension of ECMAScript. This makes it a cousin of Javascript, which if you’ve ever seen Douglas Crockford’s lectures you know is an amazing language.

Most people, I gather, use Adobe’s Flash CS Professional IDE to create Flash projects. I have a hard time justifying $700 for an IDE, so I thought I try the open source route.

For a compiler, I’m using haXe, which is not an ActionScript compiler, but is its own language which closely resembles ActionScript and talks to the Flash API.

I picked up Rich Shupe and Zevan Rosser’s Learning ActionScript 3.0 from O’Reilly. It’s a good learning book, but it assumes that you’re using Adobe’s $700 Flash IDE. They expect you to drag and drop UI features into your project via the IDE, and I’ve spent a lot of time figuring out how to do this without.

This article is about these and other hurdles I’ve overcome learning about creating Flash applications using the haXe language. I hope it helps others move more rapidly over the learning curve.

Acknowledgements

I owe thanks to the archived messages at motion-twin.com and nabble.com as well to haXe author Nicholas Cannasse, who generously shares his time and knowledge on the haXe mailing list. Thank you Nicholas, both for the haXe language, and your help.

The Sample Project

We’ll work through a sample project that I’ve adapted from Chapter 3 of the Shupe and Rosser book: implementing some simple buttons to manipulate a small box.

This project will get us through most features of the haXe language, as well as some key parts of the Flash API, and should serve as a useful base for further learning.

Getting Set Up

From here on out things get technical. I’m using a newish MacBook, but most everything takes place in the terminal, which is built on BSD Unix. All my shell scripts and whatnot below should work fine in any Linux.

A note about Windows

If you’re using Windows, most of the commands and scripts (the files with a .sh extension) I show will be useless to you, but the information on haXe and Flash should translate. When explaining shell commands, I use the $ character to indicate a bash prompt. This is the same as C:\> in DOS.

Using haXe

You need three things to get started with haXe:

  1. the haXe compiler
  2. swfmill
  3. a text editor.

You probably already have a favorite text editor. Mine is vim. The haXe.org IDE and Editor Plugins page offers several haXe syntax files. They’ve got syntax files for vim, emacs, and a number of other editors. I contributed the alternative haxe.vim syntax file, which is a modification of the first haXe syntax file listed.

The haXe compiler is available on the haXe.org download page.

swfmill is a tool that that turns resource files, such as images, into a format that you can use in Flash. There are Mac and Windows binaries for download, as well as C++ source. I have no experience compiling swfmill for Linux.

swfmill takes an XML file that associates resource files with identifiers (which become class names in haXe). Its output is a .swf file, which your project swf will link to during compilation. More on this later.

Reference Material

The documentation on haXe.org is very good. If you have previous programming experience, you should have no problem with haXe.

Before we get started, have a look at the haXe language reference, Getting Started with haXe/Flash, and Using haXe with Flash9 for some background info.

If you absolutely must skip those excellent articles, at least know what this is:

flash.Lib.current.addChild(obj)

It’s a call to the Flash API to attach object obj to the stage. It’s the glue between our source code objects and the swf’s output.

The Flash API

The Flash API is large. haxe.org has a tree representation of the Flash API structure, making it very easy to browse objects and see what’s there. Note — use the “flash9” tree, not the “flash” tree, unless you are intentionally restricting yourself to to support only very old Flash players.

For a comprehensive API reference, there’s no substitute for the Adobe ActionScript 3.0 Language and Components reference. I’ll be linking to this whenever I reference a Flash API component in the code examples below.

Project Files

All the files described in this article, as well the icons I’ve used, are available in a source code archive for download (.tar.gz). Use them to follow along in your favorite editor.

The icons files in the archive originated at freeiconsweb.com. The licensing information is vague on the subject of noncommercial use. I’m going to assume I’m OK to include these icons with the project source code, unless I hear otherwise.

The Library

In this section, I’ll show you how to set up a resource library to use with your projects, and give you some bash scripts that will help you use swfmill efficiently. But first, we need buttons.

I downloaded these arrows (look for the tiny “download” link at the bottom of the page) to use with this sample project. You could use these, or your own. We need 12 buttons total: an up, down, and hover image for each of up, down, left, and right arrows. If you want to skip the hover states, that’s fine, but you’ll have to modify the code yourself.

If you don’t want to make your own icons, all the icons I used (scaled down to a reasonable size) are available in the project source files archive.

Now we’ll use swfmill to make a file called library.swf that we can use in our haXe project.

swfmill usage is

$ swfmill <command> <input_source> <output_destination>

The only swfmill command we’ll use is “simple”. Input will be an xml file, and output will be our library.swf file.

The XML looks something like this:

<?xml version="1.0" encoding="utf-8"?>
<movie version="9">
        <background color="#ffffff"/>
        <frame>
                <library>
                        <bitmap id="Blue_down_png" import="library/blue_down.png"/>
                        <bitmap id="Blue_left_png" import="library/blue_left.png"/>
                        <bitmap id="Blue_right_png" import="library/blue_right.png"/>
                        <bitmap id="Blue_up_png" import="library/blue_up.png"/>
                        <bitmap id="Green_down_png" import="library/green_down.png"/>
                        <bitmap id="Green_left_png" import="library/green_left.png"/>
                        <bitmap id="Green_right_png" import="library/green_right.png"/>
                        <bitmap id="Green_up_png" import="library/green_up.png"/>
                        <bitmap id="Yellow_down_png" import="library/yellow_down.png"/>
                        <bitmap id="Yellow_left_png" import="library/yellow_left.png"/>
                        <bitmap id="Yellow_right_png" import="library/yellow_right.png"/>
                        <bitmap id="Yellow_up_png" import="library/yellow_up.png"/>
                </library>
        </frame>
</movie>

Note the id attribute of each bitmap node: These ids are important. They must be unique, and will be used as the class names for your images in haXe.

There are lots of types of files that could go into the library - jpg images, other swf files, font files.

swfml_gen.sh

I’m lazy and unwilling to type an xml file for every project — to say nothing of editing that file whenever I change an image’s name. So here’s a script, swfml_gen.sh that generates the xml for you:

#!/bin/bash
if [[ -z $1 ]]; then
    echo "first arg is path of resource dir"
    exit 1
fi

#escape * so it doesn't try to sub with file names in local dir
gfx=`find $1 -name \*.png -or -name \*.jpg`
swfs=`find $1 -name \*.swf`
fonts=`find $1 -name \*.ttf`

if [[ -z $gfx && -z $fonts && -z $swfs ]]; then
    echo "no matching files found in $1"
    exit 1
fi

#function to upcase first letter
function up_first
{
    #TODO: take an arg instead of only reading from pipe
    while read line; do
        first=`echo $line | sed -e 's/^\(.\).*$/\1/'`
        first_up=`echo $first | tr '[a-z]' '[A-Z]'`
        echo `echo $line | sed -e "s/^$first/$first_up/"`
    done
}

# escape slashes is dir paths for use in regexes
function escape_dir
{
    if [[ -z $1 ]]; then
        while read line; do
            echo `echo $line | sed -e 's/\//\\\\\//g'`
        done;
    else
        echo `echo $1 | sed -e 's/\//\\\\\//g'`
    fi
}

# generate unique id by replace / and . in filenames with _
# it's unique because it's namespaced with directory names
function make_id
{
    #replace / with _ in file names so i can use base as regex
    #this will still have the trailing / from find, so get rid of it
    base_name=`echo "$1/" | sed -e "s/\//\_/g"`
    file_name=`echo "$2" | sed -e "s/\//\_/g"`
    
    #always upcase the first char of the file name
    echo "$file_name" | sed -e "s/$base_name//" | sed -e "s/\./\_/g" | up_first 
}

#get a file path with the extra / removed after the dir that we did find on
function clean_base_dir
{
    #remove trailing / and escape for use in regex
    clean_base=`echo "$1" | sed -e "s/\/$//" | escape_dir`
    #escape original for use in regex
    old_base=`escape_dir $1`
    echo "$2" | sed -e "s/$old_base/$clean_base/"
}

#make .hx class file for each resource
function write_class_file
{
    # $1 is Class name (id) and $2 is file type and $3 is file name
    if [[ $2 == "swf" ]]; then
        echo "class $1 extends MovieClip { public function new() { super(); } }" >> $3
    else #bitmap files
        echo "class $1 extends Bitmap { public function new() { super(); } }" >> $3
    fi
}

echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
echo "<movie version=\"9\">"
echo -e "\t<background color=\"#ffffff\"/>"
echo -e "\t<frame>"
echo -e "\t\t<library>"

#remove old class file
class_file="LibraryClasses.hx"
if [[ -f $class_file ]]; then
    rm $class_file
fi

#put default info in class file
if [[ ! -z $gfx ]]; then
    echo "import flash.display.Bitmap;" >> $class_file
fi

if [[ ! -z $swfs ]]; then
    echo "import flash.display.MovieClip;" >> $class_file
fi

#add a blank line after includes
echo "" >> $class_file

for file in $gfx; do
    #remove the last / that find adds to the base dir
    path=`clean_base_dir $1 $file`
    id=`make_id $1 $file`
    write_class_file $id "bmp" $class_file
    echo -e "\t\t\t<bitmap id=\"$id\" import=\"$path\"/>"
done

for swf in $swfs; do
    path=`clean_base_dir $1 $swf`
    id=`make_id $1 $file`
    write_class_file $id "swf" $class_file
    echo -e "\t\t\t<clip id=\"$id\" import=\"path\"/>"
done

echo -e "\t\t</library>"

for font_file in $fonts; do
    path=`clean_base_dir $1 $font_file`
    id=`make_id $1 $font_file`
    #TODO: how do fonts go in the class file?
    #TODO: figure out if i need a more complete set of glyphs
    echo -e "\t\t<font id=\"$id\" import=\"$path\" glyphs=\"abcdefghijklmnopqrstuvwxyz\"/>"
done

echo -e "\t</frame>"
echo "</movie>"

Usage is:

$ swfml_gen.sh path/to/resources

where path/to/resources is the path to the directory where all your resource images live. XML is output to stdout, but a file called LibraryClasses.hx is also created in the local directory. This is a haXe file containing class definitions for all the resources in your library.

swf_lib.sh

Here’s another script, swf_lib.sh, that runs the first script, and pipes the output to swfmill to make the final library.swf (note that in this script, swfml_gen.sh is treated like a command - I usually make a symlink in /usr/bin to scripts I use a lot. You’ll want to either do this or modify the script to point to your local copy of swfml_gen).

#!/bin/bash
if [[ -z $1 ]]; then
    echo "First arg is resource dir"
    exit 1;
fi

if [[ -z $2 ]]; then
    echo "Second arg is output swf"
    exit 1;
fi

swfml_gen.sh $1 > ${2}.xml
cat ${2}.xml | swfmill simple stdin $2

To use this script, I run

$ swf_lib.sh path/to/resources library.swf

where library.swf is the output file name. Call yours whatever you like.

All you have to do is set up a directory that contains all the images (I’m using PNGs in this example) you want to use, and run swf_lib.sh. You’ll get a library swf of all your image resources, and well as some xml you can refer to, and the LibraryClasses.hx file that you’ll use to access the library via the haXe language.

Writing haXe

It feels like we’re working a little backwards here. This is already getting long and we haven’t written a single line of code yet. Instead, we’re setting up all the project infrastructure that will allow you to work quickly very soon here. Trust me.

compile.hxml

When you compile your haXe project, instead of using a standard Makefile, haXe uses a configuration file in its own hxml format.

The project is compiled by issuing the command

$ haxe compile.hxml

where compile.hxml is the name of my hxml configuration.

To get a complete list of switches available in the configuration file, you can issue a haxe command with no arguments. Hopefully you won’t need to, because the samples here will be sufficient for you.

Here’s what the compile.hxml file looks like for this project:

# set the output file as flash v 9
-swf9 button_etude.swf

# set swf version to 9
# don't know if that's necessary given the above swf9 switch
-swf-version 9

# set dimensions of 700 wide x 400 tall
# 40 frames per second
# background color of #FFFFFF
-swf-header 700:400:40:FFFFFF

# identify library.swf as my library file
-swf-lib library.swf

# identify main method in ButtonEtude class
# which lives in ButtonEtude.hx
-main ButtonEtude

Now that we’ve identified where everything lives and how to compile the project, let’s write some code.

main

Here’s a simple haXe program:

class HelloWorld
{
    public static function main()
    {
        trace("Hello World!");
    }
}

In order to run it, the file would need to be named helloWorld.hx, and the main line in compile.hxml would need to be -main HelloWorld. (My only gripe about haXe is the tight relationship between file names and class names.)

The key thing to take from this is that main is a static function that takes no parameters and has no return value. (trace is a Flash API call).

Étude for Buttons

For this project, we’re creating some buttons to manipulate the properties of a small square.

Let’s start with an outline for our project (“ButtonEtude”) class, and its main method.

Note — this is just a skeleton to get started, this code will not compile until all that pieces are put together, which does’t happen until the end of this article. If you’re in a hurry, you can download the project source files and compile them.

import flash.display.MovieClip;

class ButtonEtude
{
    private static var _button_factory : ArrowButtonFactory;

    public static function main()
    {   
        ButtonEtude._button_factory = new ArrowButtonFactory();

        var target_box : MovieClip = ButtonEtude._makeTargetBox();

        /* labels go under clickable things */
        ButtonEtude._makeLabels();

        /* initialize button events with target and make the buttons */
        var button_events : ButtonEvents = ButtonEvents.getInstance(target_box);
        ButtonEtude._makeButtons(button_events);
    }
}

In main, I’m setting up an ArrowButtonFactory object for making my buttons, making the target box (the guy the buttons will be manipulating), and setting up buttons and their labels.

Watch Out

Some of my usage here in non-standard, so I should point that out.

First, I’ve adopted the practice of prefixing private members and methods with _, as a kind of reminder to myself that they’re private. Feel free not to do this.

Second, class methods and members are can be referenced within class methods without additional reference to the class. Where I have ButtonEtude._makeTargetBox(); I could have simply _makeTargetBox(); instead. In PHP, for example, this would be self::_makeTargetBox();. I like including the extra reference to the class so that I know where to go looking for the code.

Import

The import flash.display.MovieClip line at the top of the file is like any other language’s include statement. It identifies a class in another file we want to use. In this case, because it’s a reference to a Flash API class, we don’t have to know where that file lives.

When we make classes that live in other files, haXe has some expectations for the file names and class names. If I want to include a class named Foo, I say

import Foo;

and haXe assumes it’s in a file called Foo.hx or foo.hx. If the file foo.hx is located at path/to/foo.hx, then I would do this:

import path.to.Foo;

where the directory separators become dots. This requires (I believe) all the directories to have lowercase names, though I could be wrong.

In the following files, you’ll see this line:

import LibraryClasses;

in any file that uses classes from the library. LibraryClasses.hx was created by the swfml_gen script above, and looks like this:

import flash.display.Bitmap;

class Blue_down_png extends Bitmap { public function new() { super(); } }
class Blue_left_png extends Bitmap { public function new() { super(); } }
class Blue_right_png extends Bitmap { public function new() { super(); } }
class Blue_up_png extends Bitmap { public function new() { super(); } }
class Green_down_png extends Bitmap { public function new() { super(); } }
class Green_left_png extends Bitmap { public function new() { super(); } }
class Green_right_png extends Bitmap { public function new() { super(); } }
class Green_up_png extends Bitmap { public function new() { super(); } }
class Yellow_down_png extends Bitmap { public function new() { super(); } }
class Yellow_left_png extends Bitmap { public function new() { super(); } }
class Yellow_right_png extends Bitmap { public function new() { super(); } }
class Yellow_up_png extends Bitmap { public function new() { super(); } }

The ArrowButtonFactory

We’re going to be making a lot of buttons in this project, and when you’re making a lot of a similar-but-not-identical objects, the Factory Pattern is frequently convenient. Note to purists: yes, I’m aware that this isn’t a textbook factory implementation — the caller specifies the kind of object he wants; the objects created are of the same type, varrying only in their properties — but I argue it qualifies because the part of the returned objects that differs is encapsulated in the factory, hidden from the caller.

Make new file in the same directory called ArrowButtonFactory.hx. Here’s the code:

import Button;

import LibraryClasses;

import flash.display.Bitmap;
import flash.display.BitmapData;

class ArrowButtonFactory
{
    private var _bitmap_up_up        : Bitmap;
    private var _bitmap_up_hover     : Bitmap;
    private var _bitmap_up_down      : Bitmap;
    
    private var _bitmap_down_up      : Bitmap;
    private var _bitmap_down_hover   : Bitmap;
    private var _bitmap_down_down    : Bitmap;
    
    private var _bitmap_left_up      : Bitmap;
    private var _bitmap_left_hover   : Bitmap;
    private var _bitmap_left_down    : Bitmap;
    
    private var _bitmap_right_up     : Bitmap;
    private var _bitmap_right_hover  : Bitmap;
    private var _bitmap_right_down   : Bitmap;

    private function _newButton(bitmap_up : BitmapData, bitmap_hover : BitmapData, bitmap_down : BitmapData, ?x : Int, ?y : Int)
    {
        if(x == null)
        {
            x = 0;
        }
        if(y == null)
        {
            y = 0;
        }

        var button = new Button(bitmap_up, bitmap_hover, bitmap_down, x, y);
        flash.Lib.current.addChild(button);

        return button;
    }

    public function newUpButton(?x : Int, ?y : Int) : Button
    {
        return this._newButton(this._bitmap_up_up.bitmapData, this._bitmap_up_hover.bitmapData, this._bitmap_up_down.bitmapData,  x,  y);
    }

    public function newDownButton(?x : Int, ?y : Int) : Button
    {
        return this._newButton(this._bitmap_down_up.bitmapData, this._bitmap_down_hover.bitmapData, this._bitmap_down_down.bitmapData,  x,  y);
    }

    public function newLeftButton(?x : Int, ?y : Int) : Button
    {
        return this._newButton(this._bitmap_left_up.bitmapData, this._bitmap_left_hover.bitmapData, this._bitmap_left_down.bitmapData,  x,  y);
    }

    public function newRightButton(?x : Int, ?y : Int) : Button
    {
        return this._newButton(this._bitmap_right_up.bitmapData, this._bitmap_right_hover.bitmapData, this._bitmap_right_down.bitmapData,  x,  y);
    }

    public function new()
    {
        /* Up state - Blue
         * Hover state - Yellow
         * Down state - Green
         */
        this._bitmap_up_up       = new Blue_up_png();
        this._bitmap_up_hover    = new Yellow_up_png();
        this._bitmap_up_down     = new Green_up_png();

        this._bitmap_down_up     = new Blue_down_png();
        this._bitmap_down_hover  = new Yellow_down_png();
        this._bitmap_down_down   = new Green_down_png();
        
        this._bitmap_left_up     = new Blue_left_png();
        this._bitmap_left_hover  = new Yellow_left_png();
        this._bitmap_left_down   = new Green_left_png();
        
        this._bitmap_right_up    = new Blue_right_png();
        this._bitmap_right_hover = new Yellow_right_png();
        this._bitmap_right_down  = new Green_right_png();
    }
}

Don’t worry about the Button class yet. That’s coming up soon. Bitmap and BitmapData are from the Flash API (but you guessed that already).

This classes uses the library.swf that we compiled with swfmill to access the images. If you’re using your own images, you’ll want to change the lines that assign the factory’s bitmap properties in the constructor so they use the classes identified in your library swf.

To use this factory, simply call one of the public methods, such as newUpButton, and it will return a Button object that represents an up-pointing arrow button.

The Button Class

The ArrowButtonFactory creates and returns Button classes. Here’s the code for your Button.hx file:

import flash.display.Sprite;
import flash.display.BitmapData;
import flash.events.MouseEvent;

class Button extends Sprite
{
    private var _bitmap_up    : BitmapData;
    private var _bitmap_hover : BitmapData;
    private var _bitmap_down  : BitmapData;

    public function new(bitmap_up : BitmapData, bitmap_hover : BitmapData, bitmap_down : BitmapData, ?x : Int, ?y : Int)
    {
        super();
        
        this._bitmap_up = bitmap_up;
        this._bitmap_hover = bitmap_hover;
        this._bitmap_down = bitmap_down;

        if(x != null)
        {
            this.x = x;
        }
        if(y != null)
        {
            this.y = y;
        }

        this.addEventListener(MouseEvent.MOUSE_OVER, _onHover);
        this.addEventListener(MouseEvent.MOUSE_OUT, _onBlur);
        this.addEventListener(MouseEvent.MOUSE_DOWN, _onDown);
        this.addEventListener(MouseEvent.MOUSE_UP, _onUp);
        this._draw(this._bitmap_up);
    }

    private function _draw ( bitmap : BitmapData )
    {
        this.graphics.beginBitmapFill(bitmap);
        this.graphics.drawRect(bitmap.rect.x, bitmap.rect.y, bitmap.rect.width, bitmap.rect.height);
        this.graphics.endFill();
    }

    private function _onHover( event : MouseEvent )
    {
        this._draw(this._bitmap_hover);
    }

    private function _onBlur( event : MouseEvent )
    {
        this._draw(this._bitmap_up);
    }

    private function _onDown( event : MouseEvent )
    {
        this._draw(this._bitmap_down);
    }

    private function _onUp( event : MouseEvent )
    {
        this._draw(this._bitmap_hover);
    }
}

The Button class extends Sprite, a class from the Flash API. This means that in addition to the properties we give it, it also includes all the properties and methods of a Flash Sprite object. If you need more info on class inheritance, pick up a good intro to Object Oriented Programming.

One of the properties of a Sprite is its Graphics member called graphics. The graphics property allows us to perform drawing functions on the Sprite.

A Button is essentially a Sprite wrapped up with three images (stored as Flash BitmapData objects) that it can switch among.

Note in the constructor the calls to addEventListener. If you’ve done any DOM scripting for the web, this should be very familiar. Buttons create themselves to listen for events from the mouse, such as being clicked or hovered over, and re-drawing themselves to reflect that state (note the import of flash.events.MouseEvent at the top of the file).

Give it Life

We now have a class for buttons, and a factory to make them. Before we look at our main ButtonEtude class again, we’re going to make one more helper class.

This ButtonEvents class contains the methods that power all the buttons we’re going to make. This design approach might seem overmuch for the small task we’ve laid out, but I think that it’s good practice to design even simple things so that they maintain healthy layers of abstraction. It keeps you ready for when projects get bigger.

Here’s ButtonEvents.hx:

import flash.display.MovieClip;
import flash.events.MouseEvent;

import Button;

class ButtonEvents
{
    private var _MOVE_X     : Int;
    private var _MOVE_Y     : Int;

    private var _SCALE      : Float;

    private var _ROTATE     : Int;

    private var _FADE       : Float;

    private var _target : MovieClip;
    private static var _instance : ButtonEvents;

    public static function getInstance(?target : MovieClip)
    {
        if( ButtonEvents._instance == null )
        {
            if(target == null)
            {
                //TODO: make these exceptions
                trace( "target MovieClip is required to create new instance" );
            }
            ButtonEvents._instance = new ButtonEvents(target);
        }
        else if ( target != null )
        {
            trace("calling getInstance with instance set but target passed");
        }
        
        return ButtonEvents._instance;
    }

    private function new(target : MovieClip)
    {
        this._MOVE_X    = 3;
        this._MOVE_Y    = 3;

        this._SCALE     = .1;

        this._ROTATE    = 10;

        this._FADE     = .1;

        this._target    = target;
    }

    private function _moveUp( e : MouseEvent )
    {
        this._target.y -= this._MOVE_Y;
    }

    private function _moveDown( e : MouseEvent )
    {
        this._target.y += this._MOVE_Y;
    }

    private function _moveLeft( e : MouseEvent )
    {
        this._target.x -= this._MOVE_X;
    }

    private function _moveRight( e : MouseEvent )
    {
        this._target.x += this._MOVE_X;
    }

    private function _scaleUp( e : MouseEvent )
    {
        this._target.scaleX += this._SCALE;
        this._target.scaleY += this._SCALE;
    }

    private function _scaleDown( e : MouseEvent )
    {
        this._target.scaleX -= this._SCALE;
        this._target.scaleY -= this._SCALE;
    }

    private function _rotateCounterClockwise( e : MouseEvent )
    {
        this._target.rotation -= this._ROTATE;
    }

    private function _rotateClockwise( e : MouseEvent )
    {
        this._target.rotation += this._ROTATE;
    }

    private function _fadeIn( e : MouseEvent )
    {
        this._target.alpha += this._FADE;
    }

    private function _fadeOut( e : MouseEvent )
    {
        this._target.alpha -= this._FADE;
    }
    
    /* public setters for events */
    public function setMoveUp( button : Button )
    {
        button.addEventListener(MouseEvent.MOUSE_DOWN, this._moveUp);
    }

    public function setMoveDown( button : Button )
    {
        button.addEventListener(MouseEvent.MOUSE_DOWN, this._moveDown);
    }

    public function setMoveLeft( button : Button )
    {
        button.addEventListener(MouseEvent.MOUSE_DOWN, this._moveLeft);
    }

    public function setMoveRight( button : Button )
    {
        button.addEventListener(MouseEvent.MOUSE_DOWN, this._moveRight);
    }

    public function setScaleUp( button : Button )
    {
        button.addEventListener(MouseEvent.MOUSE_DOWN, this._scaleUp);
    }

    public function setScaleDown( button : Button )
    {
        button.addEventListener(MouseEvent.MOUSE_DOWN, this._scaleDown);
    }

    public function setRotateLeft( button : Button )
    {
        button.addEventListener(MouseEvent.MOUSE_DOWN, this._rotateCounterClockwise);
    }

    public function setRotateRight( button : Button )
    {
        button.addEventListener(MouseEvent.MOUSE_DOWN, this._rotateClockwise);
    }

    public function setFadeUp( button : Button )
    {
        button.addEventListener(MouseEvent.MOUSE_DOWN, this._fadeIn);
    }

    public function setFadeDown( button : Button )
    {
        button.addEventListener(MouseEvent.MOUSE_DOWN, this._fadeOut);
    }
}

ButtonEvents is an example of the Singleton design pattern, because you only ever need one of it.

Wiring it Up

So now with the Button class, the ArrowButtonFactory class, and the ButtonEvents class, we have nearly all the elements that will go into the final project.

To flesh out our main ButtonEtude class, we’ll add the imports for these classes we’ve created, as wells as the code to give the buttons life.

Here’s the final ButtonEtude.hx file:

import flash.display.MovieClip;

import flash.text.TextField;
import flash.text.TextFormat;

import Button;
import ButtonEvents;
import ArrowButtonFactory;

class ButtonEtude
{
    private static var _button_factory : ArrowButtonFactory;
    
    private static function _makeTargetBox()
    {
        var box : MovieClip = new MovieClip();

        box.graphics.beginFill(0xFFFF00);
        box.graphics.drawRect(100,25,100,100);
        box.graphics.endFill();

        flash.Lib.current.addChild(box);
    
        return box;
    }

    private static function _stdText(text : String, ?x : Int, ?y : Int) : TextField
    {
        if( x == null )
        {
            x = 0;
        }
        if( y == null )
        {
            y = 0;
        }

        var text_field = new TextField();
        
        text_field.defaultTextFormat = ButtonEtude._getStdTextFormat();
        
        text_field.x = x;
        text_field.y = y;
        text_field.text = text;

        flash.Lib.current.addChild(text_field);
    
        return text_field;
    }

    private static function _makeButtons(button_events : ButtonEvents)
    {
        /** move buttons **/
        var button_move_up      = ButtonEtude._button_factory.newUpButton(48, 300);
        button_events.setMoveUp(button_move_up);
        
        var button_move_down    = ButtonEtude._button_factory.newDownButton(48, 360);
        button_events.setMoveDown(button_move_down);

        var button_move_left    = ButtonEtude._button_factory.newLeftButton(5, 330); 
        button_events.setMoveLeft(button_move_left);
        
        var button_move_right   = ButtonEtude._button_factory.newRightButton(90, 330);
        button_events.setMoveRight(button_move_right);

        /** scale buttons **/
        var button_scale_up     = ButtonEtude._button_factory.newUpButton(246, 300);
        button_events.setScaleUp(button_scale_up);

        var button_scale_down   = ButtonEtude._button_factory.newDownButton(246, 360);
        button_events.setScaleDown(button_scale_down);

        /** rotation buttons **/
        var button_rotate_left  = ButtonEtude._button_factory.newLeftButton(408, 330);
        button_events.setRotateLeft(button_rotate_left);

        var button_rotate_right = ButtonEtude._button_factory.newRightButton(488, 330);
        button_events.setRotateRight(button_rotate_right);
       
        /** fade buttons **/
        var button_fade_up      = ButtonEtude._button_factory.newUpButton(644, 300);
        button_events.setFadeUp(button_fade_up);

        var button_fade_down    = ButtonEtude._button_factory.newDownButton(644,360);
        button_events.setFadeDown(button_fade_down);
    }

    private static function _makeLabels()
    {
        var label_move      = ButtonEtude._stdText("move", 40, 337);
        var label_scale     = ButtonEtude._stdText("scale", 240, 337);
        var label_rotate    = ButtonEtude._stdText("rotate", 440, 337);
        var label_fade      = ButtonEtude._stdText("fade", 640, 337);
    }

    private static function _getStdTextFormat() : TextFormat
    {
        var text_format = new TextFormat();
        
        text_format.font = "Helvetica";
        text_format.size = 16;
    
        return text_format;
    }

    public static function main()
    {
        ButtonEtude._button_factory = new ArrowButtonFactory();
        
        var target_box : MovieClip = ButtonEtude._makeTargetBox();

        /* labels go under clickable things */
        ButtonEtude._makeLabels();

        /* initialize button events with target and make the buttons */
        var button_events : ButtonEvents = ButtonEvents.getInstance(target_box);
        ButtonEtude._makeButtons(button_events);
    }
}

Note the use of TextField and TextFormat objects from the Flash API for the button labels. I could probably make the stdTextFormat a member variable instead of creating a new one each time.

Note also that since main must be a static function, every method called by main must be static, as well as every member property. And if you skipped some of the intro sections above, read my note about my non-standard usage.

Showing it Off

The penultimate step is to compile the project.

$ haxe compile.hxml

That should create a swf called button_etude.swf (or whatever you used on the -swf9 flag in your compile.hxml).

If you get compiler errors on any of this, please leave a comment below so I can fix the error. However, you’ll likely be able to remedy the problem yourself in just a few minutes. haXe’s compiler errors are quite clear and well-written.

Lastly, create an HTML page to display the swf, and open it in your browser. Here’s some html:

<object width="700" height="400" align="center" id="button_etude">
    <param name="movie" value="test.swf"/>
    <param name="allowScriptAccess" value="always" />
    <param name="quality" value="high" />
    <param name="scale" value="noscale" />
    <param name="salign" value="lt" />
    <param name="bgcolor" value="#ffffff"/>
    <embed src="button_etude.swf"
        bgcolor="#ffffff"
        width="700"
        height="400"
        name="button_etude"
        quality="high"
        align="center"
        allowScriptAccess="always"
        type="application/x-shockwave-flash"
        pluginspage="http://www.macromedia.com/go/getflashplayer"
    />
</object>

Here’s what mine looks like:

What’s going on?

You’ll notice that the box seems to use the upper-left-hand corner of the swf object as its anchor for rotation and scaling. Fixing this in the code is left as a exercise for the reader. Hint: think about the box’s initial x and y values, and about relative positioning when drawing the rectangle.

The End

I hope that this tutorial has been useful to you, and gets you going using haXe to publish for Flash. Please email me, or leave a comment below, with any tips, questions, corrections, errors of omission, or anything else you feel might be helpful.

By way of further reading, please see the Reference Materials section earlier in this article, and get active in the haXe Community.

Comments:

  1. not so short a tutorial, but superb. Thank you very much.


    mark    May 5, 02:57 AM    #
  2. Where did you get the information on SWFML tags like ‘bitmap’? When visiting the SWFmill site or the OSFlash wiki, I haven’t seen any tags like that. They only use tags to turn PNGs and other embedded things into movie clips, which doesn’t make sense to me. Right now I’m trying to figure out a good way to compile a resource library and am leaning toward using Flex since I can’t get enough info on using SWFmill or SWFML.


    — Falmil    May 25, 05:47 PM    #
  3. @ Falmil – I found that doing web searches, and probably some just trying stuff to see what works. I agree that a DTD or some other clear SWFML documentation would be very helpful.


    colin    May 26, 07:33 AM    #
  4. On my Debian Linux box, I found I had to delete some extraneous files from the library subdirectory (which I assumed was intended to be used as the resources location). The “hidden” files such as “button_etude/library/._blue_left.png” are picked up by the find command and cause issues in the library.swf.xml file. I suppose I could have altered the arguments to the find commands, but…


    — Scott A. Thisse    Jul 28, 02:25 PM    #
  5. Taking some time in Haxe recently, thank you for this nice tutorial.

    In a team production environment, I’m wondering how graphists could produce 2D keyframe animations (animated sprites) in a ready to use flash format (MovieClip) without using the flash authoring tool.

    It does not seem reasonable to rebuild all animation content by constructing keyframes from swfmill jpg asset, What to you think about that ?


    Philippe    Sep 10, 11:31 PM    #
  6. Nice posts there – thank’s for the interesting information.


    Oleg    Sep 30, 04:57 PM    #
  7. Thanks for the lesson! Will the continuation of lessons?
    When used in Linux have met several problems.
    :) I had to learn bash …
    Now I have the audacity to propose changes.

    1. in the file “swf_gen.sh” – line 13: command not found
    Line 13 – ” swfml_gen.sh ” replace to ” ./swfml_gen.sh ”

    2. in the file ” swfml_gen.sh ” – ignore the old and hidden files.
    Lines 8 9 10 – ” -name \*. ” replace to ” -name [^~.]\*. ”

    3. in the file “swfml_gen.sh” – ” library/ ” and ” library ” is not the same. Line 97 – add ” cl_path=`echo $1 | sed -e ’s/\/$//’` ”.
    Line 100 101 108 109 117 118 – ” $1 ” replace to ” $cl_path ”.

    Unfortunately I do not know how this will affect the work in the Mac.
    Thanks again for a wonderful lesson.

    p.s. Sorry for my(google translator) English.
    — Sergey    Apr 8, 09:13 PM    #
  Textile Help