Applet and Code

Playing cards are used in many applications that focus on puzzles and games. This "how to" illustrates basic methods of two custom classes (CardLoader and PlayingCard) that are currently under development at flashandmath.com. A PlayingCard object consists of front and back image files, optional suit and value properties, and several methods to allow typical card motion -- the primary method illustrated here is the rotateView method, which shows the card rotated a specified number of degrees about either the horizontal or vertical center axis. (The motion is managed by simple Timer object that repeatedly changes the rotation angle as well as the x and z coordinates.) A CardLoader object creates an array of PlayingCards from a list of URL's for card face / card back image files. Click the screen shot below or this link to see the simple effect of dealing a five card hand from a shuffled deck of cards.:

Downloads

Download the well-commented source files corresponding to the applet above, carddeal.zip.

The card images used in our example were created by Jesse Fuchs and Tom Hart from free SVG images created by David Bellot, and are themselves available for use under a Creative Commons License.

Related Tutorials

Check out Flash and Math tutorials related to playing cards and random selection when dealing cards:

The code

We start by loading the custom classes for working with playing cards

import flashandmath.as3.cards.PlayingCard;

import flashandmath.as3.cards.CardLoader;

We align the two buttons and makethe "deal button" visible. We set properties of a dynamic text field, txtOutput, created on stage.

btnRepeat.x = btnDeal.x;

btnRepeat.y = btnDeal.y;

btnDeal.visible = false;

btnRepeat.visible = false;

txtOutput.wordWrap = true;

txtOutput.mouseEnabled = false;

We create a board that we can attach all objects to. This will allow us to move the effect easily to different parts of the stage, if desired.

var board:Sprite = new Sprite();

board.x = 50;

board.y = 110;

addChild(board);

We creat an Array to hold loaded cards and a separate array for the shuffled deck:

var arrCards:Array = [ ];

var arrDeck:Array = new Array(52);

The following variables are for things that will need to be referenced in multiple functions:

var topCard:PlayingCard;

var cardLdr:CardLoader;

The turnRadius is the radius of the semicircular path followed by y and z coordinates of each card when dealt.

var turnRadius:Number = 150;

The card will move in "turnSteps" many steps over a "turnTime" millisecond period.

var turnSteps:Number = 16;

var turnTime:Number = 400;

Between cards dealt there is a slight pause of this many milliseconds:

var delayTime:Number = 200;

We use a global variable to count the number of cards dealt so we know when to stop:

var numDealt:int = 0;

Each card will have the same perspective projection values set:

var pp:PerspectiveProjection = new PerspectiveProjection();

pp.fieldOfView=20;

pp.projectionCenter=new Point(0,0);

txtOutput.text = "Loading images. Please wait...";

The makeCards function sets up file names that correspond to the files in the "cards-50" subfolder located in the same folder as this fla source file.

makeCards();

 

function makeCards():void {

var arrVals:Array = ["A","2","3","4","5","6","7","8","9","T","J","Q","K"];

var arrSuits:Array = ["C","H","S","D"];

var arrCardStrings:Array = new Array(52);

var i:int, j:int;

 

// Fill arrCardStrings with the 52 file names for the card faces

for (i=0; i<4; i++) {

for (j=0; j<13; j++) {

arrCardStrings[13*i+j] = "cards-150/"+arrVals[j]+arrSuits[i]+"-150.png";

}

}

 

/* CardLoader object is constructed with an array of file names of card faces
and a single file name for the card back. */

cardLdr = new CardLoader(arrCardStrings,"cards-150/redback3-150.png");

cardLdr.addEventListener(CardLoader.CARDS_LOADED, loadComplete);

cardLdr.addEventListener(CardLoader.LOAD_ERROR, loadError);

}

The following handler is called when all card image files have completely loaded; it primarily calls the setupDeck function, defined further below.

function loadComplete(e:Event):void {

 

setupDeck();

 

btnDeal.visible = true;

txtOutput.text = "";

txtOutput.visible = false;

 

cardLdr.removeEventListener(CardLoader.CARDS_LOADED, loadComplete);

cardLdr.removeEventListener(CardLoader.LOAD_ERROR, loadError);

}

Actions to take if there is a file error:

function loadError(e:Event):void {

txtOutput.text = "There has been an error loading files. Try refreshing the page.";

cardLdr.removeEventListener(CardLoader.CARDS_LOADED, loadComplete);

cardLdr.removeEventListener(CardLoader.LOAD_ERROR, loadError);

}

Set up the deck of cards by "shuffling" the loaded cards:

function setupDeck():void {

var i:int;

var r:int;

var pc:PlayingCard;

 

// Remove all current children of board

while (board.numChildren>0) {

board.removeChildAt(0);

}

 

/* arrDeck is initially empty. arrCards is initially the array of

PlayingCard objects from the CardLoader object cardLdr */

arrDeck = new Array(52);

arrCards = (cardLdr.getCardArray()).concat();

 

/* Remove random cards from arrCards and place them on arrDeck. Each is set to be initially facedown.*/

for (i=0; i<52; i++) {

r = Math.floor(arrCards.length * Math.random());

pc = arrCards.splice(r,1)[0] as PlayingCard;

 

pc.makeFaceDown();

 

pc.transform.perspectiveProjection=pp;

 

board.addChild(pc);

 

// Position each card

pc.x = 0;

pc.y = 0;

pc.z = 0;

 

arrDeck[i] = pc;

}

}

The following Timers manage the animation. The first timer manages the turn, and the second timer provides a delay between cards dealt.

var tmTurn:Timer = new Timer(turnTime/turnSteps,turnSteps);

tmTurn.addEventListener(TimerEvent.TIMER, tmTurn_Move);

tmTurn.addEventListener(TimerEvent.TIMER_COMPLETE, tmTurn_Done);

 

var tmDelay:Timer = new Timer(delayTime,1);

tmDelay.addEventListener(TimerEvent.TIMER_COMPLETE, tmDelay_Done);

This is the main function that handles all motion on each Timer "tick."

function tmTurn_Move(te:TimerEvent):void {

// r is an angle (degrees) measure between 0 and 180, and ang is equivalent radian measure

var r:Number = tmTurn.currentCount/turnSteps*180;

var ang:Number = r*Math.PI/180;

 

// x coordinate moves in a line, y and z move in a sine-curve arc

topCard.x = (150+35*numDealt)*r/180;

topCard.y = turnRadius*Math.sin(ang);

topCard.z = -1.5*turnRadius*Math.sin(ang);

 

// Rotate about the horizontal axis

topCard.rotateView(180-r,"horizontal");

}

When the turning timer is finished, we start the delay timer, which has no TIMER handler because its job is to fire exactly once.

function tmTurn_Done(te:TimerEvent):void {

/* Calling this method sets the topCard to be truly "face up" so the card "knows" its own orientation for possible subsequent actions.*/

topCard.makeFaceUp();

tmDelay.reset();

tmDelay.start();

}

When the delay timer is finished (firing exactly once), we set up for the next card to be dealt until there are five.

function tmDelay_Done(te:TimerEvent):void {

if (numDealt < 5) {

numDealt++;

 

/* Remove new top card from arrDeck and ensure the topCard is above (in the display list) all else on the board. */

topCard = arrDeck.splice(arrDeck.length - 1,1)[0];

board.setChildIndex(topCard,board.numChildren-1);

 

// Start the turning timer

tmTurn.reset();

tmTurn.start();

}

else {

// Show the "Shuffle" button once all five cards are dealt.

btnRepeat.visible = true;

}

}

Handler for a click on the "Deal 5" button:

btnDeal.addEventListener(MouseEvent.CLICK, dealUp);

 

function dealUp(me:MouseEvent):void {

btnDeal.visible = false;

numDealt++;

 

/* Remove new top card from arrDeck and ensure the topCard is above (in the display list) all else on the board. */

topCard = arrDeck.splice(arrDeck.length - 1,1)[0];

board.setChildIndex(topCard,board.numChildren-1);

 

// Start the turning timer

tmTurn.reset();

tmTurn.start();

}

 

btnRepeat.addEventListener(MouseEvent.CLICK, reset);

 

function reset(me:MouseEvent):void {

numDealt = 0;

setupDeck();

btnRepeat.visible = false;

btnDeal.visible = true;

}

Back to AS3 How-Tos and Tips              Back to Flash and Math Home

We welcome your comments, suggestions, and contributions. Click the Contact Us link below and email one of us.

Adobe®, Flash®, ActionScript®, Flex® are registered trademarks of Adobe Systems Incorporated.