Tutorial: Coin Toss using ActionScript 3



Tutorial: Jigsaw using ActionScript 3.0.

This tutorial describes how to build a computer implementation of a jigsaw puzzle. It will demonstrate the use of classes, packages, objects and separate .as files.

Applications

I prepared three versions of the jigsaw. The opening screen of the first version would be something like this (I say 'something like', because the pieces are mixed up by a random process and, presumably, would look different each time):

[pic]

Notice that it has 3 buttons. The player moves pieces by pressing the mouse button down, dragging the piece by moving the mouse, and dropping/releasing control by releasing the button. The player can rotate a piece by pressing the mouse button down and using the left and right arrow keys. The player checks if the puzzle is done by clicking on the Check button. The next screen shot shows the result of clicking on the Build button and then clicking the Check button.

[pic]

The next shows the result of clicking on Check at a different time:

[pic]

The next version of my jigsaw does away with the Check button and incorporates automatic checking whenever the player releases the mouse button. Here is the opening screen:

[pic]

The next screen shot shows the puzzle after the program has determined that it is done:

[pic]

Note that checking for completion in both programs is done with a tolerance factor: the player does not have to be perfect. If you want to make the puzzle more challenging, you can decrease this factor. If you require the player to position things exactly to the pixel, it will be very difficult to complete.

The third version of the jigsaw has something called an attractor or idler. The game begins with the pieces periodically in new, mixed up positions. When the player starts to drag a piece or clicks on a button, the pieces stop moving. If the player does not do anything for a certain period of time, the pieces move again. The idea here is that this application could run on a kiosk and 'attract' people passing by. The term idler comes from the notion that the game is idling. People build idler/attractors that have no connection to the actual application. For jigsaw, mixing up the pieces is an obvious choice for the idling display.

Overview

The critical parts of building this application are

• creating the puzzle pieces, including determining the information that needs to be stored in order to re-create the puzzle

• using the ActionScript 3.0 constructs of class and package to define a class, named Piece, that will specify Piece objects as well as class methods (build, mixup and checkit)

• creating the .fla file, with the piece movie clips and frame code, which will make reference to a package and a class, and also contain buttons and a text field.

You will create the puzzle pieces by taking an image and effectively slicing it up. You move the pieces so that the individual registration point/transformation point is close to the middle of the piece and not where the registration point or transformation point was for the original whole image. Because we (the code) needs to be able to put the pieces together and also check if the pieces are close enough to consider the puzzle done, you need to record coordinate information. The exact procedure is described below.

Note that the checking method does not require the pieces to go in the same position but just be correct (close to correct) relative to each other. Putting this another way, the puzzle can be put together with the top left corner NOT at the top of the screen. Check out the last screen shot to see this.

The coordinate information, referred to here as offsetx and offsety, needs to be bundled together with the movie clip instance itself for each piece. This is a perfect application of class and objects. Classes are ways to define new types of values. They are a way to put together data (variables) and functions (called methods). You have seen these in action for built-in classes whenever you see the dot notation. I haven't used it in these tutorials, but perhaps you know from JavaScript about Date objects. In ActionScript, we have movie clip objects. Objects have properties, also called attributes, and also methods, functions that work with and on the object attributes. Recalling the target movie clip instance in cannonball, I used the code

target.gotoAndPlay(2);

This was making use of the gotoAndPlay method of the target object. The movie clip class also set up attributes for each object, for example, x, y, rotation, and visible. There also are classes that contain methods and variables for the whole class, not for individual objects of the class. The important example of this is the Math class and class methods such as random and floor and class variables such as PI.

Programmers in ActionScript can define their own objects. The terminology/format is as follows: you define a class

modifier class classname {

For any class variables:

modifier static var variable_name:datatype;

For the object variables

modifier var variable_name:datatype;

For the class methods

modifier static function method_name ( ) { }

For the object methods

modifier function method_name( ) { }

one of the methods, which would have the same name as the classname, is the

constructor: the method called when a new object of the class is created.

}

[Note: you do not need to follow a strict order and you can mix these different categories up.]

The modifiers signify how/where the function can be called or the variable referenced. This is done to do what is called information hiding. It isn't really to hide things but to make it easier to find errors and debug code. If the exact details of some implementation are confined to one place, it will be easier to make changes. For this example, certain things will have the modifier public indicating that they can be used outside of the class and others will have the modifier internal meaning that they will only be used inside the class. There are two other possibilities, but I won't get into that here.

Here is some of the code (the whole thing is given below) for the class Piece:

public class Piece {

internal var offsetx: Number;

internal var offsety: Number;

internal var mclip:MovieClip;

public function Piece (xx, yy, clip) {

this.offsetx = xx;

this.offsety = yy;

this.mclip = clip;

}

}

To create an object of class Piece, I use statements such as

var p1:Piece = new Piece(41.6,48.1,piece1);

This will set up an object of type Piece and invoke the function named Piece to set the object variables to hold the coordinates and a reference to the movie clip instance.

There are 3 functions that I need to use that do not apply to individual Piece objects but to all the objects. These are for building a complete puzzle, mixing up the puzzle pieces randomly on the screen and checking if the pieces have been put together reasonably correctly. There probably are many ways to do this. What I did was to set up a class variable that was an array holding all the Piece objects and three class methods: buildit, mixup and checkit.

internal static var pieces:Array = new Array();

The constructor function Piece includes a statement that adds the newly constructed Piece object to this array. The buildit, mixup and checkit functions make use of pieces.

Class definitions are contained in packages. Specifically, each class definition is in its own file and the contents of the file are

package jigsaw {

class Piece {



}

}

The example here has just one class and, therefore, just one file: Piece.as. It MUST go in a folder named jigsaw and I placed it in a folder I named as3 directly in my C drive. See below concerning the specification of the classpath that indicates to the Flash environment where the packages are located.

The code interprets the key stroke event using built-in variables.

Implementation

You need to prepare two files for this application: the .fla file and the .as file that holds the package that holds the Piece definition. The name of the .as file must be Piece.as. It needs to go in a folder with the same name as the package, namely jigsaw, and the jigsaw folder must be in a folder named in the classpath list. I will give instructions on this later.

Start by opening up the Flash environment, under Create New, click on Flash File (ActionScript 3.0)

[pic]

Using the Web or some other program, copy an image to the Clipboard. Back in Flash, click on Insert/New Symbol/ and, making sure that Movie clip is selected, give the symbol the name base. In the panel, click on Edit/Paste. If the image is too big, reduce the size using Transform/Scale or the width and height on the Property panel. The screen shot below shows an image I used Scale on (moving in from the corners) to make be under 550 by 450 in size. By using Scale and the arrows, I kept the proportions (aspect ratio). I also moved the image so that its origin is at the upper left corner.

[pic]

Making sure the whole image is selected, click on Modify/Break apart. This turns the intact image into something you can slice up. You should see something like the screen shot below, with the pixels in the image selected.

[pic]

Sometimes it is necessary to do Modify/Break Apart a second time. Sometimes, even this doesn't work. An alternative is to import the image as a Bitmap and convert it to a vector graphic using the following procedure:

NOTE: Images created within Flash have the characteristic that a patch of the same color can be selected. This is part of so-called vector graphics. Bitmaps do not have this characteristic. To turn a bitmap into a base that you can slice up for your puzzle:

1. Insert New Symbol (movie clip). You can call it base.

2. Click on File/Import/Import to Stage. Then browse to the image file you have identified.

3. Click on Modify/Bitmap/Trace Bitmap. This will produce a vector graphic version of the image.

You now use the pencil tool or the straight line tool to slice up the picture. The screen shot below shows the result of using the pencil, with the smooth option (in between straighten and ink) to draw a curve and then using the arrow to select the cut-out part.

[pic]

Use this method to 'slice up' the picture.

[pic]

Then, do the following for each piece. Select one piece

[pic]

and then click on Edit/Copy. Then Insert/New Symbol. Make sure the movie clip option is selected, and give the pieces the names piece1, piece2, etc. Click on Edit/Paste in Place. Go to Window/Info to make the info panel appear. Make sure that in the Info panel the graphic in the middle shows 3 squares and a circle in the lower right. This indicates the transformation point.

[pic]

The print is small, but notice in my example the numbers for X and Y: 68.5 and 81.3. For each of your pieces, WRITE DOWN piece1 and the two numbers on paper or using a reliable on-line note pad. Then change the numbers to 0 and 0.

You will see your piece jump so that the cross hairs are more towards the middle.

[pic]

Continue with all the pieces OR you can do one or two more and go on to do the rest of the application and then come back.

Bring each piece to the Stage and give each piece an instance name: use piece1, piece2, etc.

Rename the first layer board. Add a new layer and call it actions. Add a third layer and call it interactions. The interactions layer will be above the board layer, meaning that what you are about to put on it will be above any pieces. Add 3 buttons and one dynamic text field using the techniques you have learned from the other tutorials. Name the buttons: buildbtn, mixupbtn and checkbtn and name the text field result. The screen shot shows my lizardplain application after this has been done (and also after code has been put in the actions layer, which I have not shown you yet).

[pic]

Click on the actions layer and Window/Actions to open up the Actions panel. Write as the first line:

import jigsaw.*;

These are instructions to the Flash environment to import everything in the jigsaw package. Since there is only one thing, one class, the Piece class, this statement has the same effect as writing

Import jigsaw.Piece;

You will see later how to let Flash know where to find this package.

The next part of the code is setting up the objects that hold the offset information and a reference to the movie clip instance. I haven't told you the specifics of Piece yet, but it will be available (in the jigsaw package). Here is my code for the lizard puzzle. The numbers here are the numbers you wrote down from the Info panel for each piece.

var p1:Piece = new Piece(41.6,48.1,piece1);

var p2:Piece = new Piece(101.5,48.5,piece2);

var p3:Piece = new Piece(169.0,39.5,piece3);

var p4:Piece = new Piece(42.0,110.5,piece4);

var p5:Piece = new Piece(111.0,122.5,piece5);

var p6:Piece = new Piece(179.6,111.5,piece6);

Again, you can do just 2 or 3 and wait to do the rest or you can do them all now.

The next 3 statements should be familiar to you from the other tutorials. You will set up the event handling for the 3 buttons. The functions invoked here are class methods (static methods) of the Piece class. I haven't shown you that code yet. That comes soon.

mixupbtn.addEventListener(MouseEvent.CLICK,

function(ev) {Piece.mixup();}

);

buildbtn.addEventListener(MouseEvent.CLICK,

function(ev) {Piece.buildit();}

);

checkbtn.addEventListener(MouseEvent.CLICK,

function(ev) {

var res:Boolean;

res = Piece.checkit();

if (res) {

result.text = "Good job!"; }

else {result.text = "Keep working.";}}

);

I want the puzzle to start with the pieces mixed up, so I put in a call to the mixup method.

Piece.mixup();

Lastly, I put very brief instructions into the result text field.

result.text="Use mouse to drag&drop. With mouse down, use arrows to rotate"

Now let's go on to the .as file. Click on File/New and then select ActionScript File.

[pic]

The code is a definition of the Piece class in the package jigsaw. There could be other files with other classes, but for this application, there is just this one file. The outline is

package jigsaw {

import statements indicating what parts of Flash are needed.

public class Piece {

definition of class variables, object variables, class methods and object variables

}

}

There are 3 import statements needed:

import flash.display.*;

import flash.events.*;

import flash.ui.Keyboard;

You may ask: why do we need this? You might accept that it is necessary to indicate in the .fla ActionScript the need for the jigsaw package, but why is it necessary to indicate that I need specific stuff in Flash. The answer is that requiring the import statements prevents certain errors such as referencing a built-in thing when you actually meant a programmer defined thing. It also may make compilation go quicker. If you imported class definitions that you actually did not use, this would not increase the size of the published files.

In ActionScript, you can define something to be a constant by using const in place of var. This means that if the code assigns a value to it, this will be caught as an error. By custom, the names of constants are all caps. I use this for the numbers specifying how much to rotate the movie clip instance for each arrow key stroke.

The coding in Piece.as includes addEventListener statements.

Here are the entire contents of the file named Piece.as.

|package jigsaw{ |Start package |

| import flash.display.*; | Import display |

| import flash.events.*; | Events |

| import flash.ui.Keyboard; | Keyboard |

| | |

| public class Piece { |Start class definition of Piece |

| internal static var pieces:Array = new Array(); |This will hold all the pieces |

| internal static const ROTUNIT= 15; |Defines amount of 1 rotation |

| internal static const ROTNUM = 24; |How many rotations in a circle |

| internal static var dragging:Boolean = false; |Indicates if a piece is being dragged |

| | |

| internal var offsetx:Number = 0; |Object variable |

| internal var offsety:Number = 0; |Object variable |

| internal var mclip:MovieClip; |Object variable: points to the actual movie |

| |clip instance |

| | |

| public function Piece(xx, |Starts constructor for a piece |

| yy, | |

| clip) { | |

| this.offsetx = xx; |Sets object variables |

| this.offsety = yy; | |

| this.mclip = clip; | |

| Piece.pieces.push(this); |Adds to pieces array |

| clip.addEventListener(MouseEvent.MOUSE_DOWN, startdragging); |Sets up for mouse events |

| clip.addEventListener(MouseEvent.MOUSE_UP, stopdragging); | |

| clip.addEventListener(KeyboardEvent.KEY_DOWN,reportKeyDown); |Sets up for keyboard event |

| clip.buttonMode = true; |Sets up so movie clip can get mouse events and|

| |key events |

| } | |

| internal function startdragging(ev) { |Object method |

| this.mclip.startDrag(); | |

| dragging = true; | |

| } | |

| internal function reportKeyDown(ev) { |Object method |

| if (dragging) { | |

| if (ev.keyCode==Keyboard.RIGHT) { | |

| this.mclip.rotation += ROTUNIT; | |

| } | |

| if (ev.keyCode==Keyboard.LEFT) { | |

| this.mclip.rotation -= ROTUNIT; | |

| } |Close if |

| } |Close if dragging |

| } |Close reportKeyDown method |

| internal function stopdragging(ev) { |Object method |

| dragging = false; | |

| this.mclip.stopDrag(); | |

| } | |

| public static function buildit() { |Class method |

| var i:int; | |

| var np:Piece; | |

| for (i=0; i= ROTUNIT) { |Compares rotations to 0 |

| return false; | |

| } |Close if |

| } |Close for loop |

| return true; | |

| } |Close checkit method |

| } |Close class definition |

|} |Close package |

CAUTION: The location of package folders appears to be a delicate matter. The following works on my home computer. See below for instructions for the computer classroom. The name of the package must match where it resides in the computer file structure. I constructed a new folder 'right below' c: called as3 and created a new folder in it called jigsaw. (I used as3 for all my ActionScript packages.) See the location of as3 and jigsaw in the following screen shot.

[pic]

Now that you have saved the Piece.as file in a folder named jigsaw, you need to go back to the .fla file to let Flash know where to find the package. This is called setting the classpath. Click on File/Publish Settings and then Flash, and then next to ActionScript 3.0, the Setting button. Then next to classpath, click on the cross-hairs in a circle to get a browse window. Click on the folder holding the package folder (the parent folder of jigsaw).

[pic]

Spring 2008, Purchase College NS1013 computer lab of PCs: create a new folder called as3 on the D drive, create a new folder called jigsaw (the name of the package), and put the file Piece.as in this folder. In Flash, for the .fla file, under Publish Settings, browse to D:\as3 and click ok to make that the Classpath.

You now can test the program. First save the .fla file. This can be saved anywhere. I saved it as lizardplain. Then, in the usual way, click Control/Test Movie.

Changes for the automatic checking

Now the question is how to implement automatic checking? One aspect of this was easy and another turned out to be more difficult than I initially expected. The first steps are easy: delete the button labeled check and remove the corresponding addEventListener statement. Change the first statement to be

import jigsawauto.*;

I renamed the Text Field instructions. This was not necessary, but I thought it was a better name since that is how the field is used initially.

Save the .fla file as lizardauto.fla. This can be saved in the same folder as lizardplain.fla.

Next, open up the Piece.as file. I then saved this file by going to c:\as3 and creating a new folder called jigsawauto. Save Piece.as in this folder. Note that this means you do not need to change or add to the classpath for lizardauto. It is the same as lizardplain. [Of course, I am not forcing the name lizard on you. You can name the .fla file whatever you want. Since, hopefully, your initial one is working, I would save the new one with a different name.]

This code refers to the TextField datatype, so you need to add the statement

import flash.text.*;

to the import statements at the start of the Piece file.

In the Piece.as file, look at the stopdragging method. This is where I will put the code to call checkit. I change the header for checkit to have the internal modifier, since it is no longer called from outside the class.

internal static function checkit():Boolean {

The idea that did not work was to have the stopdragging method create a Text Field dynamically. This is possible, but it is necessary to do what is called add the Text Field to the display list and there is not a good candidate. This is chiefly for security reasons. So in order to provide a place to write the "Good Job", what I do is have the mixup method pass in the name of the text field already on the board:

Piece.mixup(instructions);

instructions.text=

"Use mouse to drag&drop. With mouse down, use arrows to rotate"

When you make this change, be sure and save the .fla file again.

Now, back to Piece.as, add a new class variable:

internal static var res:TextField;

Change the header and the statement to mixup:

public static function mixup(t:TextField) {



Piece.res = t;

The mixup function also is called during the program as a result of the event of the mix up button being pressed. This means that you must change the addEventListener call

mixupbtn.addEventListener(MouseEvent.CLICK,

function(ev) {Piece.mixup(instructions);}

);

The res class variable will be re-set to the same thing every time mixup is called, but that is okay. It happens at least once. The modified definition of the method stopdragging is the following:

internal function stopdragging(ev) {

dragging = false;

this.mclip.stopDrag();

if (checkit()) {

Piece.res.text = "All Done!";

}

}

This 'says' if checkit returns true, change the text in the text field pointed to by the class variable res to show "All Done!" If the method does not return true, do nothing. That is, don't tell the player to keep working.

Note: the use of Piece is optional within the class, so I could omit it before the res and I could add it before checkit.

Save the file as Piece.as in the jigsawauto folder. Test the program.

When you publish the program for uploading to the web, you upload the .html and the .swf file. As before, you need to upload a file named AC_RunActiveContent.js to the file folder, if that is not already present. This file is created by the Flash environment and will be in the folder with the other files.

Changes for idler/attractor

This application has 2 stages: idling and not idling. During idling, there is a timing event with a short time interval. During not idling, the event is harder to describe. It is the event of nothing happening. This is how I think about it: do you even set an alarm clock and then decide, BEFORE it goes off, to push it further ahead. Say it is set for 8am. You wake up at 7:30 and decide you need at least another hour of sleep, so set the alarm for 8:30. You wake up at 8:15, decide you want at least one more hour, so set the alarm for 9:15. This is how I handle the funny event of nothing happening. I will write code that stops a time and then starts it anew. If the timing event actually occurs, it does so because it has been allowed to get to the end of an interval.

Start by creating a new folder in the parent director of the jigsaw and jigsawauto folders and name it jigsawidler. In Piece.as, change the name of the package to jigsawidler and save it in the jigsawidler folder. You will be making more changes. Go to the .fla file and change the name of the file to lizardidler and save it. You will be making more changes here as well. It is my practice to do this right away to avoid saving on top of a working program.

Back to the Piece.as file (in the jigsawidler folder): here is my strategy. I will have two timers: shorttimer with an interval of 1 second and longtimer with an interval of 20 seconds. I need to add to the import statements. They now are:

import flash.display.*;

import flash.events.*;

import flash.ui.Keyboard;

import flash.text.*;

import flash.utils.Timer;

The timer objects are class variables and not accessed outside of the Piece coding so the modifiers are static and internal:

internal static var shorttimer:Timer = new Timer(1000);

internal static var longtimer:Timer = new Timer(20000);

I define 2 new class (static) functions. One, idler, calls mixup and does things with the timers. Both the timers set up (the term register is used) idler as the event handler. The other, setups, is used one-time-only to send the text field AND something I will describe soon. I could have used such a function in the jigsawauto game. I needed to do it now because of the requirements for event handlers to have one argument being an event. I changed mixup to NOT have any arguments and NOT have the line setting Piece.res.

The idler function is:

public static function idler (ev) {

// only one timer actually will be going

longtimer.stop();

shorttimer.stop();

Piece.mixup();

shorttimer.start();

}

Now the challenge is to find a place to set up (register) the timing events. I decided to do it in the same function used to get the text field address 'over' to where the Piece class instances could use id. The new function to accomplish the task for setting up the text field to be used to display All Done and register the timing events is:

public static function setups(t:TextField) {

Piece.res = t;

shorttimer.addEventListener(TimerEvent.TIMER,Piece.idler);

longtimer.addEventListener(TimerEvent.TIMER,Piece.idler);

}

I added the following two statements to each of the startdragging, buildit and mixup functions.

shorttimer.stop();

longtimer.stop();

There apparently is no harm in stopping a timer that is not started. This greatly simplifies the coding. It is possible to access the status of a timer, but it is not necessary here.

The stopdragging function is the one to start the longtimer. Add this one statement:

longtimer.start();

This one requires patience to test! Be sure both files are saved (and that the .fla file has

import jigsawidler.*;

and the Piece.js file starts with

package jigsawidler {

ActionScript 2.0 to ActionScript 3.0

Only read this if you are familiar with previous versions of Flash and want to reflect on the differences.

As I say in all these tutorials, consider that much is the same: you still need to work with the base image and write down coordinates to be used for the offsetx and offsety values. What is different is how this information is associated with the piece. You need to set up buttons, and you need to set up events and write code for event handling for mouse actions and for key presses. Second, please appreciate the fact that creating the separate Piece files in the jigsaw and jigsawauto folders means that you can produce new jigsaw puzzles quickly. You do not copy code but use the very same files.

Beforehand the offset data was associated with the pieces as part of the frame code of a movie clip symbol. When that proved difficult to do in ActionScript 3.0, I decided to use objects. I worked to make the interactions and the specifics of each jigsaw puzzle part of the .fla file and the general stuff concerning how jigsaw puzzles work part of the Piece.as files. The created a small challenge when I wanted to put the generation of the All Done message into the Piece code, but I solved it by passing a reference to the text field.

This application demonstrates what is termed the composition relation between objects: the movie clip symbol instances that are the pieces are referenced as object variables in the Piece object. In other applications (for example, bouncing things), you will see another type of relationship of objects, specifically inheritance.

The idler/attractor jigsaw made use of the Timer class. This implementation seems simpler to me, but I haven't really gone back to study the older versions. Setting up the two timers and thinking about having two alarm clocks that didn't 'mind' being turned off even if they were off, helped me. Note that creating a Timer does involve adding more to an application than using multiple frames or the Enter_Frame event. For this application, I did use 2 Timer objects. It is possible to change the timing interval. The attribute is named delay, so instead of defining two timers, I could have declared just one, call it mytimer, and had code

mytimer.delay = 1000;

and

mytimer.delay = 20000;

My not so economical approach appears to be acceptable.

Please send comments and suggestions.

................
................

In order to avoid copyright disputes, this page is only a partial summary.

Google Online Preview   Download