October 03, 2006

AS2.0 development gotchas

In the last few months I've been working on a number of largish AS2.0 coding tasks. I've come across a few problems/bugs that I've spent literally days debugging/rewriting.
Just so you don't have to go through the same pain, here's a quick lowdown on my top timewasting/obscure-but-common problems and issues, with a fix for each.

You need better realtime debugging tools when writing classes

The debugger panel just doesnt hack it when using the standard Flash IDE, and trace isn't that good either because its limited in what it can do (it simply traces values to the output panel).

Fix: You can trace function calls. This one is so easy once you see it, but many coders have never seen it. If you are writing a class, create a debugging method called snapShot() that itself traces a snapshot of the relevant values/states in the class (and returns null to the calling trace()). Thus, whenever you want to debug an error, use

trace(snapShot());

Rather than using trace() to output text, we use it to call a debug function. This way the snapShot() function can do all sorts of things other than just display data (it can contain test harness logic that forces values for example). As with all trace actions, this trace is not exported to the final swf, so the debug code never gets invoked in the final swf. Sweet!

Don’t assume that delete actually deletes immediately.

When you use the delete action, you are relying on the Flash player’s memory 'garbage collection', and this is dangerous for two reasons. Firstly, the garbage collection routines are player specific; Flash player 8 is better at garbage collection than Flash player 7, but how good either are is not a documented feature. Also, even though you may delete something, the something may be forcing itself to be kept in memory in a number of ways (such as references that are still active, particularly if they reside in anonymous functions).

The issue of delete not deleting immediately is not normally an issue, except when you are creating event driven code where the events occur very quickly, (i.e. on the same frame). The fact that something you thought you deleted is actually still there may then cause all sorts of problems, especially if you can’t always be sure of the order of events (e.g when your code is at least partly driven by events generated by external webservice returns or the user, and both generate lots of graphic changes in the UI).

Fix: If you want something to be deleted immediately, make it null first. That way, you are much less worried if the something is not actually deleted right away because you know its value is deleted right away. For example, if you want an onEnterFrame to become inactive right away, don't use:

  delete this.onEnterFrame;

Consider doing this instead:

  this.onEnterFrame = null;
  delete this.onEnterFrame;

Another closely related issue I’ve seen a lot is where you are implicitly expecting Flash to delete local variables in event handlers, particularly those associated with recieving data. For example, Flash may not be deleting XML instances when event handlers end. The fix here is to:

Following the fixes seen above makes the Flash player much more stable when there's a lot of communications with a back-end via XML/webservices going on, especially for Flash 7 and previous versions of the player (and although we have Flash player 8 and 9 at the time of this writing, most projects still specify compatibility with Flash player 7 as a requirement).

Avoid using this in an AS2.0 class because scope bugs are hell to fix.

Okay, I know some people disagree totally with this, but I’ve found time and time again that using this in a class is a major timewaster when it comes to debugging. The problem here is that unlike other languages, in ActionScript, this can very easily change in a class containing event handlers. It can be difficult to see that this has changed if you have a scoping bug because this will always have a valid value, whereas lots of other things will become invalid (such as all the class properties because you are no longer scoping the class). The biggest problem in fixing this bug is that the situation causing the bug (an incorrectly scoped event handler) isn't persistent - once the event handler finishes, the properties magically become defined.

You can (and always should!) prevent this from changing throughout a class by using Delegate.create() when defining event handlers, but the problem is when you have bugs; if you forget or fail to force scope to remain unchanged, this is still equal to something (it usually becomes scoped to a property that can have event handlers, such as a movie clip or XML instance, rather than to the class). It would be far better if this became invalid, because that’s easier to debug.

Fix: Instead of using this directly, use a property that is equal to this:

  // In property definition…
  private var me:MovieClip;
  ...
  ...
  // In constructor…
  me = this;

The cool thing here is that property me will be undefined if you ever go out of class scope, thus making scoping errors quicker to pick out when debugging (because code that doesn't work is easier to fix than code that works but does the wrong thing). I can’t emphasize how much time you will save by doing this; most timewaster bugs in class based code are to do with scoping error.

A more structured solution is to ensure that any property that has event handlers is coded up via its own class. That way, this in the event hander and this in the class are actually the same thing. My feeling here is that you are then (at least partly) working around the problem rather than going head-on at the issue and fixing it; you should only be creating new classes if your model requires it, and not to work-around Flash.

It should of course be noted that the problem is less important in AS3.0... but AS3.0 is still beta and it will be at least the middle of next year before it bcomes mainstream.

Class inheritance doesn’t know about streaming.

Class inheritance works well when you are creating components because registering classes to movie clips causes the classes to load at the same time as the movie clips. You can also define which frame classes that are registered to movie clips load (File > Publish Settings > Flash Tab > ActionScript Version: Settings).

Class inheritence doesn’t always work when you have classes that are simply datatypes (i.e. they are not registered to a movie clip). In this situation, a subclass may load before its superclass, which is very bad for your code; super() stops working because the superclass is not yet available. Worse still, Flash won’t tell you the error occurs. Even worse still, you probably won’t even see the error until you put the application online!

Fix: The simple (and obvious) solution is to fully preload your application before running any class based code. The problem is that this is not always possible, particularly for more complex applications. A better way is to explicitly force classes to load at a certain frame ('better' because it sounds like a fix rather than a structural workaround!). You can do this by calling a static class method on the required frame. By using a static method, you can force the class to load before you start creating instances.

Instances don't contain methods and properties; they contain data.

Okay. This is not really a bug but a way of thinking when writing AS2.0 code. All reference material you read on OOP tells you about classes >> methods >> properties. Thus, when you start writing classes, your mindset in centered around methods and properties, and you may fall into the trap of seeing classes as defining 'containers of logically related code'.

Classes define containers of data. This data is held internal to each instance (or static class), and methods and properties are merely the external interfaces to that data. This is an important mindset to work to when defining your classes, but its always easy to forget...

Thus when defining a class, think first in terms of what data each instance should contain, and then think about the inerfaces external code will need to access that data. What you should not do is simply think about what methods and properties the instance should have, then backfit the data to support it. You can tell if you have done the latter, because a couple of months down the line, your classes will start to seem unwieldy and more trouble maintaining than they are worth!

Posted by motiongraphics at 12:34 AM | Comments (6)

February 26, 2006

vCam v2 - roll your own

I've been seeing loads of requests to make the vCam rotation code available so that the vCam can be modified through a community effort. Well, here's the code to start you off...

The main problem with creating code that can rotate the view seen through the vCam is not the rotation code itself (that is the easy part), but the code that forces your rotation to occur around the center point of the vCam.
A second problem is that the rotation code uses trig, and performing trig calcuations every frame can slow your animations, so you need to run the rotation code only when it is required. You can do this via a conditional that checks whether the angle of the vCam (i.e. the vcam _rotation property) has changed since last frame.

The swf below shows a solution to the 'rotate around the vCam and not any other point' problem (written by Big Dave at work). If you click the swf, you will see a rotating rectangle (which can be made to be the full stage). This is rotating with a crosshair at the center of rotation. If you can make this crosshair the center of the vCam, bingo... vCam with rotation!







The fla can be downloaded here (Flash MX 2004, totally unoptimized AS v1.0 code, 6k file).

NOTE - this fla is not a vCam v2.... I've put it up for the programmers out there twho want to make a vCam by using this code as a starting point.

Posted by motiongraphics at 11:39 PM | Comments (15)

January 14, 2006

Dynamic frame rates for Flash animators.

Unlike a number of other applications, you cannot change the frame rate of a Flash swf at runtime. You can only set the frame rate of the whole swf at author time.

Well, that is except if you read this entry, because I'll show you an easy way (which I call framerate pumping) to change the frame rate of any movie clip in your swf so that each of them can run at a different framerate if you want!

Flash has a useful timed event handler for ActionScripters, setInterval(). This allows you to run a script periodically at a defined time period. That’s not the same as having a timeline that runs at a user defined frame rate, but you can create a setInterval() event handler that forces a timeline to run at a user defined speed. This means that you can vary the frame rate of each movie clip at runtime. You can even do it to the root timeline.

This is way cool for animators because it allows them to create more expressive animations. You can even add that ‘go fast for a split second and then slow down’ effect they use a lot in the Matrix (and any number of other films since) to create tension and/or make key moments in the action stand out.

A speeded up timeline is easy to create. Try this:

In a new fla using the default frame rate (12fps), create a tween animation inside a movie clip (such as a motion tween moving a simple circle symbol left ro right). Test your movie. Nothing unusual so far.

On the first frame of the movie clip, add the following code:

function timelineFaster() {
  nextFrame();
  updateAfterEvent();
}
var tweenFaster:Number = setInterval(timelineFaster, 1);

When you test this movie, the code will force the timeline to run as fast as it can. The script is actually telling Flash to move to the next frame every 1ms, or 1000fps. Flash can’t get that fast, so the timeline goes as fast as it can (typically 40-70fps).

To run at a movie clip at a more sensible speed such as twice the frame rate, substitute the 1 in the last line to 1/24s, (which equals 42ms).

var tweenFaster:Number = setInterval(timelineFaster, 42);

This makes the timeline go twice as fast as the normal frame rate (24fps as opposed to 12fps).

To undo the frame rate change, add the following line on the keyframe you want normal framerates:

clearInterval(tweenFaster);

If you want the timeline to continue playing from this keyframe, also add a play().

The 'scripters out there will of course see that its pretty easy to create a new movie clip class that has a frame rate property (or you can use prototypes to change the MovieClip class directly) but I don’t really think that’s necessary - most of your movie clips will run at the standard frame rate, and you will only want one or two to run faster/slower.


Posted by motiongraphics at 11:14 PM | Comments (27)

September 15, 2005

Flash 8 filters

One of the biggest new features of Flash 8 is the filter and blending modes. Although the code required for scripted effects using filters can be daunting for the beginner (and let's face it, theres no end of sites currently showing ActionScript heavy Flash 8 effects, so I dont think I need to go into that here), here's a few script free examples for the don't-do-scripting Flash designers to pore over (sample files included)...

When Photoshop first started supporing layered blend modes and began going in for filtered effects bigtime, the world was full of tutorials showing you how to create various text effects. (Un)luckily, many of these effects are still around online, so I thought it would be a good idea to try to recreate many of them with Flash 8. Waddaya know, most of them worked as well. Here's a selection.

Selection of Flash text effects, based on Photoshop tutorials

All the above effects started off with the same sans font. Each text effect has Flash filters applied at runtime, so there is little additional download caused by these effects. Because the effects are applied at runtime, you can use the text effects on dynamic text (such as user input textfields).
You can download the source FLA file as a zip (5k) here. Note that this is a Flash 8 Pro specific file, and it will not open in anything else.

Once you have the FLA open, you can see how each text effect was created by clicking on the text on the Flash stage and viewing the filters tab of the Properties panel.

OF course, text effects are cool as a learning device, but they are not that practical... and other designers will be prone to laughing openly in the streets at you (and perhaps fart in your general direction) if you use text effects to the same extent as the above example in a real design.

What is needed in design work is a less-is-more approach. The Flash menu below is an example of this.

Flash menu using filter effects


The 3D buttons are created with nothing more than a white circle with a couple of filters applied to it. You can see this menu in operation in the swf below (you may see a blank space if you do not have the Flash 8 player installed), or you can see a working example of this file in a new browser here (requires the Flash 8 player to be installed), and the source files here (zip file, 5K). Note that the FLA is again Flash 8 Pro specific.




The buttons will change due to user interaction (they have different appearances for the up, over and down states). These changes are caused by changing the filter effects - the physical button graphic actually remains the same. This is of course a very powerful trick when designing animated user interfaces. Changing filter effects rather than creating new graphics results in very bandwidth friendly components.

Thus, although it's totally possible to go over the top with filter effects (resulting in a site that will look so yesterday by tomorrow, once everyone knows the trickery), careful use of filter effects can create low bandwidth graphics that look great despite their small size. Additionally, filter effects can be added via a quick couple of clicks, so the amount of time needed to create something like the menu above has gone down from hours to seconds when you use Flash 8.

This entry is based on work carried out for the book Foundation Flash, and a fuller tutorial on these effects can be found in that book.

Posted by motiongraphics at 11:17 PM | Comments (1)

September 10, 2005

Flash 8 and AsBroadcaster

The previously undocumented AsBroadcaster listener event system is now part of Flash 8... but do we really need it when we've rolled our own in Flash 7 (and the home grown version is a mellower smoke)?

Note: this post assumes a good understanding of ActionScript2.0 class based coding.

One of the coolest 'cool but undocumented' features of Flash 7 (a.k.a MX 2004) was AsBroadcaster. This useful class allows your custom classes to pass events between each other, something that you need when creating complex class based structures

For example, in web applications, you will want to create a class that maintains the underlying webservice as well as creating an event handling model that allows the rest of your code to respond automatically to service traffic. To do this effectively, you need to define custom event handling within your classes, and this is what AsBroadcaster allows you to set up.

However, Flash 7 AsBroadcaster sufferred from two problems.

Firstly, it was undocumented, so your boss wouldn't like you to use it, just in case it got changed in Flash 8 (and all your applications stopped working). Now that AsBroadcaster is documented, this is not an issue, but the undocumented status most likely prevented any real serious use in Flash 7.

Secondly, and more subtly (i.e - you won't realise this until you actually try to use Asbroadcaster a few times...), you can't use dynamic classes (such as AsBroadcaster) in static classes. The problem here is that some of the most likely classes that would benefit from being broadcasters are the base (and invariably) static classes that underpin the sort of complex system where you would want to use a broadcaster-listener system in the first place...

... which is why, in Flash 7, I deconstructed AsBroadcaster and created my own simple broadcast methods. Once you do this, it becomes apparent that broadcaster-listener systems are very simple in their construction.

All a broadcaster-listener event system consists of is a list of registered listeners, held by the broadcaster. This list is nothing more than an array of method (Function) references. When the broadcaster broadcasts an event, all it actually does is cycle through the listener list, invoking all the method references.

This is easily created in your own classes without ever needing to use AsBroadcaster at all. Your addListener method is simply a 'push to array', and the broadcast method just cycles through the array, calling the broadcast method for each registered object.

Here's an example (highly simplified) broadcasting class:


class CBroadcast {
  //  CBroadcaster class definition
  var m_listeners:Array;
  //
  function CBroadcast() {
    m_listeners = new Array();
  }
  //
  public function addListener(anObj:Object):Void {
    //  This function registers a listener to CBroadcast.
    // 
    m_listeners.push(anObj);
  }
  //
  public function broadcast(msg:String):Void {
    //  This function broadcasts to all registered listeners.
    //  If a listener (say aListener) has the function aListener.msg, 
    //  that function will be executed.
    //
    for (var i = 0; i<m_listeners.length; i++) {
      m_listeners[i][msg]();
    }
  }
  //
}

Here's a corresponding listening class:


class CListen {
  //  CListen class definition
  //
  function CListen() {
  }
  //
  public function anEvent():Void {
    trace("CListen: I heard the anEvent event.");
  }
  //
}

Assuming you have saved these two classes as Cbroadcast.as and CListen.as, you can test your custom broadcaster-listener event model with the following FLA code:


var aBroadcaster:CBroadcast = new CBroadcast();
var aListener1:CListen = new CListen();
var aListener2:CListen = new CListen();
aBroadcaster.addListener(aListener1);
aBroadcaster.addListener(aListener2);
aBroadcaster.broadcast("anEvent");

Your CListen instances will both execute the CListen.anEvent method (event) when the CBroadcast class calls Cbroadcast.broadcast("anEvent").

This is a powerful way to add a custom event model to your classes, and works even when your classes are static. Further, it works in both Flash 8 and Flash 7 - by 'works' we mean 'your boss would allow you to use it, even in Flash 7'.

Bonus!

Posted by motiongraphics at 02:52 AM | Comments (1)