ActionManager

I was asked recently - well, okay, not that recently, I've been busy - about our ActionManager system and how it works. I said that if I had time I'd open-source it; so here's a (cut-down) version of it.

Our system was developed in AS2, and has now been ported to AS3; it's been around for a while now and has taken most of what we've thrown at it.

At its root, it's a simple concept, and it's not far removed from many tweening engines that exist out there.

The idea is that every bit of animation/motion and various other user-targeted feedback - sound effects, looped sounds etc. - can be broken down into individual steps, which we call Actions. We represent each action as a class. One class for moving a graphic from one point to another; one class for playing a sound effect; one class for tweening a property and so forth.

All of our classes for performing actions implement the IAction interface, a really simple interface with start(), cancel(), pause() and resume() methods, and an event that fires when the action is complete.

At its simplest, you create an action object and pass it to the action manager to execute, like so.

var actions:ActionManager=new ActionManager();
 
// Move mySprite from 0,0 to 100,100 over 1 second
var moveAction:MoveAction=new MoveAction( mySprite, new Point(0,0), new Point(100,100), 1000);
 
actions.start(moveAction);

If you want to be called back when the action is complete:

var actions:ActionManager=new ActionManager();
 
// Move mySprite from 0,0 to 100,100 over 1 second
var moveAction:MoveAction=new MoveAction( mySprite, new Point(0,0), new Point(100,100), 1000);
moveAction.addEventListener(ActionEvent.COMPLETE,onMoveComplete,false,0,true);
 
actions.start(moveAction);
 
function onMoveComplete(ev:ActionEvent):void
{
  trace("Move Complete.");
}

At this point you might be wondering - why pass it to the action manager at all? Why not just call .start() on the action? Well, you could; but the fundamental reason we came with this system in the first place is because we want, at any point, to be able to pause everything.

Why? Because in our apps we have a help button. When the help button is pressed, we just want everything to stop.

ActionManager has a pauseAll() method. Any actions - and any composite actions, and any child actionmanagers - all just stop. And they know exactly what to do when resumeAll() is called.

Similarly, when we exit a portion of an app, we want to be able to cancel everything that's going on.

That's why we use AnimActions rather than relying on MovieClip.play(), why we use LoopedSounds etc. - they are all actions, and all know how to pause, resume and cancel. It's also why we use EnterFrameActions rather than a standard Sprite ENTER_FRAME - because the Sprite doesn't know about pausing and resuming and cancelling, and the EnterFrameAction does.

We support composite actions, which are extremely useful; they let you chain actions in sequence or run them in parallel, and they don't complete until the last action in their set finishes. So you can play a sound while moving an object to a location, and won't be called back until it's all finished - like so:

var actions:ActionManager=new ActionManager();
 
// Move mySprite from 0,0 to 100,100 over 1 second
var moveAction:MoveAction=new MoveAction( mySprite, new Point(0,0), new Point(100,100), 1000);
// Play a sound (this class isn't in the source code, it's an in-house one)
var soundAction:SoundAction=new SoundAction(mySound);
 
var aset:ActionSet=new ActionSet();
aset.add(moveAction);
aset.add(soundAction);
aset.addEventListener(ActionEvent.COMPLETE, onMoveAndSoundComplete,false,0,true);
 
actions.start(aset);
 
function onMoveAndSoundComplete(ev:ActionEvent):void
{
  trace("Both move and sound complete!");
}

Likewise, you can queue things up - here we move a sprite to a location while playing a sound, then chain another move:

var actions:ActionManager=new ActionManager();
 
// Move mySprite from 0,0 to 100,100 over 1 second
var moveAction:MoveAction=new MoveAction( mySprite, new Point(0,0), new Point(100,100), 1000);
// Play a sound (this class isn't in the source code, it's an in-house one)
var soundAction:SoundAction=new SoundAction(mySound);
 
var aset:ActionSet=new ActionSet();
aset.add(moveAction);
aset.add(soundAction);
aset.addEventListener(ActionEvent.COMPLETE, onMoveAndSoundComplete,false,0,true);
 
var nextMove:MoveAction=new MoveAction(mySprite, new Point(50,50), new Point(0,0), 500);
 
var aq:ActionQueue=new ActionQueue();
aq.add(aset);
aq.add(nextMove);
 
aq.addEventListener(ActionEvent.COMPLETE, onEverythingComplete,false,0,true);
actions.start(aq);
 
function onMoveAndSoundComplete(ev:ActionEvent):void
{
  trace("Both move and sound complete!");
}
 
function onEverythingComplete(ev:ActionEvent):void
{
  trace("Everything complete!");
}

You get the idea. Like I said - a very simple concept. We use it a lot. Its huge benefit is that whole pause()/resume() thing, although it's also very handy for sketching out simple interactions and then adding more detail later (put in a simple move first; expand it with sound effects and animation and so on afterwards).

The source code for the action manager, supporting classes and some simple actions is attached. Hope someone finds it useful!

Not enough actions included for you? They're really simple to write, honestly! But again, if there's enough interest we might expand this into a wider project and include a bunch more source code.

(New BSD license. Go for it. :-) )

AttachmentSize
ActionManager.zip21.45 KB