Flash: Broadcasters & Listeners AS 2.0

05.02.08 | Author: Deborah
Write-Up by Danny Staten

Introduction:

Broadcasters and Listeners are one of the most powerful tools for dealing with asynchronous events in flash. Any time you have coded an onPress or onEnterFrame function, you have been listening to a broadcast event. Their true power comes when you start using them to create custom broadcasting and listening. By the end of this article, we will be discussing advanced topics like how to implement your own broadcasting and listening.

The Basics:

So what is a broadcaster, and what is a listener? Think of the broadcaster like somebody with a radio transmitter. They can speak into a microphone, and anybody tuned into their frequency can hear what they say and react as they choose. In reality, this is a simplified analogy that doesn’t accurately describe the actual implementation used for broadcasting. It is however sufficient to describe them for our purposes. So in order for the broadcaster to work, it needs to have subscribed listeners.

You might be inclined to ask why broadcasters are so important. They are very important in flash due to the order code executes. Code written in a frame starts on that frame, but a command started on a given frame often finishes many frames later. A simple example of this is the following tween:

var myTween = new mx.transitions.Tween(clip_mc,”_alpha”,mx.transitions.easing.Regular.easeOut,0,100,12,false);
trace(“object faded in”);

At initial glance, one might think that the trace saying that the object’s fade in will show up once the tween is finished. It should be obvious to any who have programmed a tween that it doesn’t work that way. All the code written in a given frame begins execution during that frame, but this tween finishes 12 frames after the code that fired it actually executed. If you want that trace to fire when the tween is done you have to use a listener.

var myTween=new mx.transitions.Tween(clip_mc,”_alpha”,mx.transitions.easing.Regular.easeOut,0,100,12,false);
myTween.onMotionFinished=function()
{
            trace(“object faded in”);
}

This example is not as clear an example of a listener because it takes advantage of the fact that the tween object will hear its own broadcast events. The following example does the same thing, but is clearer for instruction:

var myTween=new mx.transitions.Tween(clip_mc,”_alpha”,mx.transitions.easing.Regular.easeOut,0,100,12,false);
var listener_obj:Object=new Object();
myTween.addListener(listener_obj);
listener_obj.onMotionFinished=function()
{
            trace(“object faded in”);
}

Here we see the broadcaster-listener behavior more clearly demonstrated. I create an object that I will then subscribe to the myTween object. That means that I can then set that object up to react to any message sent by the myTween object. In this case it reacts to when the tween broadcasts “onMotionFinished”. It is important to note that a subscriber listens to just its object’s broadcasting. For example if you have two tween objects myTween and myTween2, you can consider it as if they broadcast on separate frequencies. You can have one listener listening to multiple broadcasts simultaneously if you subscribe it to both.

Another valid item to listen to for this tween is onMotionChanged
listener_obj.onMotionChanged=function()
{
           trace(“fade in progress”);
}

Not all objects broadcast messages. The tween object is implemented by Macromedia and happens to be set up to broadcast several events. The two events, onMotionFinished and onMotionChanged turn out to be the most useful.

Examples:

Here I will show some interesting concepts that can be used with broadcasters and listeners. The examples will generally increase in complexity and usefulness.

One tween for multiple objects.
It is quite common to want to do something like move a clip 200 in x, 100 in y for example. At first glance it appears that you need two tweens to do this, one for the x and one for the y. In reality, it is very easy to set up one tween that drives any number of related attributes for any number of movie clips. Consider the following example:

Var tween_obj:Object = new Object();
tween_obj.cur=0;
tween_obj.move_x=200;
tween_obj.move_y=100;
tween_obj.startX=myMovieClip_mc._x;
tween_obj.startY=myMovieClip_mc._y;
tween_obj.target_mc=myMovieClip_mc;
//tween_obj is going to be the listener I use
Var myTween = new mx.transitions.Tween(tween_obj,”cur”,mx.transitions.easing.Regular.easeOut,0,100,12,false);
myTween.addListener(tween_obj);
tween_obj.caller=myTween;
tween_obj.onMotionChanged=function()
{

/*one trick to look out for is the scope inside these functions. When inside an event function for an object the ‘this’ keyword is actually the object. In this case this refers to tween_obj. */

           //note that the tween object’s cur variable is being changed by the tween

            var stepX:Number=this.cur/100 * this.move_x;
            var steyY:Number = this.cur/100* this.move_y;
            this.target_mc._x=this.startX+stepX;
            this.target_mx._y=this.startY+stepY;
}

tween_obj.onMotionFinished=function()
{
/*You should always clean your own memory up. Theoretically flash has garbage clean up, but true to the rule of thumb, garbage clean up is not to be relied on. */
            delete this.caller;
            delete this;
}

This particular example actually is considerably more code than just using two tweens, and it likely won’t provide any real performance benefits. However the concept allows you to do advanced tweening where your code ensures that all the needed motions are synchronized across as many objects as you can keep straight in your head.

Bending a Line:
An example of using this concept in a more powerful way is to create a tween that bends a line. Click here to download the example fla file.

This is a simple example of something you couldn’t animate programmatically without using broadcasting and listening. If you were to now do a tween that actually drives both the curveX and curveY values you could get something where the line bends and stretches in any direction.

Snapping the Line Back:
Now let’s say that you want to make it so that after the line bend is complete, it then snaps back to a straight line. The example fla file is available here. The only changes to the code are in the onMotionFinished function.

An important thing to note is that the memory clean up for these animations is deferred to the second onMotionFinished event. It is also a good idea to attach any items you will want to delete to your listener object so you can make sure that you have a pointer that is in scope when you do your memory clean up. This is a better example of how you can use one tween to drive multiple parameters.

Making the Bending and Snapping Dynamic:
So you are probably thinking, “that looks like a lot of work for something I could do with a timeline animation.” You have a point, but even with the examples you have seen so far there are powerful advantages to programmatic animation.

  • This is mathematically very clean and precise.
  • This particular animation would likely be very challenging via the timeline and would require shape tweens and or lots of key frames.
  • Coded animations are much easier to reuse, and modify.  If you take time to code them up properly, you can modify your animation in unlimited ways in seconds. 

One thing that you cannot do in pure timeline animation is make an animation that interacts with any user input. The following example will demonstrate something that you couldn’t possibly do without broadcasters and listeners. Download the source file here.

There are some nifty things to notice in this code. One is that you can overwrite your listener events at any time. Use this to turn off an object’s response to one event if you still want the object to catch other events. Also be aware of the trickiness with the this keyword. Remember that it is always in reference to the variable space for the specific object whose function is being called. Many events can pass variables if your listener provides them as parameters, but I tend to move things into my function spaces by attaching them to my listening object. That way you can have whatever data you want available in an event, and not just what a broadcaster has set up to send as a parameter.

This example is also an example of how mouse clicks can cause undesired effects if the user clicks while the snapping animation is running. You will notice that if you click, drag, let go, and then click really fast your line will be permanently bent. See my other article on disabling mouse clicks to find a pretty powerful solution to most problems of that nature. With slight modifications, that technique can prevent the glitches we can create here, but that is beyond the scope for this article.

Custom broadcasters:

Up to this point, we have really been using listeners and taking advantage of broadcasters that are already implemented. Think about the cool stuff that can be done with listeners and then think that we have only begun to harness one half of the sweetness that is broadcasters and listeners. If you really want to get so you can have fun with this idea, you will want to learn how to do your own broadcasting.

The following are a few core principles to be aware of:

<!--[if !supportLists]-->·         <!--[endif]-->Almost anything more than a simple variable can be a listener.  For example you can subscribe a movie clip to a broadcaster, but you shouldn’t subscribe a number. (I never have tried to make a number a listener, so I can’t say what the results will be, but it will likely destroy the number’s data or fail to respond to events or both.)

<!--[if !supportLists]-->·         <!--[endif]-->You can subscribe any number of items to a broadcaster.

<!--[if !supportLists]-->·         <!--[endif]-->A listener can subscribe to any number of broadcasters simultaneously.  This can be nice, but you have to be careful.

<!--[if !supportLists]-->·         <!--[endif]-->Make sure to use broadcasting_obj.removeListener(listener_obj), where broadcasting is the name of whatever object you have broadcasting, and listener_obj is your own listener.  Do this when you no longer need a listener to respond to broadcast events.  Forgetting to remove a listener can cause challenging bugs when you have an old object’s listener events firing long after you are expecting them to.

<!--[if !supportLists]-->·         <!--[endif]-->Not all objects can be set up as broadcasters, and simple variables should never be broadcasters.  (I don’t think it is possible, but I also haven’t ever tried.)  When you make something a broadcaster you are adding functions to the object.  I have yet to find any native objects that cannot be broadcasters (I have been able to make a movie clip a broadcaster or even _level0).  However, instances of 3rd party classes may not allow you to make them broadcasters.  The class describing the object either has to be dynamic (which allows its objects to have new variables and functions attached to it), or it has to already have the correct functions with the correct signatures set up in its code.

Why Use Broadcasters?

Okay so why would you want to do your own broadcasting? I have found them very valuable in the following applications.

  • Site animation control:   My favorite application deals with large dynamic web sites or applications.  In this case, the time it takes to remove a dynamically generated page can vary greatly depending on the amount of content or the style of animation or both.  If you have your code to remove a section broadcast an event such as “onRemoveFinished”, then you can easily wait until any section has removed before drawing the next section to be brought in.
  • Advanced animation needs: Another really interesting application can be extended from the bending line examples.  To see the bending line extended via custom broadcasting, check out the Martin Doors flash application, and pay attention to the light grey boxes.  In this case, the code in question broadcasts when the bending line’s start and end points have changed.  That allows the lines on the side of the boxes to listen to that and redraw to stay connected.  The result is an animation that has all the benefits of being dynamic, extremely flexible and configurable, while still being interesting and relatively complex.
  • Custom class development: If you get involved in creating your own custom ActionScript classes, you will find broadcasters are indispensable.  Imagine how useless the Tween would be without onMotionFinished or onMotionChanged events.  Sure it could probably do its basic animations, but there would be no means for code that uses a Tween object to know what is going on.  Broadcasters allow custom classes to send messages to your main code.  There are very ugly and unhappy ways to try and do the same thing with your custom classes, but for practical intents and purposes, broadcasting makes things possible that cannot (or at least should not) be done in other ways.

How To Use Broadcasters.
To make something a broadcaster you simply make the following call:

AsBroadcaster.initialize(myObj);

Where myObj is whatever object you wish to make a broadcaster. Keep in mind that the broadcaster has to be visible to all listeners in order for them to subscribe to it. For management of events that happens across multiple sections of a web site, you will want something that is going to be around in all areas that will need to listen for your events.

Say for example you have movie clips as follows:

Contents_mc.sectionA_mc.lots_of_child_clips
Contents_mc.sectionB_mc.lots_of_child_clips
Contents_mc.sectionC_mc.lots_of_child_clips

Suppose that you have animations that remove section A, and broadcast when that is done so code can draw either section B or C. It would make sense in this case to make Contents_mc your broadcaster.

Once you have made something a broadcaster, it is very easy to use.

AsBroadcaster.initialize(broadcaster_obj);
Var listener_obj:Object=new Object();
broadcaster_obj.addListener(listener_obj);

listener_obj.onHello=function()
{
            trace(“hello event caught”);
}
broadcaster_obj.broadcastMessage(“onHello”);

The example code above is so trivial it is not inherently useful, but it demonstrates how to create your own custom broadcasting.

Making Custom classes broadcasters
For situations where you want to make your own custom class a broadcaster, there is one more thing to be aware of. The class must either be declared as follows:

dynamic class className{…….}

An object created from a dynamic class can have variables and values attached to it at run time. This can be very handy, but is not always the best idea. The other way to do it is to add the following member variables to your class:

public var addListener:Function;
public var removeListener:Function;
public var broadcastMessage:Function;

Final Thoughts

I hope this article proves useful for you. It brings me to tears to think about some of the truly painful and ugly ways I found to survive without taking full advantage of broadcasting and listening. If you are willing to spend the time to figure them out, and then start thinking about how you can use the tools they open up to you, you will be amazed at the doors they open up to your flash development.

Add to Technorati Favorites

1 RESPONSE:

lloydie says:
08.18.08

thank you. very insightful article, particularly on Tween. I still struggle with listeners, but you've pointed me true.

LEAVE A COMMENT

 
*