Thursday, December 9, 2010

AS3 Command Pattern for Rapid Asset Development

You know those little modular swf projects where you just program in some basic functionality in lieu of using the timeline? Well, I have had a few of those lately, so I made a little module manager.

It's based upon a useful tutorial I ran across and tweaked a bit. I created a google code repository to host the result, called as3minimalcommandpattern.

See this demo which uses only this library. All the code from the demo is pasted below ( the demo can be found with source in the repository).

The demo consists of the document class and as many 'scenes' as are required for the project. A scene in this sense is simply a way to encapsulate an intro-outro sequence. It's good for doing the monotonous work for you so you can focus on integrating advanced features.

Document Class:

package
{
import com.bluediesel.utils.managers.scenemanager.SceneManager;
import flash.display.MovieClip;
import scenes.*;

public class Main extends MovieClip
{
public function Main()
{
var sceneManager:SceneManager = new SceneManager();
sceneManager.setScene(new IntroScene(this));
}
}
}

IntroScene (Setup the UI):

package scenes
{
import com.bluediesel.utils.managers.scenemanager.commands.*;
import com.bluediesel.utils.managers.scenemanager.commands.greensock.*;
import com.bluediesel.utils.managers.scenemanager.scenes.*;
import com.greensock.plugins.*;
import fl.transitions.easing.Strong;

public class IntroScene extends AbstractScene implements IScene
{
public function IntroScene(c:Object) {

TweenPlugin.activate([AutoAlphaPlugin]);
super(c);
}

override public function createIntroCommand():Command
{
return new SerialCommand( 0.1,
new TweenMaxTo ( c.backgroundMC, 0.1, { blurFilter:{blurX:20, blurY:20} } ),
new TweenMaxTo ( c.backgroundMC, 0.4, { autoAlpha:1, blurFilter:{blurX:0, blurY:0} } ),
new ParallelCommand( 0,
new TweenMaxTo( [c.uiTopBanner, c.uiBottomBanner, c.buttonPrevious, c.buttonNext], 0.2, { autoAlpha:1 } ),
new TweenMaxFrom( [c.uiTopBanner, c.uiBottomBanner, c.buttonPrevious, c.buttonNext], 0.3, { y:500, easing:Strong.easeOut } )
),
new TweenMaxTo ( [c.appTitle, c.clickForSoundText], 0.2, { autoAlpha:1 } )
);
}

override public function onSceneSet():void {
sceneManager.setScene( new Scene01(c) );
}
}
}

Scene01 (Intro-Outro Sheep):

package scenes
{
import com.bluediesel.utils.managers.scenemanager.commands.*;
import com.bluediesel.utils.managers.scenemanager.commands.events.*;
import com.bluediesel.utils.managers.scenemanager.commands.greensock.*;
import com.bluediesel.utils.managers.scenemanager.commands.utils.SetProperties;
import com.bluediesel.utils.managers.scenemanager.scenes.*;

import fl.transitions.easing.Strong;

import flash.events.MouseEvent;
import flash.media.*;

public class Scene01 extends AbstractScene implements IScene
{
public function Scene01(c:Object)
{
super(c);
}

override public function createIntroCommand():Command
{
return new SerialCommand(0,
new TweenMaxTo ( c.sheepTitle, 0.2, { autoAlpha:1 } ),
new TweenMaxTo ( c.sheepMC, 0.2, { autoAlpha:1 } ),
new TweenMaxTo ( c.bodyCopyBG, 0.2, { height:231, width:470, autoAlpha:1, easing:Strong.easeOut } ),
new TweenMaxTo ( c.sheepBodyCopy, 0.2, { autoAlpha:1 } ),
new SetProperties ( [c.buttonPrevious, c.buttonNext, c.sheepMC, c.horseMC, c.pigMC], {buttonMode:true, useHandCursor:true} ),
new AddEventListener( c.sheepMC, MouseEvent.MOUSE_DOWN, playNoise ),
new AddEventListener( c.buttonPrevious, MouseEvent.MOUSE_DOWN, handlePrevious ),
new AddEventListener( c.buttonNext, MouseEvent.MOUSE_DOWN, handleNext )
);
}

override public function createOutroCommand():Command
{
return new SerialCommand(0,
new RemoveEventListener( c.sheepMC, MouseEvent.MOUSE_DOWN, playNoise ),
new RemoveEventListener( c.buttonPrevious, MouseEvent.MOUSE_DOWN, handlePrevious ),
new RemoveEventListener( c.buttonNext, MouseEvent.MOUSE_DOWN, handleNext ),
new TweenMaxTo ( c.sheepBodyCopy, 0.1, { autoAlpha:0 } ),
new TweenMaxTo ( c.bodyCopyBG, 0.1, { height:0, width:0, autoAlpha:0, easing:Strong.easeOut } ),
new TweenMaxTo ( c.sheepMC, 0.1, { autoAlpha:0 } ),
new TweenMaxTo ( c.sheepTitle, 0.1, { autoAlpha:0 } )
);
}

override public function onSceneSet():void
{
//TODO: implement function
}

/* CUSTOM METHODS */
private function handlePrevious(e:MouseEvent):void
{
trace ("Already At Beginning");
//sceneManager.setScene( new IntroScene( c ) );
}

private function handleNext(e:MouseEvent):void
{
sceneManager.setScene( new Scene02 ( c ) );
}

private function playNoise(e:MouseEvent):void{
var soundChannel:SoundChannel = new SoundChannel();
soundChannel = new SheepNoise().play();
}
/* END CUSTOM METHODS */
}
}

Scene02 (Intro-Outro Horse):

package scenes
{
import com.bluediesel.utils.managers.scenemanager.commands.*;
import com.bluediesel.utils.managers.scenemanager.commands.events.*;
import com.bluediesel.utils.managers.scenemanager.commands.greensock.*;
import com.bluediesel.utils.managers.scenemanager.scenes.*;

import fl.transitions.easing.Strong;
import flash.events.MouseEvent;
import flash.media.*;

public class Scene02 extends AbstractScene implements IScene
{
public function Scene02(c:Object)
{
super(c);
}

override public function createIntroCommand():Command
{
return new SerialCommand(0,
new TweenMaxTo( c.horseTitle, 0.2, { autoAlpha:1 } ),
new TweenMaxTo( c.horseMC, 0.2, { autoAlpha:1 } ),
new TweenMaxTo ( c.bodyCopyBG, 0.2, { height:231, width:470, autoAlpha:1, easing:Strong.easeOut } ),
new TweenMaxTo( c.horseBodyCopy, 0.2, { autoAlpha:1 } ),
new AddEventListener( c.horseMC, MouseEvent.MOUSE_DOWN, playNoise ),
new AddEventListener( c.buttonPrevious, MouseEvent.MOUSE_DOWN, handlePrevious ),
new AddEventListener( c.buttonNext, MouseEvent.MOUSE_DOWN, handleNext )
);
}

override public function createOutroCommand():Command
{
return new SerialCommand(0,
new RemoveEventListener( c.horseMC, MouseEvent.MOUSE_DOWN, playNoise ),
new RemoveEventListener( c.buttonPrevious, MouseEvent.MOUSE_DOWN, handlePrevious ),
new RemoveEventListener( c.buttonNext, MouseEvent.MOUSE_DOWN, handleNext ),
new TweenMaxTo ( c.horseBodyCopy, 0.1, { autoAlpha:0 } ),
new TweenMaxTo ( c.bodyCopyBG, 0.1, { height:0, width:0, autoAlpha:0, easing:Strong.easeOut } ),
new TweenMaxTo ( c.horseMC, 0.1, { autoAlpha:0 } ),
new TweenMaxTo ( c.horseTitle, 0.1, { autoAlpha:0 } )
);
}

override public function onSceneSet():void
{
}

/* CUSTOM METHODS */
private function handlePrevious(e:MouseEvent):void
{
sceneManager.setScene( new Scene01 ( c ) );
}

private function handleNext(e:MouseEvent):void
{
sceneManager.setScene( new Scene03 ( c ) );
}

private function playNoise(e:MouseEvent):void{
var soundChannel:SoundChannel = new SoundChannel();
soundChannel = new HorseNoise().play();
}
/* END CUSTOM METHODS */
}
}

Scene03 (Intro-Outro Pig):

package scenes
{
import com.bluediesel.utils.managers.scenemanager.commands.*;
import com.bluediesel.utils.managers.scenemanager.commands.events.*;
import com.bluediesel.utils.managers.scenemanager.commands.greensock.*;
import com.bluediesel.utils.managers.scenemanager.scenes.*;

import fl.transitions.easing.Strong;
import flash.events.MouseEvent;
import flash.media.*;


public class Scene03 extends AbstractScene implements IScene
{
public function Scene03(c:Object)
{
super(c);
}

override public function createIntroCommand():Command
{
return new SerialCommand(0,
new TweenMaxTo( c.pigTitle, 0.2, { autoAlpha:1 } ),
new TweenMaxTo( c.pigMC, 0.2, { autoAlpha:1 } ),
new TweenMaxTo ( c.bodyCopyBG, 0.2, { height:231, width:470, autoAlpha:1, easing:Strong.easeOut } ),
new TweenMaxTo( c.pigBodyCopy, 0.2, { autoAlpha:1 } ),
new AddEventListener( c.pigMC, MouseEvent.MOUSE_DOWN, playNoise ),
new AddEventListener( c.buttonPrevious, MouseEvent.MOUSE_DOWN, handlePrevious )
);
}

override public function createOutroCommand():Command
{
return new SerialCommand(0,
new RemoveEventListener( c.pigMC, MouseEvent.MOUSE_DOWN, playNoise ),
new RemoveEventListener( c.buttonPrevious, MouseEvent.MOUSE_DOWN, handlePrevious ),
new TweenMaxTo( c.pigBodyCopy, 0.1, { autoAlpha:0 } ),
new TweenMaxTo( c.bodyCopyBG, 0.1, { height:0, width:0, autoAlpha:0, easing:Strong.easeOut } ),
new TweenMaxTo( c.pigMC, 0.1, { autoAlpha:0 } ),
new TweenMaxTo( c.pigTitle, 0.1, { autoAlpha:0 } )
);
}

/* CUSTOM METHODS */
private function handlePrevious(e:MouseEvent):void
{
sceneManager.setScene( new Scene02 ( c ) );
}

private function playNoise(e:MouseEvent):void{
var soundChannel:SoundChannel = new SoundChannel();
soundChannel = new PigNoise().play();
}
/* END CUSTOM METHODS */
}
}
If you notice the repeating signature of a Scene class, the timeline functionality is controlled by createIntroCommand and createOutroCommand. Below those functions are any custom methods that the scene needs. There are ways this could be further packaged up, like an external class that contains all the helper functions. For now, this is a way to build a quick linear module or application without resorting to the timeline or to a more intensive solution such as a fully-fledged framework.

No comments: