Archive for the ‘Flash+ActionScript’ Category

getDefinitionByName error: “Variable … is not defined”

Sunday, March 14th, 2010

I wrote this post mainly as a reminder for myself, but it might be useful for anybody dealing with the same issue. In ActionScript3 you can get a class by its name like this:

import flash.utils.getDefinitionByName;
...
var myClass = getDefinitionByName("MyClass") as Class;

When you only use the name of the class, you will get an error: “Variable … is not defined”, because the Flash Player can’t find it at run time. There are several ways to make it work.

1. You can import the class and create a dummy variable. The dummy is needed, otherwise the compiler won’t add the class.

import domain.project.MyClass;
...
private var _dummyClass:MyClass;

2. Instead of a variable you can also use the following notation.

import domain.project.MyClass;
...
MyClass;

3. You can also use the Frame metadata tag:

import domain.project.MyClass;
...
[Frame(extraClass="MyClass")]

All these solutions work, but you need to specify the classes you want to use up front.

4. Far more easy, elegant and flexible is to include the path of the class when calling getDefinitionByName. You won’t need to import anything and you don’t have to create dummy vars or use extra metadata tags.

var myClass = getDefinitionByName("domain.project.MyClass") as Class;

Ant script to build Flex Library project

Friday, March 5th, 2010

Ever wondered how to create an Ant build script for a Flex Library project? I did, and although it’s very easy (when you know how) it’s very poorly documented. This is the solution Dusan and I found after some trail and error. Save this script as build.xml in the root of your project, run ant and your project will be build.

<?xml version="1.0" encoding="utf-8"?>
<project name="floorplanner_as3_core" basedir="." default="build">
  <property name="FLEX_HOME" value="your-flex-sdk-dir-here"/>
  <property name="src.dir" value="src"/>
  <property name="release.dir" value="bin"/>
 
  <taskdef resource="flexTasks.tasks" classpath="${FLEX_HOME}/ant/lib/flexTasks.jar"/> 
 
  <target name="compile">
    <compc output="bin/your-library-project.swc">
      <source-path path-element="${src.dir}" />
      <include-sources dir="${src.dir}" includes="*"/>	
    </compc>
  </target>
 
  <target name="clean">
    <delete>
      <fileset dir="${release.dir}" includes="your-library-project.swc"/>
    </delete>
  </target>
 
  <target name="build" depends="clean, compile" />
 
</project>

Flex Formatter

Wednesday, February 24th, 2010

I use Eclipse 3.4.2 in combination with Flex Builder 3.0. An important feature that is missing in FB3 is a formatter. I’m very glad I found this project on SourceForge by Ernest Pasour: Flex Formatter.

Installing it is easy. Download it, unzip it, move the files to the plugins folder of Eclipse and restart Eclipse. You now see a couple of new icons in your Flex perspective, with the most important being the “Format Flex code” icon. The plugin has a huge number of settings which can be adjusted in the general Properties menu of Eclipse by selecting Flex Formatter.

No more excuses for messy Flex / AS3 code!

Flex SDK 4.0.0.13875

Thursday, February 4th, 2010

On Friday 29 January Adobe released a new ’stable’ version of the Flex SDK, version 4.0.0.13875. One of the nice things about it was the addition of the Flex 4 TitleWindow. Peter deHaan uses the new TitleWindow in one of his articles on his excellent blogConstraining the movement on a Spark TitleWindow container in Flex 4

When using the new SDK we encountered quite an annoying issue. Since Flex 4 is still in beta, we use Flex Builder 3 in combination with a Flex 4 SDK. The problem was that after upgrading to the latest SDK, default flash and mx classes weren’t recognised anymore and couldn’t be imported. When added manually they gave an error.

Luckily Tim found a workaround. Go to your project’s Properties, click on Flex Build Path and  select Library path. First open up the Flex 4.0.0.13875, select the playerglobal.swc and remove it. Then click on Add SWC… and select the playerglobal.swc from the SDK (in my case /Developer/SDKs/flex_sdk_4.0.0.13875/frameworks/libs/player/10.0/playerglobal.swc).

I hope this little workaround is useful for everybody out there working with the latest Flex SDK.

PS. the workaround only seems to work for Flex/AS3 projects, but not for Library projects…

Introducing ASBlender

Monday, January 25th, 2010

As all 3D modelers probably know: exporting models from 3D applications and use them in Flash (Papervision3D etc.) can be frustrating. Stuff like animations, matrices, etc. sometimes don’t work as expected. So I thought: why not read the native file format of a 3D app instead? That way we get access to the native data as used by a 3D app. Not some ‘twisted’ data as presented by exporters.

Don’t get me wrong: most exporters of course do their job very well, but… exporters ‘interpret’. In other words: exporters think in my place. Often exporter are ‘right’, but sometimes they go wrong (or present data in some awkward form). Then it would be cool to have control over the data yourself.

The workflow becomes very simple. We don’t have to export anymore, Flash could simply read the 3D app’s latest saved file. We can interpret the 3D app’s data as we wish. I like that ;-) . On the bad side: native files contains lots of data we probably don’t need, like the 3D app’s viewport size etc. Then again: how cool is that?

Enter ASBlender, a library I slapped together in a few days to read Blender’s .blend file format and parse it to AS3. Blender is a very cool (open source, free!) 3D application which can compete with MAX, Maya, Cinema4D etc. The Blender UI is kind of weird when you first see it, but after a – admittedly – steep learning curve, it rocks.

Blender 2.5 - Eclipse - FDT

More info, code and a wiki is available at github.

The code is very basic and can be improved for sure, so comments, criticism and suggestions are more then welcome.

Have fun!

Build ActionScript3 projects with TextMate in 5 easy steps

Saturday, September 5th, 2009

The text editor of choice of our Rails team is TextMate. Our Flash team is a bit divided between Eclipse+FDT and FlexBuilder. I wanted to see if TextMate is a good alternative for building Flash/ActionScript projects.

There are a couple of good sources on using TexMate for ActionScript projects, but it’s a little fragmented and sometimes outdated. I found pixelate’s blog post and Simon’s blog very useful.

1. TextMate

First of all, you need have MacroMates’ TexMate of course. If you don’t already own a copy, you can download a 30-day-trial.

2. ActionScript3 bundle

Simon Gregory did some fine work by creating a TextMate bundle for ActionScript3. The most important things that the bundle handles are auto-completion and auto-import. You can download it directly from his blog, or fetch it with Git or SVN.

To install via Subversion:

export LC_CTYPE=en_US.UTF-8
cd ~/"Library/Application Support/TextMate/Bundles/"
svn co http://svn.textmate.org/trunk/Review/Bundles/ActionScript\ 3.tmbundle
osascript -e 'tell app "TextMate" to reload bundles'

To install via Git:

cd ~/"Library/Application Support/TextMate/Bundles/"
git clone git://github.com/simongregory/actionscript3-tmbundle.git "ActionScript 3.tmbundle"
osascript -e 'tell app "TextMate" to reload bundles'

3. Flex SDK

The actual building of your project is done by the free Flex 3 SDK. Download it and move the extracted folder into your /Developer/SDKs/ folder. The SDK has to be accessible throughout your whole system. You can do this by adding it to the PATH variable in the /etc/profile file. Open terminal and type:

sudo mate /etc/profile

Then add the folder “/Developers/SDKs/flex_sdk_3/bin” to the file and save it. It should look something like this:

PATH="/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/mysql/bin:/Developer/SDKs/flex_sdk_3/bin"

4. TextMate settings

Before we can start the ActionScript fun we have to setup a few things in TextMate. Open TextMate and select File→New Project. Click on the info button located in the bottom of the Project Drawer. Add two shell variables so that the ActionScript Bundle knows where to look for your files:

TM_FLEX_FILE_SPECS    src/Main.as
TM_FLEX_OUTPUT        bin/Main.swf

We also need to let TextMate know where the Flex SDK is located. Go to TextMate→Preferences→Advanced→Shell Variables and add a new global variable:

TM_FLEX_PATH    Developer/SDKs/flex_sdk_3

5. Hello World

With all that out of the way, we can finally start working on a ActionScript project: Hello World. Create a folder on your system that will hold this project. Drag this folder to TextMate’s Project Drawer. Create two new folders named bin and src in your project folder. Then create a new file in the src folder and name it Main.as. It should look something like this:

package {        
    import flash.display.Sprite;
    import flash.text.TextField;
 
    [SWF( backgroundColor='0xFFFFFF', frameRate='30', width='200', height='200')]
 
    public class Main extends Sprite {
        private var textField: TextField;
 
        public function Main() {
            textField = new TextField();
            textField.text = "Hello World.";
 
            addChild(textField);
        }        
    }
}

Choose Bundles→ActionScript 3→Build to build your project. A terminal window pops up which runs the Flex SDK to build your project. You can find the resulting Main.swf file in your bin folder. Yay!

Notes

If you want to build Flash Player 10 specific stuff (like me) you have to let the Flex SDK know. Open /Developer/SDKs/flex_sdk_3/frameworks/flex-config.xml and set the target-player tag to 10.0.0

<target-player>10.0.0</target-player>

The easiest way to work with libraries, the so called SWC files, in this setup is to drop them directly into /Developer/SDKs/flex_sdk_3/frameworks/libs folder. That way the Flex SDK has no problem finding them.

Papervision3D 2.1 – alpha

Tuesday, May 26th, 2009

Just committed rev. 911 to the Papervision3D SVN trunk. Rev. 911 and upwards will become Papervision3D 2.1 because the changes made are quite big.
Major changes where made to the DAE, MD2 and animation classes.

NOTE:
This revision is considerably different then previous revisions. Use with care!
At this point its not advised to use rev. 911 for production.

This revision fixes several issues regarding the DAE class:

  1. vertex-animation
  2. nested animations
  3. Cinema4D support
  4. morph-weight animation
  5. splines
  6. cloning
  7. play(), play(“clipName”), stop(), pause(), resume()
  8. more…

The whole org.papervision3d.core.animation.* package has been revamped completely to allow for the changes in the DAE class.

DAE Example:

var autoPlay : Boolean = false; // don't play animations automatically
 
var dae : DAE = new DAE( autoPlay, "myCollada" );
 
dae.addEventListener(FileLoadEvent.LOAD_COMPLETE, onDaeComplete);
dae.addEventListener(FileLoadEvent.LOAD_PROGRESS, onDaeLoadProgress);
 
// optionally pass materials to DAE
// NOTE: here's a change with previous revs :
// 1. lookup the <material> elements in the COLLADA file (inside <library_materials>).
// 2. write down / remember the @id attribute of the <material> element.
// 3. materials.addMaterial( myMaterial, materialElementID ).
// ==> this will probably change in future revs
var materials : MaterialsList = new MaterialsList();
 
// If textures fail to load optionally add some search-paths 
// (relative to the swf):
dae.addFileSearchPath( "images" );
dae.addFileSearchPath( "textures" );
 
// set to true if you get a script-timeout error
var asyncParsing : Boolean = false;
 
// load it!
dae.load( "/path/to/dae", materials, asyncParsing );
 
/**
 * The DAE has loaded completely
 */
private function onDaeComplete(event : FileLoadEvent) : void
{
     var dae : DAE = event.target as DAE;
 
     // add to scene
     scene.addChild( dae );
 
     // start playing animation (if any available)
     // other animation controls include :
     // 1. play( "clipName ")
     // 2. stop()
     // 3. pause()
     // 4. resume()
     // 5. playing (getter: bool indicating if playing)
     dae.play();
 
     // lets create a clone
     // NOTE: DAE#clone() is somewhat bugged still, 
     // but seems to work in most cases
     var clone : DAE = dae.clone() as DAE;
 
     // add clone to scene
     scene.addChild( clone );
 
     // move it a bit
     clone.x = 200;
}
 
private function onDaeLoadProgress(event : FileLoadEvent) : void
{
 
}

MD2 Example:

var md2 : MD2 = new MD2();
 
var material : MaterialObject3D = new WireframeMaterial();
 
md2.addEventListener(FileLoadEvent.LOAD_COMPLETE, onMD2Complete);
 
md2.load("/path/to/md2", material);
 
scene.addChild(md2);
 
private function onMD2Complete(event : FileLoadEvent) : void
{
       var md2 : MD2 = event.target as MD2;
 
       md2.play();
       // or play some clip :
       // md2.play( "run" )
}

Animation:

Click the image below to show an example of the new animation controls.
Animation Test
Download the source of this example here.

The DAE and MD2 class implement IAnimatable, IAnimationProvider and IControllerProvider, which can be found in the org.papervision3d.core.animation.* package.

As so much has changed I’m sure some bugs are introduced. Please let me know!

PS: I’ll be on vacation until june 8th, so its unlikely I’ll be able to fix any bugs before that time.
PS2: Many people have helped by submitting code-snips, reporting bugs etc. I’ll credit you all when I’m back :-)

Load, modify and save local images with Flash Player 10

Monday, May 4th, 2009

One of the cool new things about Flash Player 10 is that you now have access to the local file system. This means that you can load, modify and save files directly on the client side without any server interaction. Mike Chambers wrote a post about reading and writing text files and I was wondering if it was possible to do this with image files too.
thumbr here

Publish Flash Player 10 content

To test this, I first needed a way to make my Flex Builder compile Flash Player 10 content. I found a rather old post by Andrei Ionescu about building Flash Player 10 applications that told me exactly what I was looking for. I downloaded the latest Flex SDK (3.3) from Adobe’s Flex Download page, followed the tutorial and I was ready to go.

Load image from local file system

Reading a local file was no biggie, just copied and pasted the code from Mike’s sample. What I needed now was a way to convert the loaded data into an image. As usual Tim helped me out by sending me a snippet:

var data:ByteArray = fileRef['data'];
var loader:Loader = new Loader();
loader.loadBytes(data);
addChild(loader);

Modify image

Now that I could load the image, I wanted to modify it before having it rendered. I extended the snippet to use BitmapData and a Matrix to resize the image to a thumb image (240×180px).

var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onDataLoadComplete);
loader.loadBytes(loadFileRef.data);
 
private function onDataLoadComplete(e:Event):void {
  var bitmapData:BitmapData = Bitmap(e.target.content).bitmapData;
  var matrix:Matrix = new Matrix();
  matrix.scale(THUMB_WIDTH/bitmapData.width, THUMB_HEIGHT/bitmapData.height);
 
  imageView.graphics.clear();
  imageView.graphics.beginBitmapFill(bitmapData, matrix, false);
  imageView.graphics.drawRect(0, 0, THUMB_WIDTH, THUMB_HEIGHT);
  imageView.graphics.endFill();
}

Save image to local file system

Now that it’s possible to load and modify an image, saving it is the last step. To save an image, it has to be encoded to a ByteArray. I used the open source as3corelib to help me out. Saving the ByteArray to a file is rather straight forward.

Update 1: You don’t need to use the as3corelib anymore, Flex 3.3 has it own JPEGEncoder.

var encoder:JPEGEncoder = new JPEGEncoder();
var rawBytes:ByteArray = encoder.encode(bitmapData);
 
var saveFileRef:FileReference = new FileReference();
saveFileRef.save(rawBytes);

Update 2: Thibault Imbert figured out a way to speed up JPEG encoding using the new FP10 Vector class. Good stuff!

Update 3: You can now also use Alchemy for the encoding. It’s much, much faster then the other options! See this post from Jens Krause: Speed up JPEG encoding using Alchemy

This sample shows that it’s not only possible to load, modify and save images directly from the local file system, but that it’s actually very simple to do so. Add some PixelBender image processing power to the game and you almost have a Photoshop killer running completly client side!

Code sample

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" backgroundGradientColors="[0xFFFFFF,0xFFFFFF]" backgroundColor="0xFFFFFF">
 
	<mx:Script>
		<![CDATA[
			import mx.graphics.codec.JPEGEncoder;
			import mx.binding.utils.BindingUtils;
			import mx.core.UIComponent;
 
			import flash.net.FileReference;
			import flash.net.FileFilter;
			import flash.events.IOErrorEvent;
			import flash.events.Event;
			import flash.utils.ByteArray;
 
			private var loadFileRef:FileReference;
 
			private static const FILE_TYPES:Array = [new FileFilter("Image Files", "*.jpg;*.jpeg;*.gif;*.png;*.JPG;*.JPEG;*.GIF;*.PNG")];
			private static const THUMB_WIDTH:uint = 240;
			private static const THUMB_HEIGHT:uint = 180;
 
			private function loadFile():void {
				loadFileRef = new FileReference();
				loadFileRef.addEventListener(Event.SELECT, onFileSelect);
				loadFileRef.browse();
			}
 
			private function saveFile():void {		
				var bitmapData:BitmapData = new BitmapData(THUMB_WIDTH, THUMB_HEIGHT);
				bitmapData.draw(imageView);
 
				var encoder:JPEGEncoder = new JPEGEncoder();
				var rawBytes:ByteArray = encoder.encode(bitmapData);
 
 				var saveFileRef:FileReference = new FileReference();
				saveFileRef.save(rawBytes);
			}
 
			private function onFileSelect(e:Event):void {
				loadFileRef.addEventListener(Event.COMPLETE, onFileLoadComplete);
				loadFileRef.load();
			}
 
			private function onFileLoadComplete(e:Event):void {
			   	var loader:Loader = new Loader();
			   	loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onDataLoadComplete);
			   	loader.loadBytes(loadFileRef.data);
 
				loadFileRef = null;
			}
 
			private function onDataLoadComplete(e:Event):void {
				var bitmapData:BitmapData = Bitmap(e.target.content).bitmapData;
 
                var matrix:Matrix = new Matrix();
                matrix.scale(THUMB_WIDTH/bitmapData.width, THUMB_HEIGHT/bitmapData.height);
 
				imageView.graphics.clear();
				imageView.graphics.beginBitmapFill(bitmapData, matrix, false); 
				imageView.graphics.drawRect(0, 0, THUMB_WIDTH, THUMB_HEIGHT);
				imageView.graphics.endFill(); 
 
				saveButton.enabled = true;
			}
 
		]]>
	</mx:Script>
	<mx:Panel title="Create thumb image" paddingBottom="5" paddingLeft="5" paddingRight="5" paddingTop="5">
		<mx:VBox>
			<mx:Canvas id="imageView" width="240" height="180" borderThickness="1" borderColor="#CCCCCC" borderStyle="solid"/>
			<mx:HBox paddingTop="5" borderThickness="1">
				<mx:Button label="Load image" click="loadFile()"/>
				<mx:Button label="Save image" click="saveFile()" id="saveButton" enabled="false"/>
			</mx:HBox>
		</mx:VBox>
	</mx:Panel>
</mx:Application>

Unproject with useProjectionMatrix = true

Wednesday, April 29th, 2009

I just updated Papervision3D to allow the CameraObject3D#unproject method to work when CameraObject3D#useProjectionMatrix = true.

Papervision3D has two methods of ‘projecting’ vectors onto the screen:

  1. useProjectionMatrix = false, this is the default ‘fast’ method
  2. useProjectionMatrix = true, this is the method where projection is done by a matrix

Note that Papervision3D switches to method #2 in ‘ortho mode’.

So what is the difference exactly between these two kinds of projection? First we need to define what projection is. Projection is what adds ‘perspective’ to a scene and makes sure your scene fits to the viewport. Its the last step in the so-called ‘render pipeline’.

We can derive the ‘fore shortening’ effect of perspective in several ways. Method #1 is simple, effective and fast. It uses the camera’s focus and zoom values. Method #2 uses a more classic way of projection : it uses a dedicated matrix. Tech buffs: very much like OpenGL’s gluPerspective.

Andy Zupko has a great post on unproject using method #1.

We will discuss unproject using method #2, which works slightly different.

The difference is that when #useProjectionMatrix is set to true, then CameraObject3D#unproject returns a point in world-space, rather then a ray. Let me explain with some code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// we want to use a projection matrix
camera.useProjectionMatrix = true;
 
// '0' indicates we want a point on the near plane
var pointOnNearPlane : Number3D = camera.unproject( screenX, screenY, 0 );
 
// '1' indicates we want a point on the far plane
var pointOnFarPlane : Number3D = camera.unproject( screenX, screenY, 1 );
 
// Construct the ray's direction
var dir : Number3D = Number3D.sub( pointOnFarPlane, pointOnNearPlane );
 
// Normalize
dir.normalize();
 
// So, now you have a ray with its origin at 'pointOnNearPlane',
// and direction 'dir'

Optimization: could save some cycles by doing (*not* in ortho mode!, see below):

1
2
3
4
5
6
7
var camPosition : Number3D = new Number3D( camera.x, camera.y, camera.z );
// Construct the ray's direction
var dir : Number3D = Number3D.sub( pointOnFarPlane, camPosition );
// Normalize
dir.normalize();
// So, now you have a ray with its origin at the camera's position
// and direction 'dir'

Why can’t we use above optimization in ortho mode?
In perspective mode all ‘rays’ originate from the camera’s position: the ‘view cone’ has shape of a pyramid. Hence we can assume ‘pointOnNearPlane’ to coincide with the camera’s position.
In ortho mode however the ‘view cone’ has the shape of a cube, all rays are parallel. Hence we need to unproject *two* points to construct our ray.

Now simply follow Andy Zupko ’s post to drag objects around.
Or perform ‘picking’, or…

Update:
Created a little demo to visualize what I’m ranting about, check it out here

Creating a REST API for a Flash application

Wednesday, February 25th, 2009

We have been working hard to implement an XML-based REST-style API for Floorplanner and some of our partners are using it already to access their users and plans. We now have started to use this API ourselves.

The Floorplanner Flash application communicates with our servers to load and save projects and designs. The backend for this functionality used to be written in PHP. Eliminating this PHP application simplifies our server setup, eases development and reduces our maintenance burden. To rewrite this functionality in Rails, we decided to eat our own dogfood and use the REST API to load and save designs. Why reinvent the wheel?

However, while implementing the changes it the Flash application, we found that it did not work out of the box, because of some limitations in ActionScript. Note that we still use ActionScript 2, so some of these issues may not be relevant for ActionScript 3. In this post, we will detail what these issues are and how we overcame them.

Using a separate format

Our REST API uses the XML format supplied by Rails. Because we had to make some changes to make it work from ActionScript, we decided to add a distinct format that we could implement differently without altering the behavior of the default XML API. Adding a new format, called “flash” can be done in Rails by adding a MIME alias to your environment:

Mime::Type.register_alias "application/xml", :flash

Now we can send different responses based on this format:

respond_to do |format|
  format.xml   { ... } # default API behavior
  format.flash { ... } # do something different
end

HTTP status codes

REST APIs use HTTP status codes to return whether a call succeeded, and if not, why. Flash however uses the browser to perform HTTP requests. The browser only returns something to Flash if the request was successful. So, if an error code is used if a request failed together with some error messages, these error messages will not be delivered to Flash and cannot be displayed to the user. We resolved this by always sending the HTTP OK status.

respond_to do |format|
  format.xml   { render :xml => @project.errors, :status => 422 }
  format.flash { render :xml => XML.failure(@project.errors, 422) }
end

Our XML.failure method will return something like:

<failure status="422">
  <error on="name">A project should have a name!</error>
</failure>

Note that other HTTP success statuses than 200 work in Safari and Firefox, but not in Internet Explorer. So, never return a 201 (:created), because Internet Explorer will not send the result to Flash!

PUT and DELETE requests

REST-style APIs use HTTP PUT requests to alter objects and DELETE requests to destroy objects. Most browsers do not support these request type. It is not supported by ActionScript either, because Flash uses the browser to send the request.

To overcome this problem, these types of requests can be simulated in Rails by sending a _method parameter along with a POST request. Unfortunately, this does not work when calling the REST API. The POST request body cannot be used to send additional variables, because it is used for the XML payload.

We solved this issue by creating additional routes for POST requests to the update and destroy actions of our resource controllers. Our routes.rb file now looks like this:

  map.resources :projects, :member => { :update => :post, :destroy => :post }

These routes route to exactly the same methods as the default REST actions (ProjectsController#update and ProjectsController#destroy), so no additional code is needed. The following calls are now equivalent:

PUT    /projects/123.xml
POST   /projects/123/update.xml
POST   /projects/123/update.flash

DELETE /projects/123.xml
POST   /projects/123/destroy.xml
POST   /projects/123/destroy.flash

The result

It requires some stretching of the pure REST principles, but doing so is worth it: we can now reuse the code we use for our API to handle Flash application calls and we can eliminate the PHP backend.