RAD Model View Controller Demo

September 1, 2009 at 3:59 pm

Avatar Editor

Avatar Editor

About RAD

The Rapid ActionScript Development package lets developers get up and running with a lightweight flexible framework quickly and painlessly. Many developers are already using a model-view-controller framework. The RAD MVC package supplies a very clear set of abstract classes that will help any developer get up and running as fast as possible with a full-featured MVC design pattern. In lieu of a boring Hello World example, this demo will show how easy it is to setup an avatar editor in Flash using very little code. The user will be able to add or remove hair, add or remove a goatee, and change the hair color.

Required Files

RAD is available as open source through Google Code.
You can visit the project site or download the latest build.

You can download the entire demo source for this tutorial here.

Flash Visual Elements

This demo will have three visual components that we need to build in Flash: The Avatar Control Panel, an Avatar Info bar, and the Avatar himself. To begin, open up Flash and create a new AS3 file. Add a MovieClip titled ‘MVCDemo’ to the stage. Export the MovieClip as ‘com.craigphares.tutorials.mvc.MVCDemo’ and extend the ‘flash.display.Sprite’ class. This will be our main holder clip that will contain our three elements.

The Control Panel

Within the MVCDemo clip, create a new MovieClip called ‘ControlPanelView’, and set its instance property name on the stage to ‘controlPanelView_mc’. This will house our form controls to edit the avatar. Add a Flash ColorPicker component named ‘hairColor_cp’, and two CheckBox components named ‘hair_cb’ and ‘goatee_cb’. You can edit the labels to make it clear that one is for Hair and one is for the Goatee. That’s all we need for our ControlPanelView.

AvatarView

AvatarView

The Avatar Views

Return to the MVCDemo clip and create a new MovieClip named ‘AvatarView’, and set its instance property name on the stage to ‘avatarView_mc’. This is our main avatar display. Within the AvatarView clip, be sure to have one MovieClip named ‘hair_mc’, one named ‘eyebrows_mc’, and one MovieClip named ‘goatee_mc’. You can grab the artwork from the demo file available above, or create your own artwork for the Avatar. Just be sure to include separate artwork for the hair, eyebrows, and goatee.

Go back once more to the MVCDemo clip and create a new MovieClip named ‘AvatarInfoView’, and set its instance property name on the stage to ‘avatarInfoView_mc’. This will display some textual information about our avatar. Add a new TextField to the stage and name it ‘info_txt’.

Note: It’s important to name each important element on the stage. By working this way, a designer and programmer can work independently of each other, with the designer not needing to get involved with any ActionScript, and the programmer simply referencing the named objects within ActionScript and not worrying about the design. This significantly speeds up development time, as both members of the team can work simultaneously.

To illustrate the flexibility of the RAD framework, we’re going to show two different ways of converting these elements into View objects. So for now, edit the AvatarView in the library and export it for ActionScript as ‘com.craigphares.tutorials.mvc.AvatarView’, and export the ControlPanelView as ‘com.craigphares.tutorials.mvc.ControlPanelView’. Both of these classes will extend the RAD View class, and we will manually convert the AvatarInfoView to a View object in ActionScript.

The Model

Okay, our design is complete – now it’s on to the ActionScript. Open up your favorite ActionScript editor and create a new class named ‘com.craigphares.tutorials.mvc.AvatarModel’. This is our data model class and will extend the RAD Model class. Our AvatarModel class has three properties accessible through getters and setters: hairColor (uint), hasHair (Boolean), and hasGoatee (Boolean).

package com.craigphares.tutorials.mvc {

	import com.rad.mvc.*;

	public class AvatarModel extends Model {

		public static const DEFAULT_HAIR_COLOR:uint = 0x361808;

		private var _hairColor:uint = DEFAULT_HAIR_COLOR;	// hexadecimal hair color
		private var _hasHair:Boolean;						// if the avatar has hair
		private var _hasGoatee:Boolean;						// if the avatar has a goatee

		public function AvatarModel() {}

		public function get hairColor():uint { return _hairColor; }
		public function set hairColor(c:uint):void {
			_hairColor = c;
			notify();
		}

		public function get hasHair():Boolean { return _hasHair; }
		public function set hasHair(h:Boolean):void {
			_hasHair = h;
			notify();
		}

		public function get hasGoatee():Boolean { return _hasGoatee; }
		public function set hasGoatee(h:Boolean):void {
			_hasGoatee = h;
			notify();
		}

	}

}

The key feature of the AvatarModel is the notify() method. Whenever notify() is called from any Model, all Views associated with that Model run their own method named update(), which can handle any visual changes that need to be made to represent the change in the data model. You can see that we add the notify() method to each setter function in AvatarModel.

public function set hairColor(c:uint):void {
	_hairColor = c;
	notify();
}

AvatarView

Next, let’s create our AvatarView class. This class requires two methods, init() and update(). The init() method is called explicitly once to setup the view according to the supplied model, which will be our AvatarModel. The update() method is called every time a change is made to the AvatarModel.

package com.craigphares.tutorials.mvc {

	import com.rad.mvc.*;
	import flash.geom.ColorTransform;
	import flash.display.Sprite;
	import flash.events.Event;

	public class AvatarView extends View {

		private var _hair_mc:Sprite;
		private var _goatee_mc:Sprite;
		private var _eyebrows_mc:Sprite;
		private var _colorTransform:ColorTransform;

		public function AvatarView(m:IModel = null, c:IController = null, s:Sprite = null) {
			super(m, c, s);
		}

		override public function update(e:Event = null):void {

			_colorTransform.color = (getModel() as AvatarModel).hairColor;

			_hair_mc.transform.colorTransform = _colorTransform;
			_goatee_mc.transform.colorTransform = _colorTransform;
			_eyebrows_mc.transform.colorTransform = _colorTransform;

			_hair_mc.visible = (getModel() as AvatarModel).hasHair;
			_goatee_mc.visible = (getModel() as AvatarModel).hasGoatee;

		}

		public function init():void {

			_hair_mc = getChildByName("hair_mc") as Sprite;
			_goatee_mc = getChildByName("goatee_mc") as Sprite;
			_eyebrows_mc = getChildByName("eyebrows_mc") as Sprite;

			_colorTransform = new ColorTransform();

			update();

		}

	}

}

Let’s take a closer look at the update() method:

_colorTransform.color = (getModel() as AvatarModel).hairColor;

_hair_mc.transform.colorTransform = _colorTransform;
_goatee_mc.transform.colorTransform = _colorTransform;
_eyebrows_mc.transform.colorTransform = _colorTransform;

_hair_mc.visible = (getModel() as AvatarModel).hasHair;
_goatee_mc.visible = (getModel() as AvatarModel).hasGoatee;

Basically, we are grabbing the hair color from the model, and applying a colorTransform to all three avatar display objects: hair, goatee, and eyebrows. We also hide or show the hair or goatee based on the hasHair and hasGoatee properties of the AvatarModel. Simple enough.

AvatarInfoView

Our AvatarInfoView is even simpler. Here we are just describing the avatar as a “Hairy Man” or “Bald Man” based on whether the AvatarModel has hair.

package com.craigphares.tutorials.mvc {

	import com.rad.mvc.*;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.text.TextField;

	public class AvatarInfoView extends View {

		private var _info_txt:TextField;

		public function AvatarInfoView(m:IModel = null, c:IController = null, s:Sprite = null) {

			super(m, c, s);

			_info_txt = getSprite().getChildByName("info_txt") as TextField;
			update();

		}

		override public function update(e:Event = null):void {

			if ((getModel() as AvatarModel).hasHair) {
				_info_txt.text = "Hairy Man";
			} else {
				_info_txt.text = "Bald Man";
			}

		}

	}

}

Note: The AvatarInfoView class does not require an init() method because we will be constructing the class within ActionScript instead of exporting it for ActionScript within Flash. Therefore we have the ability to pass the model to the view during its construction and our initialization can happen within the constructor.

ControlPanelView

The ControlPanelView class will simply add EventListeners to the form controls and pass them along to our Controller class. During initialization we make sure to update the form controls to match the AvatarModel defaults.

package com.craigphares.tutorials.mvc {

	import com.rad.mvc.*;
	import fl.controls.ColorPicker;
	import fl.controls.CheckBox;
	import flash.display.Sprite;
	import flash.events.Event;

	public class ControlPanelView extends View {

		public function ControlPanelView(m:IModel = null, c:IController = null, s:Sprite = null) {
			super(m, c, s);
		}

		override public function defaultController(m:IModel):IController {
			return new AvatarController(getModel());
		} 

		public function init():void {

			var hairColor_cp:ColorPicker = getChildByName("hairColor_cp") as ColorPicker;
			hairColor_cp.selectedColor = (getModel() as AvatarModel).hairColor;
			hairColor_cp.addEventListener(Event.CHANGE, (getController() as AvatarController).onChange);

			var hair_cb:CheckBox = getChildByName("hair_cb") as CheckBox;
			var goatee_cb:CheckBox = getChildByName("goatee_cb") as CheckBox;

			hair_cb.addEventListener(Event.CHANGE, (getController() as AvatarController).onChange);
			goatee_cb.addEventListener(Event.CHANGE, (getController() as AvatarController).onChange);

		}

	}

}

The Controller

Now we can create our ‘AvatarController’ class, which extends the abstract Controller class. All our user input will pass through to this class through event listeners, and this class will be sure to tell the AvatarModel to change its data accordingly.

package com.craigphares.tutorials.mvc {

	import com.rad.mvc.*;
	import flash.events.Event;

	public class AvatarController extends Controller {

		public function AvatarController(m:IModel = null) {
			super(m);
		}

		public function onChange(e:Event):void {

			switch (e.target.name) {

				case"hair_cb":
					(getModel() as AvatarModel).hasHair = e.target.selected;
				break;

				case "goatee_cb":
					(getModel() as AvatarModel).hasGoatee = e.target.selected;
				break;

				case "hairColor_cp":
					(getModel() as AvatarModel).hairColor = e.target.selectedColor;
				break;

			}
		}

	}

}

The Main Class: MVCDemo

Finally, we put this all together in the ‘MVCDemo’ class:

package com.craigphares.tutorials.mvc {

	import flash.display.Sprite;

	public class MVCDemo extends Sprite {

		private var _avatarModel:AvatarModel;
		private var _controlPanelView:ControlPanelView;
		private var _avatarView:AvatarView;
		private var _avatarInfoView:AvatarInfoView;

		public function MVCDemo() {

			_avatarModel = new AvatarModel();

			_controlPanelView = getChildByName("controlPanelView_mc") as ControlPanelView;
			_controlPanelView.setModel(_avatarModel);
			_controlPanelView.init();

			_avatarView = getChildByName("avatarView_mc") as AvatarView;
			_avatarView.setModel(_avatarModel);
			_avatarView.init();

			var avatarInfoView_mc:Sprite = getChildByName("avatarInfoView_mc") as Sprite;
			_avatarInfoView = new AvatarInfoView(_avatarModel, null, avatarInfoView_mc);			

		}

	}

}

You can see that we created the AvatarInfoView differently from the AvatarView and ControlPanelView. By not exporting the class to ActionScript within Flash, we can pass the ‘avatarInfoView_mc’ Sprite through the View constructor from ActionScript. We also don’t explicitly pass the AvatarController, instead defining the defaultController within the ControlPanelView. This makes it easier to change controller classes later on without having to change our code in two places.

_avatarModel = new AvatarModel();

_controlPanelView = getChildByName("controlPanelView_mc") as ControlPanelView;
_controlPanelView.setModel(_avatarModel);
_controlPanelView.init();

_avatarView = getChildByName("avatarView_mc") as AvatarView;
_avatarView.setModel(_avatarModel);
_avatarView.init();

var avatarInfoView_mc:Sprite = getChildByName("avatarInfoView_mc") as Sprite;
_avatarInfoView = new AvatarInfoView(_avatarModel, null, avatarInfoView_mc);

So now we have a working avatar editor. You’ll see that both the AvatarView and the AvatarInfoView update in real time as you change the controls within the ControlPanelView. You can have unlimited views associated with each Model, allowing you to keep your data nice and tidy within one place.

If you have any questions or comments about this tutorial, feel free to contact me. I hope this makes your Flash projects much easier and faster to develop.

3D Flash Card Flip Tutorial

August 11, 2009 at 11:05 am

Card Flip Tutorial

Card Flip Tutorial

One effect I need to use often in my Flash applications is a very simple card flip. The best way to simulate this effect is to use Papervision3D to make a realistic three dimensional illusion. For this demonstration we’re going to create a playing card that will flip to the other side when the user clicks on it.

First, download the Papervision3D package from their website:
http://blog.papervision3d.org/

You will also need the AS3 Tweener package for smooth animation tweens:
http://code.google.com/p/tweener/

This demonstration makes use of several classes from the RAD ActionScript package. This is currently in beta and will be released in the near future.

You can use your own images for the card front and back, or you can download the whole source here:
card_flip_tutorial.zip

Open up Flash and create a new 640×480 document. Import the front and back images – these should ideally be the same size. Create symbols out of each image, and name them ‘CardFront’ and ‘CardBack’ respectively. Right-click on each symbol in the library and check ‘Export for ActionScript’. Have the base class extend ‘flash.display.Sprite’.

CardFront Properties

CardFront Properties

Note: This could just as easily have been done entirely in ActionScript using a Loader, but by using a Library Symbol it illustrates that this can be done with much more than simple images. You can have form controls, video, or even other MovieClips within your flippable symbol.

Next it’s onto the ActionScript. Open up your favorite ActionScript editor and create a new class named ‘PapervisionScene’. This is a generic extension of the Sprite class that will make it easy for us to setup a full Papervision3D scene, complete with camera, viewport, and renderers. Alternatively, you can use the ‘com.rad.papervision3d.PapervisionScene’ class included in the source files and skip ahead to the good part.

package com.rad.papervision3d {

	import flash.display.Sprite;
	import flash.events.Event;
	import org.papervision3d.objects.DisplayObject3D;
	import org.papervision3d.render.QuadrantRenderEngine;
	import org.papervision3d.scenes.Scene3D;
	import org.papervision3d.cameras.Camera3D;
	import org.papervision3d.view.Viewport3D;
	import org.papervision3d.render.BasicRenderEngine;

	public class PapervisionScene extends Sprite {

		private var _scene:Scene3D;						// the 3d scene
		private var _cameraPivot:DisplayObject3D;		// the origin (0, 0, 0)
		private var _camera:Camera3D;					// the main camera
		private var _viewport:Viewport3D;				// the viewport
		private var _renderer:BasicRenderEngine;		// basic renderer (faster)
		private var _quadRenderer:QuadrantRenderEngine;	// quad renderer (better)
		private var _renderAsQuad:Boolean;				// flag to determine render method

		public function PapervisionScene(viewportWidth:int = 800, viewportHeight:int = 600, autoScaleToStage:Boolean = false) {

			// default to quad rendering for accuracy
			_renderAsQuad = true;

			// create the objects
			_scene = new Scene3D();
			_cameraPivot = new DisplayObject3D();
			_scene.addChild(_cameraPivot);
			_camera = new Camera3D();
			_viewport = new Viewport3D(viewportWidth, viewportHeight, autoScaleToStage, true, true, true);
			_renderer = new BasicRenderEngine();
			_quadRenderer = new QuadrantRenderEngine(QuadrantRenderEngine.CORRECT_Z_FILTER);

			// add the viewport to the stage
			addChild(_viewport);

			// render the scene every new frame
			addEventListener(Event.ENTER_FRAME, onEnterFrame);

		}

		// getters and setters
		public function get scene():Scene3D { return _scene; }
		public function get camera():Camera3D { return _camera; }
		public function get viewport():Viewport3D { return _viewport; }
		public function get cameraPivot():DisplayObject3D { return _cameraPivot; }

		public function get renderAsQuad():Boolean { return _renderAsQuad; }
		public function set renderAsQuad(r:Boolean):void { _renderAsQuad = r; }

		/*
		* add an object to the 3d scene
		*/
		public function addToScene(o:DisplayObject3D):void {
			_scene.addChild(o);
		}

		/*
		* render the scene to the stage
		*/
		private function onEnterFrame(e:Event):void {
			if (_renderAsQuad) {
				_quadRenderer.renderScene(_scene, _camera, _viewport);
			} else {
				_renderer.renderScene(_scene, _camera, _viewport);
			}
		}

	}

}

At a glance, we’re adding the following objects to the Sprite by default:

_scene = new Scene3D();
_cameraPivot = new DisplayObject3D();
_scene.addChild(_cameraPivot);
_camera = new Camera3D();
_viewport = new Viewport3D(viewportWidth, viewportHeight, autoScaleToStage, true, true, true);
_renderer = new BasicRenderEngine();
_quadRenderer = new QuadrantRenderEngine(QuadrantRenderEngine.CORRECT_Z_FILTER);
addChild(_viewport);

We also tell the Sprite to render the scene on every frame using our preferred render engine:

private function onEnterFrame(e:Event):void {
    if (_renderAsQuad) {
        _quadRenderer.renderScene(_scene, _camera, _viewport);
    } else {
        _renderer.renderScene(_scene, _camera, _viewport);
    }
}

Now we’re going to create the core card flipping class. Create a new class named ‘CardFlipScene’ that again extends Sprite.

package com.rad.papervision3d {

	import caurina.transitions.Tweener;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import org.papervision3d.events.InteractiveScene3DEvent;
	import org.papervision3d.materials.BitmapMaterial;
	import org.papervision3d.objects.primitives.Plane;

	public class CardFlipScene extends Sprite {

		private var _side1_mc:Sprite;
		private var _side2_mc:Sprite;
		private var _scene:PapervisionScene;
		private var _plane1:Plane;
		private var _plane2:Plane;
		private var _isFlipped:Boolean;
		private var _isFlipping:Boolean;
		private var _isInteractive:Boolean;

		public function CardFlipScene(side1:Sprite, side2:Sprite, interactive:Boolean = false) {

			// save the source sprites
			_side1_mc = side1;
			_side2_mc = side2;

			// save interactive flag
			_isInteractive = interactive;

			// determine the sprite dimensions
			var planeWidth:Number = _side1_mc.width;
			var planeHeight:Number = _side1_mc.height;

			// calculate the scene size
			var sceneWidth:Number = planeWidth * 2;
			var sceneHeight:Number = planeHeight * 2;

			// create the scene
			_scene = new PapervisionScene(sceneWidth, sceneHeight);
			_scene.camera.zoom = 115;
			_scene.x = -sceneWidth / 2;
			_scene.y = -sceneHeight / 2;
			addChild(_scene);

			// capture the first material
			var bmp1:BitmapData = new BitmapData(planeWidth, planeHeight, true, 0);
			bmp1.draw(_side1_mc);
			var material1:BitmapMaterial = new BitmapMaterial(bmp1);
			material1.interactive = true;
			material1.smooth = true;

			// create the front side
			_plane1 = new Plane(material1, planeWidth, planeHeight, 10, 10);
			_scene.addToScene(_plane1);

			// capture the second material
			var bmp2:BitmapData = new BitmapData(planeWidth, planeHeight, true, 0);
			bmp2.draw(_side2_mc);
			var material2:BitmapMaterial = new BitmapMaterial(bmp2);
			material2.interactive = true;
			material2.smooth = true;

			// create the back side
			_plane2 = new Plane(material2, planeWidth, planeHeight, 10, 10);
			_plane2.rotationY = 90;
			_scene.addToScene(_plane2);

			// add rollovers to the planes
			if (_isInteractive) var ip1:InteractiveObject = new InteractiveObject(_plane1, _scene.viewport);
			if (_isInteractive) var ip2:InteractiveObject = new InteractiveObject(_plane2, _scene.viewport);

			// add listeners for clicks
			_plane1.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, onObjectClick);
			_plane2.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, onObjectClick);

		}

		/*
		* get the current card object (read only)
		*/
		public function get card():Plane {
			if (_isFlipped) return _plane2;
			else return _plane1;
		}

		/*
		* flip to the opposite side
		*/
		public function flip(delay:Number = 0, time:Number = 1, clockwise:Boolean = true):void {
			if (_isFlipped) flipToFront(delay, time, clockwise);
			else flipToBack(delay, time, clockwise);
		}

		/*
		* flip to the back
		*/
		public function flipToBack(delay:Number = 0, time:Number = 1, clockwise:Boolean = true):void {

			_isFlipping = true;

			// determine the time for half a flip
			var halfTime:Number = time / 2;

			// setup start and end rotations based on spin direction
			var rotBegin1:Number = 0;
			var rotEnd1:Number = -90;
			var rotBegin2:Number = 90;
			var rotEnd2:Number = 0;
			if (clockwise) {
				rotEnd1 = 90;
				rotBegin2 = -90;
			}

			// snap planes to start rotations
			_plane1.rotationY = rotBegin1;
			_plane2.rotationY = rotBegin2;

			// animate the planes
			Tweener.addTween(_plane1, {rotationY:rotEnd1, delay:delay, time:halfTime, transition:"linear"});
			Tweener.addTween(_plane2, {rotationY:rotEnd2, delay:(delay + halfTime), time:halfTime, transition:"linear", onComplete:finishFlip});

			// save the flip state
			_isFlipped = true;

		}

		/*
		* flip to the front
		*/
		public function flipToFront(delay:Number = 0, time:Number = 1, clockwise:Boolean = true):void {

			_isFlipping = true;

			// determine the time for half a flip
			var halfTime:Number = time / 2;

			// setup start and end rotations based on spin direction
			var rotBegin1:Number = 90;
			var rotEnd1:Number = 0;
			var rotBegin2:Number = 0;
			var rotEnd2:Number = -90;
			if (clockwise) {
				rotBegin1 = -90;
				rotEnd2 = 90;
			}

			// snap planes to start rotations
			_plane1.rotationY = rotBegin1;
			_plane2.rotationY = rotBegin2;

			// animate the planes
			Tweener.addTween(_plane2, {rotationY:rotEnd2, delay:delay, time:halfTime, transition:"linear"});
			Tweener.addTween(_plane1, {rotationY:rotEnd1, delay:(delay + halfTime), time:halfTime, transition:"linear", onComplete:finishFlip});

			// save the flip state
			_isFlipped = false;

		}

		/*
		* finish the flip motion
		*/
		private function finishFlip():void {
			_isFlipping = false;
		}

		/*
		* dispatch a click event
		*/
		private function onObjectClick(e:InteractiveScene3DEvent):void {
			if (!_isFlipping && _isInteractive) dispatchEvent(new InteractiveScene3DEvent(e.type));
		}

	}

}

First, we add a PapervisionSprite object to the stage:

			// create the scene
			_scene = new PapervisionScene(sceneWidth, sceneHeight);
			_scene.camera.zoom = 115;
			_scene.x = -sceneWidth / 2;
			_scene.y = -sceneHeight / 2;
			addChild(_scene);

Next, we are adding two Papervision3D Plane objects to the scene, grabbing the BitmapData from the two Sprites passed to the constructor, and mapping the generated BitmapMaterials to the Planes:

			// capture the first material
			var bmp1:BitmapData = new BitmapData(planeWidth, planeHeight, true, 0);
			bmp1.draw(_side1_mc);
			var material1:BitmapMaterial = new BitmapMaterial(bmp1);
			material1.interactive = true;
			material1.smooth = true;

			// create the front side
			_plane1 = new Plane(material1, planeWidth, planeHeight, 10, 10);
			_scene.addToScene(_plane1);

			// capture the second material
			var bmp2:BitmapData = new BitmapData(planeWidth, planeHeight, true, 0);
			bmp2.draw(_side2_mc);
			var material2:BitmapMaterial = new BitmapMaterial(bmp2);
			material2.interactive = true;
			material2.smooth = true;

			// create the back side
			_plane2 = new Plane(material2, planeWidth, planeHeight, 10, 10);
			_plane2.rotationY = 90;
			_scene.addToScene(_plane2);

We also create a flipToBack and flipToFront method to easily tell the scene to flip the card. We are using the Tweener library to smoothly animate the rotation of the Planes over time:

		/*
		* flip to the back
		*/
		public function flipToBack(delay:Number = 0, time:Number = 1, clockwise:Boolean = true):void {

			_isFlipping = true;

			// determine the time for half a flip
			var halfTime:Number = time / 2;

			// setup start and end rotations based on spin direction
			var rotBegin1:Number = 0;
			var rotEnd1:Number = -90;
			var rotBegin2:Number = 90;
			var rotEnd2:Number = 0;
			if (clockwise) {
				rotEnd1 = 90;
				rotBegin2 = -90;
			}

			// snap planes to start rotations
			_plane1.rotationY = rotBegin1;
			_plane2.rotationY = rotBegin2;

			// animate the planes
			Tweener.addTween(_plane1, {rotationY:rotEnd1, delay:delay, time:halfTime, transition:"linear"});
			Tweener.addTween(_plane2, {rotationY:rotEnd2, delay:(delay + halfTime), time:halfTime, transition:"linear", onComplete:finishFlip});

			// save the flip state
			_isFlipped = true;

		}

You may have noticed that the CardFlipScene class uses an InteractiveObject class to add interactivity to the card:

			// add rollovers to the planes
			if (_isInteractive) var ip1:InteractiveObject = new InteractiveObject(_plane1, _scene.viewport);
			if (_isInteractive) var ip2:InteractiveObject = new InteractiveObject(_plane2, _scene.viewport);

The InteractiveObject class simply adds event listeners to change the cursor when the mouse is over the object passed to the constructor:

package com.rad.papervision3d {

	import org.papervision3d.events.InteractiveScene3DEvent;
	import org.papervision3d.objects.DisplayObject3D;
	import org.papervision3d.view.Viewport3D;

	public class InteractiveObject {

		private var _viewport:Viewport3D;

		public function InteractiveObject(object:DisplayObject3D, viewport:Viewport3D) {
			_viewport = viewport;
			object.addEventListener(InteractiveScene3DEvent.OBJECT_OVER, onObjectOver);
			object.addEventListener(InteractiveScene3DEvent.OBJECT_OUT, onObjectOut);
		}

		private function onObjectOver(e:InteractiveScene3DEvent):void {
			_viewport.buttonMode = true;
		}
		private function onObjectOut(e:InteractiveScene3DEvent):void {
			_viewport.buttonMode = false;
		}

	}

}

We now have a very simple way to add a flippable card to our Movies. Go back to your Flash project, create a new MovieClip symbol in your library named ‘CardFlip’, and set it to ‘Export for ActionScript’ in frame 1. I have it defined as the ‘com.craigphares.tutorials.cardflip.CardFlip’ class which we will write next. It should extend ‘flash.display.Sprite’ as well.

Our entire main ActionScript class is as follows:

package com.craigphares.tutorials.cardflip {

	import caurina.transitions.Tweener;
	import com.rad.papervision3d.CardFlipScene;
	import flash.display.Sprite;
	import org.papervision3d.events.InteractiveScene3DEvent;

	public class CardFlip extends Sprite {

		private var _scene:CardFlipScene;

		public function CardFlip() {

			// get the front and back sprites
			var front:Sprite = new CardFront();
			var back:Sprite = new CardBack();

			// create the scene
			_scene = new CardFlipScene(front, back, true);
			_scene.x = 320;
			_scene.y = 240;
			addChild(_scene);

			// listen for clicks
			_scene.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, onObjectClick);

			// scale up the card when loading
			_scene.alpha = 0;
			_scene.scaleX = _scene.scaleY = 0;
			Tweener.addTween(_scene, {alpha:1, scaleX:1, scaleY:1, delay:1, time:1, transition:"easeOutBack"});

		}

		/*
		* flip the card when clicked
		*/
		private function onObjectClick(e:InteractiveScene3DEvent):void {
			// flip the card in half a second with no delay
			_scene.flip(0, 0.5);
		}

	}

}

First, we add the CardFlipScene to the stage and add an event listener for clicks:

			// create the scene
			_scene = new CardFlipScene(front, back, true);
			_scene.x = 320;
			_scene.y = 240;
			addChild(_scene);

			// listen for clicks
			_scene.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, onObjectClick);

The onObjectClick event handler just calls our utility function flip(), which is a combination of flipToBack() and flipToFront():

		/*
		* flip the card when clicked
		*/
		private function onObjectClick(e:InteractiveScene3DEvent):void {
			// flip the card in half a second with no delay
			_scene.flip(0, 0.5);
		}

Hopefully, you can find a use for this card flip tutorial, and add some interest to your Flash applications. Feel free to contact me with questions and comments.

* The background photograph used in this demo is courtesy of leafar..

The Pixel Tutorial by Derek Yu

February 16, 2006 at 6:53 pm

Derek Yu has published a great tutorial on how to draw pixel art. Don’t be fooled, pixel art is not a dead art. Anyone who’s ever had to design an icon for the web will tell you otherwise.

Illustration | |

Creating Video for iPod

December 15, 2005 at 10:44 am

Want to get your own video onto your new video iPod? Apple has a straightforward tutorial that outlines the process. You’ll need QuickTime 7 Pro.

Video | |

PowerPoint to Flash

July 28, 2005 at 12:23 pm

I’m often asked to convert PowerPoint presentations to Flash movies. While there are several third party applications that will do this, none truly meet my clients’ needs. These presentations need to behave exactly like a PowerPoint presentation. They must maintain all text and vector art. Most of the third party conversion utilities simply convert the presentation into a sequence of images, which break down in quality as the presentation is scaled. The rest overlay the slides with their own branded interface, so the presentation cannot be viewed full screen.

Well, there is a way to do this manually. The technique is not perfect, but if you spend some time setting up the PowerPoint file, you’ll find the results are beautiful. You can create a fully scalable, editable Flash movie from a PowerPoint presentation that can be completely controlled by ActionScript. Although there are many steps to the process, once you get the hang of the technique it can be competed very quickly. It’s also a good idea to keep a Flash document around as a template so that you don’t need to recreate the work you’ve already done.

First, you’ll need to prepare the PowerPoint document. Make sure you’re not using any complicated gradients or animations. These will be interpreted poorly when they’re brought into Flash. Also, make sure there are no objects that fall outside the confines of the slide area. This will ensure that all the slides align correctly when they’re imported to Flash. Now, save a copy of your presentation without any background images. You may want to also choose a contrasting background color to easily see the content of each slide. You’ll import the background images into Flash at a later time.

Second, choose File > Save As… from your PowerPoint document and save the presentation as a Windows Metafile (*.wmf). This will save your entire presentation as a sequence of files. WMF files keep all text.

Next, create a new Flash Document and resize the Stage to 720 x 540. Change the background color to black. Choose File > Import > Import to Stage… and import the first WMF file. When asked to import all of the images in the sequence, choose Yes. This will place each slide from your presentation onto a sequence of frames.

Then, create a new layer under the slides layer and import the images to use for your background. You’ll probably need two images, one for title slides and one for the regular slides.

Now it’s time for some manual labor. You’ll need to go through every frame of the movie and delete the solid background shape from your slides layer. Once this is complete, you should see the content of each slide with the correct background image behind it.

Finally, add a frame to the end of your movie. Place some static text on that frame that says something like “End of slideshow, click to exit.”

Alright, now it’s time to move on to some ActionScript. Create a new layer for your actions. There are a few statements you’ll need to include right away. First, you want this movie to play full screen so add an fscommand.

fscommand("fullscreen","true");

To make sure the Stage resizes correctly specify the scaleMode.

Stage.scaleMode = "exactFit";

Finally, you don’t want the movie to begin playing through all the slides right away before the user starts clicking, so add a stop function.

stop();

You’ll need to include some functions that will be used frequently to navigate the presentation.

function gotoNextSlide():Void {
 if (_currentframe < _totalframes) {
     gotoAndStop(_currentframe + 1);
 } else {
     quit();
 }
}

function gotoPreviousSlide():Void {
 gotoAndStop(_currentframe - 1);
}

function gotoHome():Void {
 gotoAndStop(1);
}

function gotoEnd():Void {
 if (_currentframe < _totalframes) {
     gotoAndStop(_totalframes - 1);
 }
}

function quit():Void {
 fscommand("quit");
}

Next, we need to handle all the keyboard and mouse events so that the user can navigate through the slides. We’ll do this by creating a new listener object.

var myListener:Object = new Object();

myListener.onKeyDown = myOnKeyDown;
myListener.onKeyUp = myOnKeyUp;
Key.addListener(myListener);

myListener.onMouseUp = myOnMouseUp;
Mouse.addListener(myListener);

Here are the listener functions.

function myOnKeyDown():Void {
 if (Key.isDown(Key.DOWN) || Key.isDown(Key.PGDN)) {
     gotoNextSlide();
 } else if (Key.isDown(Key.UP) || Key.isDown(Key.PGUP)) {
     gotoPreviousSlide();
 } else if (Key.isDown(Key.END)) {
     gotoEnd();
 } else if (Key.isDown(Key.HOME)) {
     gotoHome();
 }
}

function myOnKeyUp():Void {
 if (Key.getCode() == 27) {
     quit();
 }
}

function myOnMouseUp():Void {
 gotoNextSlide();
}

That should do it. You’ve now transformed a PowerPoint presentation into your own Flash movie.