ActionScript.org Flash, Flex and ActionScript Resources - http://www.actionscript.org/resources
Centralized Event Management in ActionScript 3
http://www.actionscript.org/resources/articles/829/1/Centralized-Event-Management-in-ActionScript-3/Page1.html
Patrick Cousins
I have been a professional Flash/Flex/ActionsScript developer, designer and animator for almost 10 years now. Holding up in Brooklyn, NY I recently developed sites for the Marines, Nokia, Smirnoff and too many others to go on with a silly list like this. Originally I started out as a web designer for a video game company way back in the days of Flash 3. Luckily, I was in the right place at the right time and learned programming logic from the old-school assembly and C++ programmers there while I taught myself Flash's as it grew to version 5 and its fancy new ECMA based syntax.  Feel free to check out my blog for more tutorials and scripts at: http://pajamacode.pj-co.com/.
 
By Patrick Cousins
Published on October 30, 2008
 
Some say the days of callbacks are still going strong. Others say events just rock.  Which is it? Heck, I'm not here to start a code-religion war so I'm not even going to begin to try and answer that one. Still, with all these fancy-pantsy, new-fangled events we have some big new challenges (and new opportunities too).  Luckily, we've had some good guides in the past.  Jason Cook's great article Centralized Event Management in ActionScript 2.0 was a truly inspiring jump-off point, but now it's time to jump a little farther and use some AS 3 in the process.

Who What Where
Not to long ago I was inspired by Jason Cook's article Centralized Event Management in ActionScript 2.0.  So inspired in fact, that I didn’t read the whole thing.  What? Yeah, that doesn’t sound very inspired does it.  Well it is — in a way.  I was actually so intrigued that I wanted to do more than just learn by reading.  I wanted to create something.  Learn by doing. Learn the ins and outs, the nitty gritty. So I rolled my own.  Why am I telling you all this? Yeah, time to get to the point.  Not long after I made my own EventManager, I had to update it to AS 3  (mostly to bore you with these long anecdotes!).

And so now that I am passing my version of this EventManager along, I wanted to give credit where it’s due.  And although the features of my  EventManager are different,  Jason Cook's EventCenter was the inspiration to everything that follows in this tutorial.

As a side note, there are some who might debate the merits of such a system of events and consider it bad OOP.  While I respectfully disagree, please see this forum post to delve into that discussion if you wish.

Okay, phew -- we got that cheesy intro friendly banter paragraph out of the way, so let’s dig into the nitty gritty!


Why?

What do we hope to accomplish:

  • Provide a central place that objects and classes can connect to as listeners and jam out while listening to that great new wave event music.
  • Loosen the coupling in our applications and websites by allowing objects to remain unaware of each other.  Loosen our ties too! (who wears ties?)
  • Enable debugging of the sequence our events are firing.  (what did that event just say to me?)
  • Provide new functionality to remove all listeners. (Or tell all those  long-winded-jabber-mouth broadcasters they need to hush up!)
  • Add a new longevity feature to listeners and dispatchers. (Some need life support!)

Who?

Not for Everyone.  This tutorial is for the following people and/or robots:

  • Intermediate ActionScript 3 developers.
  • Basic understanding of OOP and class inheritance.
  • Those with a working knowledge of events.
  • Banana enthusiasts.

What?

What are some of the things this system could be used for?

  • Small “postcard” or portfolio websites.
  • Flash games
  • Interactive banner ads and Rich Media.

Not?

I don’t like to admit this in public but it’s true that there is some stuff centralized event management isn’t cut out for:

  • Complex Applications
  • Projects with multiple ActionScript developers
  • Anything where the complexity is enough to justify a framework
  • NASA rovers


Pasta and Pesto

Enough already lets write some events! Make stuff happen!

Whoa, slow down there. We need to look back at why events are useful first.  Remember that first time you really “got” events?  That same day you got hit by lightning and managed to power your laptop without a battery or plug for hours?  Yeah, it was like that for me too.  Lets look at one of those o-so-fun examples of what makes events so great: The Space Balloon Attackers.

Lets say you were programming a game, one of your own original ideas that is in no way whatsoever a copy of space invaders with balloons. As you begin to code your not space invaders game, you realize that events make things super easy for you to send messages between your objects.  It allows you to separate different pieces of the functionality into some logical objects and yet still talk to that main class.  Your space balloons will notify the game class when then come onto the screen, and the ThumbtackCannon class can notify the balloons when it's fired a Thumbtack.   Finally the balloon could notify the game class that it has been popped. 

So this really makes things pretty streamlined and we can already see how events can be of help to us, but that's not the whole picture.  Events try to sneak away from a problem that all code can potentially suffer from. Spaghetti coupling.  By using listeners and dispatchers, events have a way of letting objects state what is important to them right up front.  Basically they only need to "register to be notified later."  This can be nice because it organizes the references we must keep track of from one object to another rather that having to remember that we need to call the method popReallyBigBalloonAndShowPowerUp() on the balloon class when a tack hits it.

So perhaps as you start to code, you sketch out the connections each class will have with each other. Early on with just a few objects things look something like this:

(Note: only pseudo-UML here for simplicity)


Well that looks pretty neat.  We’ve got some of our basic classes and events and we seem to be following good OOP principles.

But this is where we cue up some elevator music... ( time passes by ).... And later, when we’ve really dug into the code our classes might start to look more like this:



Oops what do we do now? Does the above diagram look familiar?  Perhaps conjuring images of pasta?  Yes, indeed I like spaghetti too --  I like most all kinds of pasta actually; but it’s best eaten with some pesto or marinara sauce, not splattered all over our code right? 

Well who hasn’t regurgitated some spaghetti code at one time or another?  I know I have.  But that’s besides the point (and a little disgusting).  Rather than talk about code upchuck, how about we see what we could do to help solve our spaghetti dilemma.


Dispatchers are such attention seekers
One of the problems with Event dispatchers (also called broadcasters in AS2), is they want everyone to know about them! They aren’t content just talking to themselves. They want people to listen, and they’ll pass their event around with fancy bubbles just to disguise it. OK, I kid a bit here of course, but it’s to make a point. EventDispatchers need to have another object listening to them for their event to have any effect outside their own DisplayObject chain. Simply: they need listeners to communicate.

And here's where the fun begins. With centralized event management we’re able to limit the number of listeners and coordinate the speeches of all those busy-body dispatchers.

Finally some code!

In the EventManager we have a method called registerDispatcher. This method is what a broadcaster would call to tell us it's going to be broadcasting events. It's like that HR person you send your resume to if you're one of those busy-body, overly-talkative EventDispatchers.

public function registerDispatcher ( dispatchingObj : IRegisteredDispatcher ) : void
{
        
        /**
* Get The events
* getEvents() is a custom method that must be in any
* class/obj that wants to register as a dispatcher
*/
var eventArray : Array = dispatchingObj.getEvents ( ) ;

/**
* Add the listeners
*/
var len : uint = eventArray.length for ( var i : uint = 0 ; i < len ; i++ )
{
var dispatcherEventName : String = eventArray [ i ] ;
dispatchingObj.addEventListener( dispatcherEventName, recieveEvent )
}

/**
* Save a reference in an array.
*/
dispatchersArray.push ( dispatchingObj );
}


The key to all this is the getEvents() function. It reaches out to that object and asks for a resume of sorts. It's the interviewer that asks the object that wants to register if it's up for the job. When an object wants to register as a dispatcher, it has to provide a list of events it's going to be sending out. It does this by returning an array of strings that represent the names of the events it will dispatch. Let's look at an example:


First, since this type of event management system is best suited for application-wide events, let's look at a custom event our Balloon Popper game will use for letting objects know when a balloon has been popped.

package com.pj_co.balloongame
{
import flash.events.Event;

/**
* Fake tutorial event!
*/
public class BalloonEvent extends Event
{
public static const POP : String = "pop";
public static const SHAKE : String = "shake";
public static const RATTLE : String = "rattle";
public static const ROLL : String = "roll";

public function BalloonEvent( type : String )
{
super( type );
}

public override function clone() : Event
{
return new BalloonEvent( type );
}
}
}



Great! we have an event but who's listening? No one seems to care yet? Well first no one is dispatching this event! Well now that we know what to say, who is going to say it? The balloon class of course. They are the objects that pop! Let's have a look:


package com.pj_co.balloongame
{
import com.pj_co.interfaces.IRegisteredDispatcher;
import com.pj_co.balloongame.BalloonEvent

/**
* Balloons are best popped but we need to tell people about it!
* @author patrick cousins
*/
public class Balloon extends MoiveClip implements IRegisteredDispatcher
{

public function Balloon ( )
{
//constructor stuff here, make a balloon!
}

/////////////////////////////////////////////

/**
* Other Balloon code would go here but we're not going to include all that.
* It's a tutorial after all we're not nuts!
*/
/////////////////////////////////////////////

/**
* Required function for my custom EventManager. This tells the EventManager
* what events to listen for. The EventManager will then add
 
* itself as a listener to this class for these events.
*/
public function getEvents ( ) : Array { var myEventsArray : Array = new Array ( ) ;

myEventsArray [ 0 ] = BalloonEvent.POP
myEventsArray [ 1 ] = BalloonEvent.SHAKE
myEventsArray [ 2 ] = BalloonEvent.RATTLE
myEventsArray [ 3 ] = BalloonEvent.ROLL

return myEventsArray;
}
}
}


Ah, so now we have it! That old friend of ours the getEvents() function. This is what lets everyone know what the baloon will be saying. Except that's the trick. It doesn't actually let everyone know. It just lets the EventManager what to listen for. We do this by returning an array that is just the names of each event we may dispatch later.

So this is still all well and good but we still don't have anyone listening! Read on to the next page to learn about how we can listen in to the events.



Listeners
What what what?  Who's listening?

Now that we've managed to send events to our EventManager class, what does it matter?  We need some objects to listen in to make this worthwhile don't we?  Indeed we do, so let's show two objects that are going to listen in for these events.

package com.pj_co.balloongame
{
        
        import com.pj_co.balloongame.BalloonEvent;
        import com.pj_co.managers.EventManager;
        
        /**
        * We Need to keep track of the score to know how the player has done!
        */
        public class ScoreCard
        {
                private var score : int = 0;
                
                public function ScoreCard ( )
                {
                        
                        EventManager.getInstance().addEventListener( BalloonEvent.POP, updateScore ) ;
                        
                }
                
                private function updateScore ( e : BalloonEvent ) : void
                {
                        score ++;
                }
        }
}


Above was our ScoreCard class, which obviously would need to know when a balloon was popped.  Below let's look at the LevelManager class which also needs to know.

package com.pj_co.balloongame
{
        
        import com.pj_co.balloongame.BalloonEvent;
        import com.pj_co.managers.EventManager;
        
        /**
        * We Need to keep track of the number of enimies killed till we can advance to the next level! 
        */
        public class LevelManager
        {
                private var score : int = 0;
                private var numBalloons : int = 15;
                
                public function LevelManager ( )
                {
                        
                        EventManager.getInstance().addEventListener( BalloonEvent.POP, updateScore ) ;
                        
                }
                
                private function updateScore ( e : BalloonEvent ) : void
                {
                        score ++;
                        if ( score > numBalloons )
                        {
                                // win!
                        }
                }
        }
}

Notice that all these two objects had to do was to listen to the EventManager.  This is really the key of what makes centralized event management powerful.  If you remember our object and event diagrams from before, can you imagine what they might look like now with such a system?




Alright now we're talking.  Looks a bit cleaner right?  So we know how to register as a dispatcher, we know how to listen in, and we can see now how many objects can more easily listen to a single place without knowing where the object is that's making the dispatch.  So what's next?  Read on for a bit about how this works behind the scenes and why the clone() method is important.  Then finally we'll talk about uses and advanced implementations that will be coming in part II of this tutorial.



Behind that curtain
The Fancy Magic

Having been through a bunch of ways to implement this centralized even management I suppose some of you may want to the powerful method that makes all this work.

This method is really the magic behind everything, so as you read it be prepared for something powerful.


/**
* Re-dispatches any events received.
*/
protected function recieveEvent ( e : Event ) : void { trace( "received event:", e.type);
dispatchEvent( e );
}


Are you amazed yet?  OK, I suppose it's a bit short.  But this really is key to everything.  Because the EventManager is listening to an event from a dispatcher, when it hears that event, the EventManager needs to tell someone.  Problem is, the EventManager isn't really sure who wants to know.  So our solution is to make the EventManager a little bit like a crazy person, and make it just talk to itself.    Eseentially, the EventManager recieves the event and then redispatches it to itself.  Have a look at the diagram below for a general idea of how this works.




Before we discuss this diagram, it's important to note again that this is only pseudo UML.  Those arrows don't actually represent events moving around but this can give us an important way to visualize what's going on.  Because the EventManager is listening to the dispatcher, the data can flow from the dispatcher (on the right) to the EventManager (center). Similarly, because the ListeningObject (on the left) is listening to the EventManager,  data can also flow from the EventManger to the listening object. 

The problem is, at this point the event that originates with the dispatchers has already completed.  It's as if it ran out of breath.  Luckily, the EventManager can give it a second life through the clone() method in all Event classes.    There are a number of tutorials online about the clone() method and how to implement it for your custom events.  A lot of these tutorials however, tend to not demonstrate all of the reasons why it's important, and this type of re-dispatching is exactly one of those reasons. 

When you re-dispatch an event, Flash's built in EventDispatcher class calls the clone() method on your old event to make a new one.  If you had any type of special values in your event, such as an ID or XML object, and you forgot to override clone(), you would lose those values.  This happens because Flash's EventDispatcher will call the clone() method of your custom classes super class -- the plain old vanilla Event Class.    And since the regular Event class has no idea that you had special values in your custom event, it will never clone them.  So be sure to override clone() in your special CustomDollyTheSheepEvent class if you want to maintain those properties.

We won't go to much into how to override the clone method when making custom classes, but below is a code sample showing a custom event with a proper clone method. 


package com.pj_co.events
{
import flash.display.Bitmap;
import flash.events.Event;

/**
* ImageLoaderEvent simple custom event for the image loader class
*/
public class ImageLoaderEvent extends Event
{
public static const LOADED : String = "imageLoaded";
public static const ERROR : String = "errorLoadingImg";

public var image : Bitmap
public var id : int;



public function ImageLoaderEvent( type : String )
{
super( type );
}

public override function clone ( ) : Event
{
var newEvt : ImageLoaderEvent = new ImageLoaderEvent ( type ) ;

newEvt.image = this.image;
newEvt.id = this.id;

return newEvt;
}
}
}


So that's the curtain, and what's behind it.  Click that next page link one more time for a conclusion and some info and challanges to be discussed in part II of this article.

Conclusion
We're done!


Well, for now we're done.  But now that you have learned centralized event management using AS3, I bet some ideas are swirling around in your head about what more can be done with all this.   What I outlined above was merely one way of centralizing the events in Flash.  Can you think of others?  What about different priorities to the events? Different tiers?  Or maybe even resetting the event lists?  I'll cover these all in a follow up part II to this tutorial but till then You can find the link to the code used in this tutorial below as well as the complete EventManager class which might just have some of those extra features.

Drawbacks

It's important to note that this isn't a full-proof system.   Much like any other centralization of data it can eventually grow too cluttered.  With this clutter comes the possibility of collision when two events are named the same thing.  Nevertheless, there are some great uses for centralized event management and I provide it to you as just another tool in your toolbox.  Part II of the tutorial will also cover some of the drawbacks and ways to mitigate the potential problems.

Thanks for reading and please feel free to comment below or catch up to me in the forums.

The files used in this tutorial can be downloaded here:
http://pajamacode.pj-co.com/files/pajamalib_EventManager.zip