Tutorial: Jigsaw using ActionScript 3.0.

slhscompsci.net

Tutorial: Jigsaw using ActionScript 3.0.

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):

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.


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


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:


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


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)


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.


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.


Sometimes it is necessary to do Modify/Break Apart a second time. 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.

Use this method to 'slice up' the picture.


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

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.


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.

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).


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.


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 = How many rotations in a

24;

circle

internal static var

Indicates if a piece is being

dragging:Boolean = false;

dragged

0;

0;

internal var offsetx:Number =

internal var offsety:Number =

Object variable

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.MOUS

E_DOWN, startdragging);

Sets up for mouse events


clip.addEventListener(MouseEvent.MOUS

E_UP, stopdragging);

Sets up for keyboard event

clip.addEventListener(KeyboardEvent.K

EY_DOWN,reportKeyDown);

clip.buttonMode = true; Sets up so movie clip can get

mouse events and key events

}

internal function

Object method

startdragging(ev) {

this.mclip.startDrag();

dragging = true;

}

internal function

Object method

reportKeyDown(ev) {

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

Object method

stopdragging(ev) {

dragging = false;

this.mclip.stopDrag();

}

public static function

Class method

buildit() {

var i:int;

var np:Piece;

for (i=0;

For each of the pieces

i


public static function mixup() Class method

{

var i:int;

var np:Piece;

for (i=0;

For each piece

itolerance))

{

return false;

} Close if

if Compares rotations to 0


(Math.abs(np.mclip.rotation)>=

ROTUNIT) {

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.

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).

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

and

mytimer.delay = 1000;

mytimer.delay = 20000;

My not so economical approach appears to be acceptable.

Please send comments and suggestions.

Similar magazines