8bitrocket.com
2Dec/090

AS2->AS3: Making A Click And Stick Christmas Tree Designer In AS2 and AS3

treeimage.jpg

In my last tutorial I focused on the differences in managing the mouse in AS2 and AS3 and how to create a click and drag effect in AS2 and AS3. This tutorial will take the code in those tutorials further to make a simple Christmas Tree designer in AS2 and AS3. This Christmas Tree designer is based (in part) on this viral Christmas Tree eCard designer I launched couple Decembers ago which, in turn, was a re-write of one of my first ever Flash applications I made in the year 2000. This Christmas season, instead of just re launching the same thing again, I've decided to try to show you, our faithful reader, how to make one yourself...in both AS2 and AS3.

The application we are going to create will do the following:

  1. Display a Christmas Tree graphic on the screen
  2. Display a red bulb and a green bulb on the screen
  3. Display a trash can on the screen
  4. Allow the user to click on the red or green bulb to create a red or green bulb
  5. Allow the user to move the bulbs using the "Click and Stick" method to decorate the Christmas tree.
  6. Allow the user to drag bulbs to the trash can to be deleted
Flash AS2

In The Library

The following assets are in the .fla library for this application:

  • Christmas Tree: a MovieClip exported as the text identifier tree
  • Red Bulb: a MovieClip with the graphic centered on the registration point, exported as the text identifier redbulb
  • Green Bulb: a MovieClip with the graphic centered on the registration point, exported as the text identifier greenbulb
  • Trash Can: a MovieClip exported as the text identifier trash

Creating The XmasAs2 Class

Since we are going to create a class that runs the application in AS2, we need to create an instance of that class in the first frame of the main timeline. The following code does that.

[cc lang="javascript" width="550"]game = new XmasAs2(this);
game._x = 0;
game._y = 0;
[/cc]

In the XmasAs2.as file, we will start by defining our class, and the class variables we will need for it. The variables we will use are as follows:

  • bulbArray: an array that will hold the list of bulbs the user has created and put on the screen
  • bulbCount: the number if bulbs the user has created. This is kept so we can create a new unique depth for each new object created
  • tree: A reference to the Christmas tree
  • trash: a reference to the trash can
  • greenbulb: A reference to the green bulb "button" users will click on to create new green bulbs.
  • redbulb: A reference to the red bulb "button" users will click on to create new red bulbs.
  • timeline: A reference to the main timeline to which we will attach MovieClips
  • parent: A dummy variable used by the bulbs as reference to the instance of XmasAs2 running the application.
  • dragging: A dummy variable used by bulbs to set if they are the bulb currently being dragged on the screen.
[cc lang="javascript" width="550"]
class XmasAs2 extends MovieClip {

var bulbArray:Array = new Array();
var bulbCount:Number;
var tree:MovieClip;
var trash:MovieClip;
var greenbulb:MovieClip;
var redbulb:MovieClip;
var timeline:MovieClip;
var parent:MovieClip;
var dragging:Boolean; }
[/cc]

Displaying Objects On The Screen/Setting Events And Callbacks

The constructor for XmasAs2 requires that a reference to the main timeline be passed to it. This is because we need a place to attach the objects we create from the library when we call attachMovie() for tree, greenbulb, redbulb, trash, and the bulbs we will create on user demand later.

We also need to set a dynamic variable in both redbulb and greenbulb named parent that is reference back to this instance of XmasAs2. Why? Well, because when redbulb and greenbulb are clicked, we need to call a function in XmasAs2 to create a new instance of a bulb. Since redbulb and greenbulb calla local function inside of XmasAs2 when their onPress event fires ( eventCreateNewBulb) that function will run in the context of those MovieClips, even though it lives in the XmasAs2 class. We need a simple way to get back to the instance of XmasAs2, and this is the way we do it. We could have created a Delegate to allow the buttons to run in their own context, or created the buttons as their own classes, but that would have created other issues and made the code even more complicated than it is already.

[cc lang="javascript" width="550"]
function XmasAs2(tl:MovieClip) {
timeline=tl;
tree = timeline.attachMovie("tree","tree1",1000);
trash = timeline.attachMovie("trash","trash1",500);
greenbulb = timeline.attachMovie("greenbulb","greenbulb1", 600);
redbulb = timeline.attachMovie("redbulb","redbulb1",700);
greenbulb.parent = this;
redbulb.parent = this;
tree._x = 150;
tree._y = 50;
trash._x =10;
trash._y = 300;
greenbulb._x = 450;
greenbulb._y = 100;
redbulb._x = 450;
redbulb._y = 150;
greenbulb.onPress = eventCreateNewBulb;
redbulb.onPress = eventCreateNewBulb;
bulbCount = 0;
}

[/cc]

 

Click And Stick Bulbs

When redbulb or greenbulb are clicked they spawn an event that then calls the eventCreateNewBulb() function, which in turn, calls parent.createNewBulb() in XmasAs2 passing a reference to itself (this). This gets out of the button context and back to the context the instance of XmasAs2.

createNBewBulb() uses the reference to redbulb or greenbulb passed to it to determine which bulb was clicked and which color bulb to create. It does this by checking the the _name property that was set was we used attachMovie() to create them. Depending on which one was clicked, we then use attachMovie() to create a new instance of the bulb. Since we need a running set of depths for each bulb we create, we create a function named getNextDepth() that uses bulbCount as a running total. It returns a depth that can be used for the object.

We then need to attach the newly created "bulb" to the mouse. This is the "stick" of the "click and stick". The bulb will stay attached until the user clicks the mouse button again. We do this by calling the startDrag() function of tempBulb. To make sure the bulb drops when the user clicks again (and can be picked up and moved later), we set the onPress listener to call the function dragTest. We also set the dynamic variable dragging to true (because the object is now being dragged), and create a dynamic variable for parent so we can reference the instance of XmasAS2 from the context of the bulb.

The dragTest() function is called whenever the user clicks on a bulb. This can happen when the user wants to drop a bulb that is stuck to the mouse or pick-up a bulb already on the screen. This function tests the dragging property of the bulb. If it is true, it calls stopDrag() set dragging to false, and then calls parent.testTrash(this) to see if the player has dropped the bulb on the trash can. If the dragging is false, we call startDrag(), and set the dragging property true.

[cc lang="javascript" width="550"] 
function eventCreateNewBulb() {
parent.createNewBulb(this);
}

function createNewBulb(bulb:MovieClip) {
var tempBulb:MovieClip;
var tempDepth = getNextDepth();
if (bulb._name == "greenbulb1") {
tempBulb = timeline.attachMovie("greenbulb","greenbulb" + tempDepth, tempDepth);
} else if (bulb._name == "redbulb1") {
tempBulb = timeline.attachMovie("redbulb","redbulb" + tempDepth, tempDepth);

}
tempBulb.onRelease = dragTest;
tempBulb.startDrag(true);
tempBulb.dragging = true;
tempBulb.parent = this;
bulbArray.push(tempBulb);

}

function dragTest() {

if (this.dragging) {
this.stopDrag();
this.dragging = false; parent.testTrash(this);
} else {

this.startDrag(true);
this.dragging = true;
}

}

function getNextDepth() {
bulbCount++;
return 2000+bulbCount;
}
[/cc]

 

Deleting Objects

When a user drops a bulb, we call testTrash() to see if the bulb was dropped on the trashcan. To do this ,we use the hitTest() function of the trashcan MovieClip to test to see if the bounding boxes of the bulb and trashcan are touching. If so, we loop through the bulbArray until we find the bulb dropped,use removeMoveClip() to get it off the screen, and then splice it out of the bulbArray.

[cc lang="javascript" width="550"]

function testTrash(bulb) {
if (bulb.hitTest(trash)) {
for (var i:Number = bulbArray.length-1; i >=0;i ) {
if (bulbArray[i] == bulb) {
bulb.removeMovieClip();
bulbArray.splice(i,1);
}
}
}
}
[/cc]

Full code

That's all there is to it. Here is the full code for the AS2 version.

[cc lang="javascript" width="550"]class XmasAs2 extends MovieClip {

var bulbArray:Array = new Array();
var bulbCount:Number;
var tree:MovieClip;
var trash:MovieClip;
var greenbulb:MovieClip;
var redbulb:MovieClip;
var timeline:MovieClip;
var parent:MovieClip;
var dragging:Boolean;

function XmasAs2(tl:MovieClip) {
timeline=tl;
tree = timeline.attachMovie("tree","tree1",1000);
trash = timeline.attachMovie("trash","trash1",500);
greenbulb = timeline.attachMovie("greenbulb","greenbulb1", 600);
redbulb = timeline.attachMovie("redbulb","redbulb1",700);
greenbulb.parent = this;
redbulb.parent = this;
tree._x = 150;
tree._y = 50;
trash._x =10;
trash._y = 300;
greenbulb._x = 450;
greenbulb._y = 100;
redbulb._x = 450;
redbulb._y = 150;
greenbulb.onPress = eventCreateNewBulb;
redbulb.onPress = eventCreateNewBulb;
bulbCount = 0;
}

function eventCreateNewBulb() {
parent.createNewBulb(this);
}

function createNewBulb(bulb:MovieClip) {
var tempBulb:MovieClip;
var tempDepth = getNextDepth();
if (bulb._name == "greenbulb1") {
tempBulb = timeline.attachMovie("greenbulb","greenbulb" + tempDepth, tempDepth);
} else if (bulb._name == "redbulb1") {
tempBulb = timeline.attachMovie("redbulb","redbulb" + tempDepth, tempDepth);

}
tempBulb.onRelease = dragTest;
tempBulb.startDrag(true);
tempBulb.dragging = true;
tempBulb.parent = this;
bulbArray.push(tempBulb);

}

function dragTest() {

if (this.dragging) {
this.stopDrag();
this.dragging = false;
parent.testTrash(this);
} else {

this.startDrag(true);
this.dragging = true;
}
;
}

function testTrash(bulb) {
if (bulb.hitTest(trash)) {
for (var i:Number = bulbArray.length-1; i >=0;i ) {
if (bulbArray[i] == bulb) {
bulb.removeMovieClip();
bulbArray.splice(i,1);
}
}
}
}

function getNextDepth() {
bulbCount++;
return 2000+bulbCount;
}
}
[/cc]

Test The Application

Flash AS3

In The Library

The following assets are in the .fla library for this application. the main difference from the AS2 application is that these are now class references and not text identifiers, and we have appended an "o" to the front of the names so the classes they they don't conflict with the variables we create for the objects.

  • Christmas Tree: a MovieClip exported as otree
  • Red Bulb: a MovieClip with the graphic centered on the registration point, exported as oredbulb
  • Green Bulb: a MovieClip with the graphic centered on the registration point, exported as ogreenbulb
  • Trash Can: a MovieClip exported as otrash

 

Creating The XmasAs3 Class

Unlike AS2 where we had to create an instance of the application class on the timeline, in AS3 this can happen automatically. All we need to to do is set the document property of the main timeline to: XmasAs3.

The XmasAs3.as file contains some important differences from it's AS2 counterpart. First, we need to specify a package in AS3, which is required for all classes. We also need to import the classes we are using before we specify the class identifier. However, because AS3 does things a bit more efficiently than AS2, we have fewer class properties to deal with. In fact, the main difference is that we don't need any variables to keep track of object depths (bulbCount), and we don't need those confusing dummy variables used by the bulb objects (parent, dragging), and we don't need a reference to the main timeline (timeline)because our class *is* the main timeline!

[cc lang="javascript" width="550"]package {
import flash.display.MovieClip;
import flash.ui.Mouse;
import flash.events.MouseEvent;

public class XmasAs3 extends MovieClip {

var bulbArray:Array = new Array();
var tree:MovieClip;
var trash:MovieClip;
var greenbulb:MovieClip;
var redbulb:MovieClip;

}

}[/cc]

Displaying Objects On The Screen / Setting Events And Callbacks

Compared to the constructor for XmasAs2, the constructor for XmasAs3 is clean and simple. We don't have to use attachMovie() in AS#, nor do we have to create arbitrary "depths" to assign to each object. Instead, we just create instances of our object using class instantiation notation (i.e. tree = new otree()), position them on the screen the same way we did in AS2 (except we use the .x and .y properties instead of ._x and ._y), and add them to the main timeline represented by this instance of XmasAs3 by calling this.addChild().

There are a few interesting differences thought that need to be highlighted. First, we are going to listen for the MOUSE_DOWN event on both redbulb and greenbulb because onPress does not exist any longer in AS3.

[cc lang="javascript" width="550"]public function XmasAs3() {

tree = new otree();
trash = new otrash();
greenbulb = new ogreenbulb();
redbulb = new oredbulb();
tree.x = 150;
tree.y = 50;
trash.x =10;
trash.y = 300;
greenbulb.x = 450;
greenbulb.y = 100;
redbulb.x = 450;
redbulb.y = 150;

this.addChild(tree);
this.addChild(trash);
this.addChild(greenbulb);
this.addChild(redbulb);

greenbulb.addEventListener(MouseEvent.MOUSE_DOWN, createNewBulb);
redbulb.addEventListener(MouseEvent.MOUSE_DOWN, createNewBulb);

greenbulb.clr = "green";
redbulb.clr ="red";
greenbulb.buttonMode = true;
greenbulb.useHandCursor = true;
redbulb.buttonMode = true;
redbulb.useHandCursor = true;
}
[/cc]

Click And Stick Bulbs

Clicking and sticking bulbs in AS3 is a bit more straight forward than in AS2. createNewBulb() is stilled called when redbulb or greenbulb are clicked, but instead of testing the _name property, we test a dynamic property named clr to that we set in the constructor. This could be taken even further to have separate class for the bulbs with an instance variable for clr.

After creating the proper instance of the bulb, this function operates very similar to the AS2 version. We add a listener for the MOUSE_DOWN event (because again, onPress does not exist in AS3), and we also set buttonMode and useHandCursor both to true to simulate the functionality of the AS2 buttons. We don't need to call getNextDepth() because this.addChild() does not require it, so we have omitted that function from the AS3 version.

The dragTest() function is also simpler and more straight forward in AS3 than in AS2. Since it is called from a mouse event, we get an instance of the MouseEvent class passed to the function, and it contains a magic property named target that represents the object clicked upon. Instead of having to pass around a reference to multiple functions, we can use this property. Also, in AS3 event callback functions execute in the context of the object that set them (XmasAs3), not the target object (bulb). This means we don't need to pass around a reference to parent, we just operate inside the function directly.

[cc lang="javascript" width="550"]public function createNewBulb(e:MouseEvent):void {
var tempBulb:MovieClip;
if (e.target.clr == "green") {
tempBulb = new ogreenbulb();
} else if (e.target.clr == "red") {
tempBulb = new oredbulb();

}
tempBulb.addEventListener(MouseEvent.MOUSE_DOWN, dragTest);
tempBulb.startDrag(true);
tempBulb.dragging = true;
tempBulb.buttonMode = true;
tempBulb.useHandCursor = true;
this.addChild(tempBulb);
bulbArray.push(tempBulb);

}

public function dragTest(e:MouseEvent):void {
if (e.target.dragging) {
e.target.stopDrag();
e.target.dragging = false;
} else {
e.target.startDrag(true);
e.target.dragging = true;
}
testTrash(MovieClip(e.target));
}
[/cc]

Deleting Objects

Deleting an object is AS3 is nearly identical to AS2. The main difference is that the hitTest() MovieClip method no longer exists in AS3. It has been replaced with hitTestObject() and hitTestPoint(). hitTestObject() is essentially the same as hitTest(), so we use that instead.

[cc lang="javascript" width="550"]public function testTrash(bulb:MovieClip):void {
if (bulb.hitTestObject(trash)) {
for (var i:Number = bulbArray.length-1; i >=0;i ) {
if (bulbArray[i] == bulb) {
this.removeChild(bulb);
bulbArray.splice(i,1);
}
}
}
}
[/cc]

Full code

And that's it! Here is the full code in AS3.

[cc lang="javascript" width="550"]package {
import flash.display.MovieClip;
import flash.ui.Mouse;
import flash.events.MouseEvent;

public class XmasAs3 extends MovieClip {

var bulbArray:Array = new Array();
var tree:MovieClip;
var trash:MovieClip;
var greenbulb:MovieClip;
var redbulb:MovieClip;

public function XmasAs3() {

tree = new otree();
trash = new otrash();
greenbulb = new ogreenbulb();
redbulb = new oredbulb();
tree.x = 150;
tree.y = 50;
trash.x =10;
trash.y = 300;
greenbulb.x = 450;
greenbulb.y = 100;
redbulb.x = 450;
redbulb.y = 150;

this.addChild(tree);
this.addChild(trash);
this.addChild(greenbulb);
this.addChild(redbulb);

greenbulb.addEventListener(MouseEvent.MOUSE_DOWN, createNewBulb);
redbulb.addEventListener(MouseEvent.MOUSE_DOWN, createNewBulb);

greenbulb.clr = "green";
redbulb.clr ="red";
greenbulb.buttonMode = true;
greenbulb.useHandCursor = true;
redbulb.buttonMode = true;
redbulb.useHandCursor = true;

}

public function createNewBulb(e:MouseEvent):void {
var tempBulb:MovieClip;
if (e.target.clr == "green") {
tempBulb = new ogreenbulb();
} else if (e.target.clr == "red") {
tempBulb = new oredbulb();

}
tempBulb.addEventListener(MouseEvent.MOUSE_DOWN, dragTest);
tempBulb.startDrag(true);
tempBulb.dragging = true;
tempBulb.buttonMode = true;
tempBulb.useHandCursor = true;
this.addChild(tempBulb);
bulbArray.push(tempBulb);

}

public function dragTest(e:MouseEvent):void {
if (e.target.dragging) {
e.target.stopDrag();
e.target.dragging = false;
} else {
e.target.startDrag(true);
e.target.dragging = true;
}
testTrash(MovieClip(e.target));
}

public function testTrash(bulb:MovieClip):void {
if (bulb.hitTestObject(trash)) {
for (var i:Number = bulbArray.length-1; i >=0;i ) {
if (bulbArray[i] == bulb) {
this.removeChild(bulb);
bulbArray.splice(i,1);
}
}
}
}

}
}
[/cc]

Test The Application

Dowload the full code for this tutorial here.

 

 

If you enjoyed this post, please consider leaving a comment or subscribing to the RSS feed to have future articles delivered to your feed reader.
Comments (0) Trackbacks (0)

No comments yet.


Leave a comment

No trackbacks yet.

This site is protected by Comment SPAM Wiper.