Build a Dynamic Flash Game Menu: Slides
Stop using static menus! Most players immediately base their initial impression of a Flash game on the menu that they see when they load it. Stand out from the crowd with a dynamic menu!
Final Result Preview
Introduction: Static to Dynamic
The word “static” essentially means lacking in change. The majority of menus we see throughout web games are lacking in change, you simply press Play and the game starts. Menus like that are overused and show little creativity or innovation.
To make a menu “dynamic” we must continuously cause change. So in this tutorial that is exactly what we are going to accomplish: a menu that continuously changes.
Step 1: Setting Up
The first thing we are going to need to create is a new Flash File (ActionScript 3.0). Set its width to 650px, its height to 350px, and the frames per second to 60. The background color can be left as white.
Now save the file; you can name it whatever you please, but I named mine menuSlides.fla.
In the next section we will create the nine MovieClips used in the menu. For reference, here is a list of all the colors used throughout the tutorial:
- White – #FFFFFF
- Gold – #E8A317
- Light Gold – #FBB917
- Blue – #1569C7
- Light Blue – #1389FF
- Green – #347235
- Light Green – #3E8F1B
- Red – #990000
- Light Red – #C10202
- Matte Grey – #222222
Step 2: Creating the Slide MovieClips
To start with we will create the slides used in the transitions, but before we begin let’s turn on some very useful Flash features.
Right-Click the stage and select Grid > Show Grid. By default it will create a 10px by 10px grid across the stage. Next, right-click the stage again and this time select Snapping > Snap to Grid.
Now we can begin drawing! Select the Rectangle Tool and draw a Light Gold rectangle, 650px wide and 350px tall (you can Alt-click on the stage to make this easier). Now change the color to Gold and draw groups of three squares, each 20x20px, to form the shape of an L in each corner,:

Select the whole stage, right-click and choose Convert to Symbol. Name the MovieClip goldSlide and make sure that the type is MovieClip and the registration is top-left.
To save time and make things a whole lot easier, right-click the goldSlide MovieClip in the Library and select Duplicate Symbol three times to make three more copies. Change the colors in the new MovieClips to blue, green and red, then rename the MovieClips to blueSlide, greenSlide and redSlide.
Before we continue we should add some text to each slide. On goldSlide write PLAY, on blueSlide write INSTRUCTIONS, on greenSlide write OPTIONS and on redSlide write CREDITS.
Now that we have the text in place we can break it apart by right-clicking on it and selecting Break Apart twice; this will break the text down to a fill which will transition more smoothly. Plus as a bonus there will be no need to embed a font if you are just using it for the menu!
The Buttons
Now that we have drawn the 4 slides we can focus on the sideButton MovieClip that is used to move the slides either left or right.
First, draw a rectangle 30x60px with only a stroke (no fill), then draw diagonal lines 45 degrees from the top-right and bottom-right corners until they snap together in the middle of the opposite side. Now apply a Matte Grey fill to the triangle:

Next, delete the lines, then right-click the triangle and select Convert to Symbol. Name it sideButton, set the type to Button and make sure the registration is in the top-left corner.
Now insert 3 keyframes in the timeline by right-clicking the timeline and selecting Insert Keyframe. On the Up frame, select the fill of the triangle, go to the Windows tab and select Color. Change the Alpha to 50%. On the Over Frame repeat the same process, but this time set the alpha to 75%.
Now we can begin on the four numbered circle buttons, for jumping directly to a particular slide.
To start draw a white 30px circle with no stroke. Convert it to a symbol, name it circleOne, and set its type to Button and its registration point to the center. Insert three keyframes like we did before and then go to the Up frame.
Draw a black 25px circle with no stroke and center it to the middle through the coordinates or by using the Align menu. Next deselect the black circle, then reselect it and delete it. You should now have a white ring remaining. Now grab the text tool and put a white “1″ in the center of the ring. Then break this number apart until it is a fill.

Go to the Over frame and draw a black “1″. Center it and break it apart until it becomes a fill. Now deselect and reselect the fill, then delete it. Select everything on the frame and copy it, then go to the Down frame, select everything on it and hit delete. Paste in what we have copied.

Now create three more circle MovieClips, following the same process, for the numbers 2, 3 and 4.
Step 3: Positioning the MovieClips
Okay, we’re almost half-way done! First drag all of the slides onto the stage and position them with the following coordinates:
- goldSlide: (0, 0)
- blueSlide: (650, 0)
- greenSlide: (1300, 0)
- redSlide: (1950, 0)
Now drag and drop two copies of the sideButton. The first copy should be positioned at (10,145); before we can position the second copy we must first flip it!
Select the second copy and press Ctrl-T. Change the left-right to -100% and leave the up-down at 100%. Now move the second copy to (640,145).
Finally drag and drop the four circle MovieClips and position them as so:
- circleOne: (30, 320)
- circleTwo: (70, 320)
- circleThree: (110, 320)
- circleFour: (150, 320)
Your stage should now look like this:

The blue, green and red slides are hidden just off to the right of the stage. Now select everything on the stage and convert to a symbol. Name it menuSlidesMC, set the type to MovieClip and the registration to the top-left corner, and export it for ActionScript as MenuSlidesMC.
Before we finish we must give each of the MovieClips inside menuSlidesMC an instance name. Select each slide in the order they appear from the left and name them slide1, slide2, slide3 and slide4 respectively. Name the circle buttons one, two, three and four, and finally name the side buttons left and right.
Step 4: Setting Up the Classes
Now that all of our MovieClips have been created we can start setting up the two classes we are going to use.
First go to your Flash file’s Properties and set its class to menuSlides; then, create a new ActionScript 3.0 file and save it as menuSlides.as.
Now copy the following code into it; I will explain it after:
package{
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.MouseEvent;
public class menuSlides extends MovieClip{
public var menuSlidesMC:MenuSlidesMC = new MenuSlidesMC();
public function menuSlides(){
addChild(menuSlidesMC);
}
}
}
Pretty basic – it’s a document class, into which we imported the MovieClips and Events we will use. Then we created an instance of MenuSlidesMC, and added it to the stage.
Now create a new ActionScript 3.0 file for the menuSlidesMC instance. Save it as MenuSlidesMC.as and copy the following code into it:
package{
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.MouseEvent;
public class MenuSlidesMC extends MovieClip{
public var speed:Number = new Number();
public var activeSlide:Number = new Number();
public function MenuSlidesMC(){
speed = 10;
activeSlide = 1;
addEventListener(MouseEvent.CLICK, slidesClick);
addEventListener(Event.ENTER_FRAME, slidesMove);
}
}
}
Just like last time, we imported what we are going to need, but we created two number variables. The first variable, speed, is actually how many pixels the slides are moved by each frame. (Note: this number has to evenly divide into your stage’s width to give a smooth transition). The second variable, activeSlide, tells us which slide is currently set to be on screen.
We also added two event listeners for functions we are going to create; one of them is called on a mouse click, and the other is called at the beginning of every frame.
Step 5: Creating the Event Handler Functions
To begin we will get the mouse click function out of the way. Start by creating a public function named slidesClick():
public function slidesClick(event:MouseEvent):void {
}
Next we will create some if-statements regarding the event.target.name. Basically, this property stores the name of the object that was targeted by the mouse click. We can use this to check which button is pressed:
if(event.target.name == "left"){
if(activeSlide>1){
activeSlide-=1;
}
}else if(event.target.name == "right"){
if(activeSlide<4){
activeSlide+=1;
}
}
if(event.target.name == "one"){
activeSlide=1;
}else if(event.target.name == "two"){
activeSlide=2;
}if(event.target.name == "three"){
activeSlide=3;
}else if(event.target.name == "four"){
activeSlide=4;
}
The code above goes in the slidesClick() function. The first set of if-statements are for the left and right side buttons; they increase or decrease the value of activeSlide, but never allow the value to become less than 1 or greater than 4 (since we only have four slides). The second set of if-statements are for the circle buttons; instead of just incrementing or decrementing the value of activeSlide they set it to the selected value.
Now let’s begin with the ENTER_FRAME handler function:
public function slidesMove(event:Event):void {
}
Add the slidesMove() function below your slidesClick() function and we’ll start adding some code to it. First, we’ll use a switch to check which slide should be on the screen, based on the value of activeSlide:
switch (activeSlide){
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
}
Now in each case we will create an if/else block that will check that slide’s current x-position, and move all of the slides either left, right, or not at all, depending on where the desired slide currently sits.
The first case looks like this:
if(slide1.x<0){
slide1.x+=speed;
slide2.x+=speed;
slide3.x+=speed;
slide4.x+=speed;
}else if(slide1.x>0){
slide1.x-=speed;
slide2.x-=speed;
slide3.x-=speed;
slide4.x-=speed;
}
Now all we have to do is repeat the same process for the other cases! After you are done your swtich should look like this:
switch (activeSlide){
case 1:
if(slide1.x<0){
slide1.x+=speed;
slide2.x+=speed;
slide3.x+=speed;
slide4.x+=speed;
}else if(slide1.x>0){
slide1.x-=speed;
slide2.x-=speed;
slide3.x-=speed;
slide4.x-=speed;
}
break;
case 2:
if(slide2.x<0){
slide1.x+=speed;
slide2.x+=speed;
slide3.x+=speed;
slide4.x+=speed;
}else if(slide2.x>0){
slide1.x-=speed;
slide2.x-=speed;
slide3.x-=speed;
slide4.x-=speed;
}
break;
case 3:
if(slide3.x<0){
slide1.x+=speed;
slide2.x+=speed;
slide3.x+=speed;
slide4.x+=speed;
}else if(slide3.x>0){
slide1.x-=speed;
slide2.x-=speed;
slide3.x-=speed;
slide4.x-=speed;
}
break;
case 4:
if(slide4.x<0){
slide1.x+=speed;
slide2.x+=speed;
slide3.x+=speed;
slide4.x+=speed;
}else if(slide4.x>0){
slide1.x-=speed;
slide2.x-=speed;
slide3.x-=speed;
slide4.x-=speed;
}
break;
}
And that’s it! We are all finished with the code and the menu should be working great right now.
Conclusion
Thank you for taking the time to read through the tutorial, I hope it was helpful and that you learned a little something about dynamic menus.
View full post on Activetuts+

Stop using static menus! Most players immediately base their initial impression of a Flash game on the menu that they see when they load it. Stand out from the crowd with a dynamic menu!
Final Result Preview
Introduction: Static to Dynamic
The word “static” essentially means lacking in change. The majority of menus we see throughout web games are lacking in change, you simply press Play and the game starts. Menus like that are overused and show little creativity or innovation.
To make a menu “dynamic” we must continuously cause change. So in this tutorial that is exactly what we are going to accomplish: a menu that continuously changes.
Step 1: Setting Up
The first thing we are going to need to create is a new Flash File (ActionScript 3.0). Set its width to 650px, its height to 350px, and the frames per second to 60. The background color can be left as white.
Now save the file; you can name it whatever you please, but I named mine
menuSlides.fla.In the next section we will create the nine MovieClips used in the menu. For reference, here is a list of all the colors used throughout the tutorial:
Step 2: Creating the Slide MovieClips
To start with we will create the slides used in the transitions, but before we begin let’s turn on some very useful Flash features.
Right-Click the stage and select Grid > Show Grid. By default it will create a 10px by 10px grid across the stage. Next, right-click the stage again and this time select Snapping > Snap to Grid.
Now we can begin drawing! Select the Rectangle Tool and draw a Light Gold rectangle, 650px wide and 350px tall (you can Alt-click on the stage to make this easier). Now change the color to Gold and draw groups of three squares, each 20x20px, to form the shape of an L in each corner,:
Select the whole stage, right-click and choose Convert to Symbol. Name the MovieClip
goldSlideand make sure that the type is MovieClip and the registration is top-left.To save time and make things a whole lot easier, right-click the
goldSlideMovieClip in the Library and select Duplicate Symbol three times to make three more copies. Change the colors in the new MovieClips to blue, green and red, then rename the MovieClips toblueSlide,greenSlideandredSlide.Before we continue we should add some text to each slide. On goldSlide write PLAY, on blueSlide write INSTRUCTIONS, on greenSlide write OPTIONS and on redSlide write CREDITS.
Now that we have the text in place we can break it apart by right-clicking on it and selecting Break Apart twice; this will break the text down to a fill which will transition more smoothly. Plus as a bonus there will be no need to embed a font if you are just using it for the menu!
The Buttons
Now that we have drawn the 4 slides we can focus on the
sideButtonMovieClip that is used to move the slides either left or right.First, draw a rectangle 30x60px with only a stroke (no fill), then draw diagonal lines 45 degrees from the top-right and bottom-right corners until they snap together in the middle of the opposite side. Now apply a Matte Grey fill to the triangle:
Next, delete the lines, then right-click the triangle and select Convert to Symbol. Name it
sideButton, set the type to Button and make sure the registration is in the top-left corner.Now insert 3 keyframes in the timeline by right-clicking the timeline and selecting Insert Keyframe. On the Up frame, select the fill of the triangle, go to the Windows tab and select Color. Change the Alpha to 50%. On the Over Frame repeat the same process, but this time set the alpha to 75%.
Now we can begin on the four numbered circle buttons, for jumping directly to a particular slide.
To start draw a white 30px circle with no stroke. Convert it to a symbol, name it
circleOne, and set its type to Button and its registration point to the center. Insert three keyframes like we did before and then go to the Up frame.Draw a black 25px circle with no stroke and center it to the middle through the coordinates or by using the Align menu. Next deselect the black circle, then reselect it and delete it. You should now have a white ring remaining. Now grab the text tool and put a white “1″ in the center of the ring. Then break this number apart until it is a fill.
Go to the Over frame and draw a black “1″. Center it and break it apart until it becomes a fill. Now deselect and reselect the fill, then delete it. Select everything on the frame and copy it, then go to the Down frame, select everything on it and hit delete. Paste in what we have copied.
Now create three more circle MovieClips, following the same process, for the numbers 2, 3 and 4.
Step 3: Positioning the MovieClips
Okay, we’re almost half-way done! First drag all of the slides onto the stage and position them with the following coordinates:
Now drag and drop two copies of the sideButton. The first copy should be positioned at (10,145); before we can position the second copy we must first flip it!
Select the second copy and press Ctrl-T. Change the left-right to -100% and leave the up-down at 100%. Now move the second copy to (640,145).
Finally drag and drop the four circle MovieClips and position them as so:
Your stage should now look like this:
The blue, green and red slides are hidden just off to the right of the stage. Now select everything on the stage and convert to a symbol. Name it
menuSlidesMC, set the type to MovieClip and the registration to the top-left corner, and export it for ActionScript asMenuSlidesMC.Before we finish we must give each of the MovieClips inside
menuSlidesMCan instance name. Select each slide in the order they appear from the left and name themslide1,slide2,slide3andslide4respectively. Name the circle buttonsone,two,threeandfour, and finally name the side buttonsleftandright.Step 4: Setting Up the Classes
Now that all of our MovieClips have been created we can start setting up the two classes we are going to use.
First go to your Flash file’s Properties and set its class to
menuSlides; then, create a new ActionScript 3.0 file and save it asmenuSlides.as.Now copy the following code into it; I will explain it after:
package{ import flash.display.MovieClip; import flash.events.Event; import flash.events.MouseEvent; public class menuSlides extends MovieClip{ public var menuSlidesMC:MenuSlidesMC = new MenuSlidesMC(); public function menuSlides(){ addChild(menuSlidesMC); } } }Pretty basic – it’s a document class, into which we imported the MovieClips and Events we will use. Then we created an instance of
MenuSlidesMC, and added it to the stage.Now create a new ActionScript 3.0 file for the
menuSlidesMCinstance. Save it asMenuSlidesMC.asand copy the following code into it:package{ import flash.display.MovieClip; import flash.events.Event; import flash.events.MouseEvent; public class MenuSlidesMC extends MovieClip{ public var speed:Number = new Number(); public var activeSlide:Number = new Number(); public function MenuSlidesMC(){ speed = 10; activeSlide = 1; addEventListener(MouseEvent.CLICK, slidesClick); addEventListener(Event.ENTER_FRAME, slidesMove); } } }Just like last time, we imported what we are going to need, but we created two number variables. The first variable,
speed, is actually how many pixels the slides are moved by each frame. (Note: this number has to evenly divide into your stage’s width to give a smooth transition). The second variable,activeSlide, tells us which slide is currently set to be on screen.We also added two event listeners for functions we are going to create; one of them is called on a mouse click, and the other is called at the beginning of every frame.
Step 5: Creating the Event Handler Functions
To begin we will get the mouse click function out of the way. Start by creating a public function named
slidesClick():public function slidesClick(event:MouseEvent):void { }Next we will create some if-statements regarding the
event.target.name. Basically, this property stores the name of the object that was targeted by the mouse click. We can use this to check which button is pressed:if(event.target.name == "left"){ if(activeSlide>1){ activeSlide-=1; } }else if(event.target.name == "right"){ if(activeSlide<4){ activeSlide+=1; } } if(event.target.name == "one"){ activeSlide=1; }else if(event.target.name == "two"){ activeSlide=2; }if(event.target.name == "three"){ activeSlide=3; }else if(event.target.name == "four"){ activeSlide=4; }The code above goes in the
slidesClick()function. The first set of if-statements are for the left and right side buttons; they increase or decrease the value ofactiveSlide, but never allow the value to become less than 1 or greater than 4 (since we only have four slides). The second set of if-statements are for the circle buttons; instead of just incrementing or decrementing the value ofactiveSlidethey set it to the selected value.Now let’s begin with the
ENTER_FRAMEhandler function:public function slidesMove(event:Event):void { }Add the
slidesMove()function below yourslidesClick()function and we’ll start adding some code to it. First, we’ll use aswitchto check which slide should be on the screen, based on the value ofactiveSlide:switch (activeSlide){ case 1: break; case 2: break; case 3: break; case 4: break; }Now in each case we will create an if/else block that will check that slide’s current x-position, and move all of the slides either left, right, or not at all, depending on where the desired slide currently sits.
The first case looks like this:
if(slide1.x<0){ slide1.x+=speed; slide2.x+=speed; slide3.x+=speed; slide4.x+=speed; }else if(slide1.x>0){ slide1.x-=speed; slide2.x-=speed; slide3.x-=speed; slide4.x-=speed; }Now all we have to do is repeat the same process for the other cases! After you are done your swtich should look like this:
switch (activeSlide){ case 1: if(slide1.x<0){ slide1.x+=speed; slide2.x+=speed; slide3.x+=speed; slide4.x+=speed; }else if(slide1.x>0){ slide1.x-=speed; slide2.x-=speed; slide3.x-=speed; slide4.x-=speed; } break; case 2: if(slide2.x<0){ slide1.x+=speed; slide2.x+=speed; slide3.x+=speed; slide4.x+=speed; }else if(slide2.x>0){ slide1.x-=speed; slide2.x-=speed; slide3.x-=speed; slide4.x-=speed; } break; case 3: if(slide3.x<0){ slide1.x+=speed; slide2.x+=speed; slide3.x+=speed; slide4.x+=speed; }else if(slide3.x>0){ slide1.x-=speed; slide2.x-=speed; slide3.x-=speed; slide4.x-=speed; } break; case 4: if(slide4.x<0){ slide1.x+=speed; slide2.x+=speed; slide3.x+=speed; slide4.x+=speed; }else if(slide4.x>0){ slide1.x-=speed; slide2.x-=speed; slide3.x-=speed; slide4.x-=speed; } break; }And that’s it! We are all finished with the code and the menu should be working great right now.
Conclusion
Thank you for taking the time to read through the tutorial, I hope it was helpful and that you learned a little something about dynamic menus.
Twice a month, we revisit some of our readers’ favorite posts from throughout the history of Activetuts+. This tutorial was first published in April, 2009.
In this tutorial we’ll create a magnifying glass effect, demonstrating use of the displacementMapFilter. The effect can be achieved in a relatively short space of time and with very little code.
Final Result Preview
Let’s take a look at what we’re aiming for:
Step 1: Brief Overview
We’re going to work with two layers, plus an additional optional layer. The first will hold an image which will contain the visual graphics, this can be anything. The second layer will be the color map which will control the pixel pushing. The third layer will hold the ActionScript.
An optional fourth layer will be an overlaying graphic acting as the frame or lens surround.
Lets look into it!
Step 2: Document Setup
First thing we need to do is make a new ActionScript 3.0 Flash file – make the document size 530px X 400px with a framerate of 30fps.
Step 3: Import Resources
Next we need to import an image that we can use for this effect – I found a cool, freely available desktop image at 1024px X 768px.
Import this to stage and name the layer "Image".
Now let’s scale the image down to 50% and center it.
Step 4: Scripting the Filter
Create a new layer on top and call it "Actions". Then let’s bring the actions panel out and start coding the effect. First we need the filter for the image so let’s create a new filter object and call it "dFilter". We’ll leave the filter free of parameters as there are quite a few to set.
Next we need to set these filter parameters in listed view.
Step 5: Effect Scale
Let’s start with the easiest ones and set the scales to around 50. This is the amount to which the magnifier will zoom in. It can also be set to a negative value, but in this case we need it to zoom in, not out.
Additional lines: 3,4
Note: this is not the actual order in which the filter normally accepts the parameters. However, in this case we can add them as we wish because we’re using the listed view for a better overview.
Step 6: Color Channel Components
Next we’ll set the component color channels for X and Y – this dictates which colorchannels in the control map (which we’ll create in a second) the filter will listen to.
If you’re familiar with the RGB hex code #RRGGBB, we can choose from BitmapDataChannel.RED, BitmapDataChannel.GREEN and BitmapDataChannel.BLUE. To make it easier we can also just write 1 (red), 2 (green) or 4 (blue) – (and no I didn’t make a spelling mistake, the blue is 4; this is set from the actual channel position in the hex code). In this example we’ll just stick to red (1) and green (2)- but we’ll come back to more about this when we design the actual displacement map.
Additional lines: 5,6
Step 7: Displacement Mode
Next we need to set the mode to determine how the pixels will react if they are pushed further than the image boundaries. Here we can choose from:
DisplacementMapFilterMode.COLOR / DisplacementMapFilterMode.WRAP / DisplacementMapFilterMode.CLAMP / DisplacementMapFilterMode.IGNORE
Again we can simplify this by writing "color", "clamp", "wrap", "ignore". I won’t get any further into these in this tutorial, so lets just use “color” which works best in most cases.
Additional lines: 7
This mode allows pixels to continue beyond the image boundary (in case the filter pushes the pixels further than the edge of the image).
Step 8: Surrounding Color and Alpha
Now let’s set the surrounding color to 0×000000 and alpha to 0. This is 100% transparent, so nothing is displayed outside the image except the source pixels.
Additional lines: 8,9
Step 9: Filter Effect Position
Now we need to set the position where the filter will affect the image; our lens position. This has to be set as a Point object containing the x and y value. We’ll begin by creating the point object so it’s ready for use when we assign it to the displacementMapFilter. Let’s call it "dPoint" and set it to 0, 0 as initial values. We’ll come back to this in a moment when we need to instruct this point to follow the mouse.
Next we assign "dPoint" to the "dFilter’s" point position.
Additional lines: 1,11
Step 10: BitmapData
Last but not least we need to assign the control map to the filter. This is the map which contains the colored pixels that the componentX and Y listen to.
Additional lines: 13
Here we also need a BitmapData object to act as a data container for our color-map.
Step 11: Designing the Color Map
First we create a 100px X 100px, red to black, linear gradient square. This will take the current pixels and push them left and right as we set the componentX to red. Let’s make this a movieclip called "redMap"
Then we do the same again – but this time with a green to black linear gradient square, again 100px X 100px. This time we’ll also rotate it 90°. You might remember we set the component for the Y axis displacement as green (componentY = 2) so the gradient goes along the y axis. Once again we’ll convert it to a movieclip, this time calling it "greenMap"
Step 12: Preparing the Map for Capture Phase
We now have two separate color images; we need just one, so set the blendmode of the greenMap to "screen". Every color from the greenMap will then shine through on the redMap. Place the greenMap on top of the redMap and make sure they align correctly.
Select both movieclips by clicking on the layer "Map" and convert the two into one movieclip called "colorMap". Then set the instance name to "colorMap_mc".
Step 13: Map Container
Now let’s return to the code and continue by capturing the colorMap movieclip in a bitmapData.
Go to the top of the code and create a new BitmapData object. Let’s call it "dMap" and set the size of it to match the size of our colorMap (in this case 100px X 100px, but this can be almost anything). We’ll set transparent to "true" and color to 0×808080. This ensures that any remaining pixels in the bitmapData are neutral.
Additional lines: 3
Step 14: Capture Phase
We need to draw the colorMap’s content into the bitmapData. Once that’s done, we’ll be able to use script to delete the colorMap from the stage. This is possible as the colorMap image will be contained within the bitmapData code.
Additional lines: 5, 7
Step 15: Assigning the Map to the Filter
Add the bitmapData dMap to the displacementMapFilter by setting the last parameter in the list (mapBitmap) to "dMap".
Modified lines: 19
Step 16: Add Filter to Image
The filter is complete! We now need to add it to the image, so select the image and make sure it has an instance name – lets call it "Image_mc". That done, we’re able to set the filter on the image. We do this at the end of the code by setting the Image filters parameter as an array like this:
Image_mc.filters = [dFilter]
Additional lines: 21
OK, let’s export the movie and see how the filter is affecting the image. It should look something like this:
Step 17: Interactivity
What we have so far isn’t very exciting, so let’s try to make the lens follow the mouse.
First we add the "enterFrame" loop code like this:
Additional lines: 23,25,27
var dPoint:Point = new Point(0, 0); var dMap:BitmapData = new BitmapData(colorMap_mc.width, colorMap_mc.height, true, 0x808080) dMap.draw(colorMap_mc) removeChild(colorMap_mc) var dFilter:DisplacementMapFilter = new DisplacementMapFilter (); dFilter.scaleX = 50 dFilter.scaleY = 50 dFilter.componentX = 1 dFilter.componentY = 2 dFilter.mode = "color" dFilter.color = 0x000000 dFilter.alpha = 0 dFilter.mapPoint = dPoint dFilter.mapBitmap = dMap Image_mc.filters = [dFilter] Image_mc.addEventListener(Event.ENTER_FRAME, onFrame) function onFrame(e:Event){ }Step 18: Follow the Mouse
Next we set the values of our dPoint’s X and Y to follow the mouse. Additionally, we’ll reassign the newly changed dPoint to the dFilter again and reassign the filter to the image.
Additional lines: 26,27,28,29
var dPoint:Point = new Point(0, 0); var dMap:BitmapData = new BitmapData(colorMap_mc.width, colorMap_mc.height, true, 0x808080) dMap.draw(colorMap_mc) removeChild(colorMap_mc) var dFilter:DisplacementMapFilter = new DisplacementMapFilter (); dFilter.scaleX = 50 dFilter.scaleY = 50 dFilter.componentX = 1 dFilter.componentY = 2 dFilter.mode = "color" dFilter.color = 0x000000 dFilter.alpha = 0 dFilter.mapPoint = dPoint dFilter.mapBitmap = dMap Image_mc.filters = [dFilter] Image_mc.addEventListener(Event.ENTER_FRAME, onFrame) function onFrame(e:Event){ dPoint.x = mouseX dPoint.y = mouseY dFilter.mapPoint = dPoint Image_mc.filters = [dFilter] }Lets test it again. It should look like this:
Step 19: Finalizing
It’s still not exactly how we want it, so lets make the center of the displacement follow the mouse and also add a small easing to the movement. To do that we change the following code:
dPoint.x = mouseXdPoint.y = mouseY
Modified lines: 26,27
var dPoint:Point = new Point(0, 0); var dMap:BitmapData = new BitmapData(colorMap_mc.width, colorMap_mc.height, true, 0x808080) dMap.draw(colorMap_mc) removeChild(colorMap_mc) var dFilter:DisplacementMapFilter = new DisplacementMapFilter (); dFilter.scaleX = 50 dFilter.scaleY = 50 dFilter.componentX = 1 dFilter.componentY = 2 dFilter.mode = "color" dFilter.color = 0x000000 dFilter.alpha = 0 dFilter.mapPoint = dPoint dFilter.mapBitmap = dMap Image_mc.filters = [dFilter] Image_mc.addEventListener(Event.ENTER_FRAME, onFrame) function onFrame(e:Event){ dPoint.x += ((mouseX-colorMap_mc.width/2)-dPoint.x)*0.3 dPoint.y += ((mouseY-colorMap_mc.height/2)-dPoint.y)*0.3 dFilter.mapPoint = dPoint Image_mc.filters = [dFilter] }To sum up: we subtract half the size of the map from the map position, so it centers. Then we add a basic tweening function, which can be written like this:
this += (that-this)*speedStep 20: Adding Custom Graphics
To top it off, I added a magnifying glass graphic that I prepared in photoshop. I converted it into a movieclip, gave it an instance name and made it follow the point that we use for the displacemenMapFilter.
This is achieved by setting the new lens_frame_image X and Y position equal to the dPoint position. Then subtract the offset for the graphics edge, so that it aligns perfectly with the filter effect.
Additional lines: 4,5
function onFrame(e:Event){ dPoint.x += ((mouseX-colorMap_mc.width/2)-dPoint.x)*0.3 dPoint.y += ((mouseY-colorMap_mc.height/2)-dPoint.y)*0.3 lens_mc.x = dPoint.x-8 lens_mc.y = dPoint.y-8 dFilter.mapPoint = dPoint Image_mc.filters = [dFilter] }Now our result should look like this:
Conclusion
When you have learned to create this effect yourself it shouldn’t take more than 15 minutes to set up. Remember; if you forget what the parameters for the displacementMapFilter are you can always look them up on "help". There you will get the listed order and what each parameter does.
For quick experimentation you can go to my website and look in the "flash" section under "test / labs" – I have a bunch of displacementMapFilter test environments you can try out.
I hope you can find use for this filter in your creative work!
I’m excited to reveal to you the most powerful tools in the entire GreenSock Tweening Platform. Everything I have discussed in the last four videos has been custom tailored to prepare you for the awesome power you are about to behold. I’m going to show you two methods that will change the way you approach any script-based animation from this point forward.
With
appendMultiple()andinsertMultiple()you will have precise control over the sequencing and timing of hundreds of tweens and timelines with very little code.TimelineLite in Action
In the SWF below, the parent timeline that is being controlled by the scrubber only has two lines of code, yet there are literally over a hundred tweens taking place.
You can find all the files used to create the SWF above in the source files for this tutorial.
Watch the Screencast
Don’t like ads? Download the screencast, or subscribe to Activetuts+ screencasts via iTunes!
Meet appendMultiple()
TimelineLite’s
appendMultiple()method allows you to add many tweens and/or timelines to a timeline and have precise control over the relative timing of all the tweens/timelines. What makes this method so powerful is that it accepts an array of tweens/timelines and treats them all as single group that can be offset and staggered however you like.In order to get most out of
appendMultiple()you must understand all four parameters.appendMultiple(tweens:Array, offset:Number = 0, align:String = "normal", stagger:Number = 0):Arraytweens:Array — an Array containing any or all of the following: TweenLite, TweenMax, TimelineLite, and/or TimelineMax instances
offset:Number (default = 0) — Amount of seconds (or frames for frames-based timelines) to offset the insertion point of the tweens from the end of the timeline. For example, to start appending the tweens 3 seconds after the end of the timeline (leaving a 3-second gap), set the offset to 3. Or to have the tweens appended so that the insertion point overlaps with the last 2 seconds of the timeline, set the offset to -2. The default is 0 so that the insertion point is exactly at the end of the timeline.
align:String (default = “normal”) — determines how the tweens will be aligned in relation to each other before getting appended. Options are: TweenAlign.SEQUENCE (aligns the tweens one-after-the-other in a sequence), TweenAlign.START (aligns the start times of all of the tweens, ignoring delays), and TweenAlign.NORMAL (aligns the start times of all the tweens, honoring delays). The default is NORMAL.
stagger:Number (default = 0) — staggers the tweens by a set amount of time (in seconds) (or in frames for frames-based timelines). For example, if the stagger value is 0.5 and the “align” property is set to TweenAlign.START, the second tween will start 0.5 seconds after the first one starts, then 0.5 seconds later the third one will start, etc. If the align property is TweenAlign.SEQUENCE, there would be 0.5 seconds added between each tween. Default is 0.
The above descriptions were taken directly from the GreenSock Documentation.
In the video I go over a number of ways to create the array of tweens that
appendMultiple()will accept. Below are the two most popular approaches.Create an Array of Unique Tweens
tl = new TimelineLite(); //bg tween tl.insert( TweenMax.to( bg, .5, {alpha:1}); tl.appendMultiple( [TweenLite.from( meet_mc, .5, {x:-100, rotation:-90, scaleX:0, scaleY:0, ease:Back.easeOut} ), TweenLite.from( the_mc, .5, {y:-100, rotation:-90, scaleX:0, scaleY:0, ease:Back.easeOut} ), TweenLite.from( blobs_mc, .5, {x:700, rotation:90, scaleX:0, scaleY:0, ease:Back.easeOut} ) ], 0, "sequence", .5);In the code above three tweens added via
appendMultiple()will be added immediately after the bg tween ends because the offset value is 0. Each tween will start .5 seconds after the previous tween ends because the align parameter is set to “sequence” and the stagger is set to .5.Create an Array of Similar Tweens Using allFrom()
TweenMax.allFrom()creates an array of tweens based on an array of objects. It applies the same values to tweens of all the objects in the array. You can also useTweenMax.allTo()if you want to specify where objects should tween to as opposed to where they should tween from.//create an array of objects to be tweened allBlobs = [blob1_mc, blob2_mc, blob3_mc, blob4_mc, blob5_mc, blob6_mc, blob7_mc] tl = new TimelineLite(); //use allFrom() to tween every blob FROM the same set of properties with the same duration. tl.appendMultiple( TweenMax.allFrom( allBlobs, .3, {scaleX:0, scaleY:0, x:"100"} ), 0, "normal", .1 )The code above will tween each blob for .3 seconds from the values supplied in the vars object (
{scaleX:0, scaleY:0, x:"100"}). Each tween will start .1 second after the previous tween has started because the align mode is set to “normal” and the stagger is .1.It’s important to note that
allFrom()returns an array of tweens and that is why we can use it as the value for thetweensparameter in theappendMultiple(). If you were to useallFrom()in anappend()you would get an error as it requires a single tween rather than an array of tweens.TweenAlign Visualizer
Align modes are a bit difficult to understand without seeing them in action. I built the TweenAlign visualizer to give you an easy way to test how they work with a combination of various tween delays, staggers and offsets.
Use the swf above to perform the following tests:
Test 1: Understand the difference between
TweenAlign.NORMALandTweenAlign.STARTTweenAlign.NORMALandTweenAlign.STARTNotice how the delay is ignored with
TweenAlign.STARTTest 2: See how the stagger works differently depending on align mode
Notice how stagger works differently with TweenAlign.NORMAL and TweenAlign.START:
TweenAlign.NORMAL, stagger is the amount of time between the start of each tween.TweenAlign.SEQUENCE, stagger is the amount of time between the end of a tween and the next tween.Go crazy combining different settings and keep an eye on how they effect the start time of each tween and the overall duration of the timeline. The TweenAlign Visualizer is also available in the source files for this tutorial.
Additional Notes
Although I reference
appendMultiple()quite a bit in this text,insertMultiple()works virtually the same way. The only difference withinsertMultiple()is that the offset parameter refers to an exact time or label at which the tweens/timelines should be added. Read more in the TimelineLite Documentation.When specifying the align mode you can reference the static contsant value in the TweenAlign class or the string value.
TweenAlign.NORMAL is the same as using “normal”.
Conclusion
Once you get a handle on how
appendMultiple()andinsertMultiple()work you basically have limitless potential for creating elaborate and flexible animations with very little code. I truly hope the demonstration of nesting multiple TimelineLites inspires you to create some truly wonderful effects. By adding just a touch of randomness to each timeline you can really start to take your animations to the next level and maintain an insane amount of control.Perhaps now you have the knowledge and fortitude to attempt the “starburst” effect that was featured in the Introduction to TimelineLite video? Come to think of it, I’m assigning that challenge to you as homework.
In the next tutorial I am going to be showing some features exclusive to TimelineMax and some other tricks you can do with all your timelines.
If you have any questions or comments on this tutorial simply post a comment below.
Thanks for watching!
Are you an RIA developer? Do you make Flash games or Unity games? Are you always on top of the latest industry news? You sound like the perfect person to contribute to Activetuts+!
We’re on the lookout for new authors, so read on to find out more…
What We’re Looking For
Right now, we’re putting out a general call for bloggers.
Okay, technically everything on Activetuts+ could be considered a blog post. What we mean, specifically, is the more chatty, conversational posts; articles that aren’t focused on building a specific type of app, or implementing a certain effect.
The best way to illustrate this is through a few examples from other Tuts+ sites. Some articles clear up misconceptions, offer advice, or discuss a pertinent issue:
Other posts help readers discover useful and inspirational content that’s relevant to their niche:
Payment will depend on the length, depth, and subject of the article, but you can expect to earn from $50 to $150 per post.
Pitch a Blog Post
Got an idea? Great! Pitch it to us (in about a paragraph or so) using the following form:
Pitch your Quick Tip idea
As long as you’ve got an original, useful idea and can string a decent English sentence together, there’s a high chance your pitch will be accepted, so don’t be shy.
No specific ideas? That’s okay, we’re still interested – just submit some relevant examples of previous writing you’ve done, whether it’s on your own blog, on forums, or even on Google+.
Of course, this is also open to existing Activetuts+ writers, past and present – you know how to get in touch.
We’re looking forward to reading your pitches!
In AS3 projects, you often need to load external assets like images and other SWFs. While loading these assets you have to keep track of several events for successful execution of your application. You might have been using some of these events like
ProgressEvent,COMPLETEevent. This tutorial shows you how to get info about the assets you’re loading withLoader.load(), using its associated events, all in one place. So if you’re having difficulty loading such assets, or you have less time in hand to debug, take a look at this.(Preview image courtesy of VisualPharm.)
Final Result Preview
Let’s take a look at the final result we will be working towards:
This SWF loads an image, showing details about the loading process (progress, whether it’s complete, and more).
It won’t work embedded in this page (which shows you the responses in case of error) but it will work if you load the SWF directly.
Step 1:The need to load assets
Hey, if you are in a hurry, you can skip this step. But, if you are relaxed, with a cup of coffee in hand, have a read.
In the early days when I started learning Flash 5 and AS1, I used to have all objects on the stage at once, and made them visible/invisible as required. Eventually as I started developing apps, I became more familiar with Flash’s system. Wow, now I was able to use “linkage identifier” to load objects from library. Getting more advanced, I soon realised the need for external assets. But unfortunately, there was no such powerful architecture. Meanwhile, Flash was going through drastic changes. Now “AS2″ was capable of loading external assets. Finally, when it lined up in Adobe’s family, the new event-based architecture “Action Script 3.0″ was introduced. In AS3, the need for loading external assets is much better addressed than in AS2.
So, when working with Flash we have three ways to manage our assets:
The first method is not versatile. As the number of objects increases, the size of the SWF also increases (especially when there are lots of images). Imagine how much time it can take to load on user’s machine? Also, the performance of your application might degrade. But, still it can be handy enough for creating lightweight apps. (E.g. developing a simple game, in which player needs to find out key objects from the given image).
The second method is very much similar to the first one. Instead of having objects on the stage, now they are residing only in the library. Again, if there are lots of objects, then the issues with the SWF’s size and performance remain. But with this approach, you can easily create multiple instances of objects. Plus, the objects are much easier to handle than the first method. (E.g. games like “Tic Tac Toe”, “Tetris”, and “Snake” can be developed as they require fewer objects).
The third method (loading assets externally) is much more flexible than the first two. The power of this method is, without having a single object in the library you can still develop big games (e.g. “RPG” style games). As objects are loaded externally, they are loosely tied to your main SWF, so you do not have to go back to Flash every time you want to replace or edit your assets. One more advantage of this method is, it allows you to load and display objects on an “On Demand” basis. Meaning, you can load your objects only when they are required and unload them when you are completely done. (E.g. if your game has 10 levels, then you do not need to load all assets needed for all 10 levels. Instead, load and unload them level by level.) Thus, the overall performance of the application is improved.
Note: The above “On Demand” method may not be suitable in case where assets are loaded and unloaded frequently (e.g. in an image viewer app).
Okay, so now we can conclude that, of the three methods, the third one is more flexible. But, as the system gets more advanced, it becomes more complicated to manage. Similarly, if you decide to go with third method, you will need to handle assets as one separate operation. You may call it “Asset Management” which can be divided into two parts:
This tutorial covers the second part, so let’s look at that now.
As our program loads assets externally, it is now dependent on those assets. If any of our assets fail to load for some reason, then our entire application will be hampered. To avoid such failures, we really have to provide “Decision Making” power to our program.
Here’s one example. You have a coin-flipping game, where the coin is an externally loaded image. Now, if this image fails to load, then what next? This is the perfect point where your program will take some decision(s). It might load another image for the coin, stored as a proxy. It might load the stand-by coin if you have placed it in the Flash library. This can be a lightweight coin made up of vector graphics and strokes converted to fill to keep the size of the main SWF as small as possible. If you do not plan to have stand-by coin, then your program could warn the user about failure.
Of course, this was just one case. For a complete game, you will need to use a mature “Asset Management” system.
Step 2: Which Type of Loader?
In an AS3 project, assets can be any of the following,
These assets have their own methods of loading as they are different in nature. You cannot see the sound, and you cannot hear images, so it is quite acceptable to have different techniques for these different types.
Hence, you cannot load sounds the way you load images and vice versa. You have to pick the right method for the selected assets.
The following are the appropriate ways of loading these assets :
load()” method of “Loader” class is used to load images and SWFs. Our tutorial is dedicated to this section.load()” method of “Sound” class is used to load external MP3 sound files.FLV video format or a video format that uses standard H.264 encoding is supported by AS3.
Video without FLV component can be loaded and streamed using classes like “
NetConnection” and “NetStream“.Video with FLV component can be loaded and streamed using “
source” method asmyFLVComponent.source("MyVideo.flv").To load such type of data, you have to use the
URLLoaderclass, which loads data from the specified URL. Any type of data can be loaded but in the form of text or raw binary data or encoded variables.e.g. You can also load an image in the form of raw binary data and further use it for processing with “
ByteArray” class.So, let us end the theoretical part here and move towards the practical stuff. Now we have enough knowledge about assets. In the rest of the tutorial, we will be dealing with the
load()method (of “Loader” class) and its associated events, to monitor the loading process of an image and a SWF.Step 3: Events for Monitoring the Loading Process
Before moving towards Loader class, first let us look at several events that are needed to monitor the loading operation:
Our first task will be to check the availability of the specified target (i.e the “loadee”).
Sometimes, the target is not found due to incorrect file names or perhaps because the file itself no longer exists at the location. Even, in some cases, assets may not be readable. If everything is okay at this level, then we can jump to monitoring the loading process.
As soon as the loading is triggered, this event is dispatched. Here, we can initialize a preloader (e.g. set up the Progress indicator), before the PROGRESS event is dispatched. This OPEN event is the first thing to be dispatched in a (successful) loading operation.
This event depends on the loading environment. Meaning, if you are loading images remotely (say, over the Internet, rather than from the user’s hard drive) and if the connection is okay but the file is not found, even now this event will be dispatched. So, be careful if you are initializing a preloader here – or try to make your program very strict in handling an
IO_Errorevent.We have got the target and now the loading is in progress. But we can’t just stay quiet and wait for the progress event to finish, because it takes some time to load the image (or SWF), especially when the image is heavy or being transferred online. So, to keep the user engaged, we must keep informing him about the state of the load. Now, what to inform?
While is in progress, we can display the message saying “Please wait. File is loading”. We can also display the “current bytes loaded” on the screen using information from the
PROGRESSevent.We can do such tasks while this event is processing. Hence, it is at second position.
This event is best suited for downloading SWF files from the Internet.
If you are loading SWF from local machine itself and the SWF is lightweight, then it is almost identically timed to the COMPLETE event. Many a time you will find no difference between INIT and COMPLETE events, because within no time, you are in a state of COMPLETE, immediately leaving INIT state.
But the situation changes when you are loading the SWF(s) from internet. In addition, if the SWF (having animations or lots of objects) is the target, then it will take more time to download. In this case the INIT becomes handy.
As soon as the SWF header is readable, this event is dispatched and the document class for the main SWF is instantiated. The first frame is available and you can do some preloading actions or you can define some properties while the rest of the part is still downloading.
This event is the third to fire in a successful load.
When loading assets over the Internet (using an HTTP request), the HTTP status code (e.g. 200) is returned if the connection is OK. Otherwise, 0 is returned.
If the connection is okay, but the file is not found, then 404 is returned.
For more information on HTTP Status Messages, follow the link below:
HTTP Status Messages
Although this event checks the status of the connection to the remote server, it is trigerred even in case of loading assets locally – but in this case it always returns 0. This event fires fourth.
As the name suggest, this event will be dispatched when an image (or SWF) is completely loaded on the user’s machine. At this point, the image or SWF is completely available for further process. Hence, this event is at last place in the loading operation.
There are also some other events like so:
This error occurs when a SWF, running asynchronous code (like AS2), is loaded.
This error occurs when Flash Player’s security model is violated during a loading process.
For the latest info on the security model of Flash Player, take a look at Flash Player Security.
For more info on
SecurityErrorEvent.SECURITY_ERRORspecifically, here are some links to follow:Although we can skip some of these events – like “OPEN”, “HTTPStatus”, “INIT” – when loading assets locally, tracking all of them ensures a high level of monitoring a loading operation.
Step 4: Preparing the Document Class
After knowing all necessary event types, now its time for loading assets.
First, we shall create a basic structure of our document class.
For detailed explanation about the document class check out this Quick Tip.
Download all source files required for this assignment by clicking the “Download Source Files” button available at the top of the page.
The folder contains “LoaderClassExample.fla”, “MyImage.png”.
Also, there is “MyLoader_Complete.as” file, from the end of the tutorial. We are going to build this class, step by step, for better understanding. At the end of this tutorial, we will have our own version of this class.
Open Flash and create new “ActionScript 3.0 Class” file and name it “MyLoader”.
You will have the basic structure of the class like so:
package { public class MyLoader { public function MyLoader() { // constructor code } } }Save this class file as “MyLoader.as” in the same downloaded folder.
Then open the downloaded “LoaderClassExample.fla” file. Don’t worry about the objects on the stage. We shall see their roles in coming steps.
So, first we must change the document class from “MyLoader_Complete” to “MyLoader”, in the Property Inspector of the FLA.
Now, the Class name must read like so:
Step 5: Loading an Image
To start with, we will load the image “MyImage.png” placed in the downloaded folder.
The basic requirements can be listed down, like so:
So, to fulfill above requirements, modify the “MyLoader.as” like so:
package { import flash.display.Sprite; import flash.display.Loader; import flash.events.MouseEvent; import flash.net.URLRequest; public class MyLoader extends Sprite { private var myURL:String; private var loader:Loader; public function MyLoader() { myURL = "MyImage.png"; loader = new Loader(); addChildAt(loader,0); loadButton.addEventListener(MouseEvent.CLICK, loadAsset, false, 0, true); } private function loadAsset(evt:MouseEvent):void { loader.load(new URLRequest(myURL)); removeChild(loadButton); loadButton.removeEventListener(MouseEvent.CLICK, loadAsset); } } }Test the movie and click the “Start Loading” button. The image is loaded and displayed.
Since, the target was an image, we needed
LoaderClass to create Loader Object.Hence, in our code we put the lines like so:
Above line made the
Loaderclass available to our program.Above line declared the
loadervar to storeLoaderobject.Above line created the
Loaderobject and assigned it to aloadervar.Now, we can access
load()method ofLoaderclass.In our code, we accessed
load()method like so:The
load()method is most crucial part of our code.We passed only one parameter as
URLRequestto this method. Actually it can take two parameters. The second parameter isLoaderContextobject. TheURLRequestis required whileLoaderContextis optional parameter.For example,
loader.load( URLRequest, LoaderContext);Let’s look at brief info about
URLRequestandLoaderContextin next couple of steps.Step 6: URLRequest and Paths
In our code, we created the instance of
URLRequestobject like so:We used a shorthand method for creating an instance of
URLRequest, which is okay for our example but not a good practice in general. It should not be used for big projects (e.g. a framework). The problem is that it leaves no reference to the URLRequest that we can use again later.Instead, use this approach:
var urlRequest:URLRequest = new URLRequest();urlRequest.url = myURL;loader.load(urlRequest);Now you can refer to
urlRequestany time you want.Now, let us learn about
URLRequest, absolute paths, and relative paths.URLRequest: This object gives us the ability to address the target by specifying a path.
This path can be one of two types:
Here are some examples of Absolute Path:
http://SomeDomain/assets/MyImage.pngD://MyProject/Assets/MyImage.pngAbsolute Path is used mostly to download assets from other domains over the Internet. Being hard-coded, these paths do not give a flexible approach. Hence, for portable projects (especially for desktop apps) do not plan to use such paths.
It uses standard URI schemes like so:
Check this link for more info on Http and Https.
Relative Path can be divided into two parts like so:
MyImage.png(we used this path in the above code as myURL.)Assets/MyImage.png../Assets/MyImage.png../../Assets/MyImage.pngUse case of above two paths depends on your project. But, in most cases you will end up using “Document Relative” path instead of “Root Relative” path.
Document Relative path is illustrated like so:
(The SWF is in a folder parallel to the Assets folder.)
Root Relative path is illustrated like so:
Step 7: Loader Context
We did not used this parameter in our example, but having information about it will add the value.
Loader Context: This optional parameter gives access to the properties like so:
Let us see some brief info about these properties.
loaderContext.securityDomain = SecurityDomain.currentDomain;This forces the loadee SWF (from other domain) to be placed into the loader SWF’s domain. By default the loadee SWF is placed in its own security domain (due to Flash Player’s security model).
Also, a policy file (e.g. crossdomain.xml) must be available on the loadee SWF’s server in order to established the trust between different servers. Thus, ensuring the interoperability between SWFs.
When loading images, this property is not required.
Once, everything is in your own security domain, then you can specify
loaderContext.applicationDomain.e.g.
loaderContext.applicationDomain = ApplicationDomain.currentDomain;It allows cross scripting between SWFs. e.g. The loadee SWF can access the class associated with parent SWF, when set as
ApplicationDomain.currentDomain.This property is not needed while loading images.
Check this example which allows the access to class definition of loadee SWF.
ApplicationDomain.currentDomain.getDefinition("com.SomeFolder.SomeAS3Class");ApplicationDomain.currentDomain.getDefinition("SomeLibrarySymbol") as Class;(Note: Be updated with Adobe’s policy about accessing external scripts. It is related to security model. Adobe is constantly improving Security model. So keep track of Adobe’s documentation.)
Read the following links for more info on
Application Domain:Application Domain
Loader Context Application Domain
This property can be used for both (SWF and an image).
e.g.
loaderContext.checkPolicyFile = true;When this property is set to true, then it always checks for policy file (crossdomain.xml) on server side. If it is not found, a security sandbox error is thrown.
Set this property to true only when you want to do more actions than just displaying that content on the screen. Otherwise keep this property as a false.
e.g. When you are loading an image from the secure server and if you want to use
BitmapData.drawmethod on that loaded content, then set this property to true.Following are some more links related to Flash Player Security:
Security sandboxes
Permission controls
In this step we completed a very simple task of loading an image. But while loading we did not track the information about loading. We just watched and waited to have the image on the screen. In the next step we will see what is needed to monitor the loading progress of an image.
Step 8: loaderInfo and contentLoaderInfo
Friends, in the above step we loaded an image without tracking the loading information. This time we will start monitoring the loading process of this image. For that we will make use of various events we discussed in Step 3.
But we still don’t know how to associate these events with Loader class. So, let us explore it.
The
Loaderclass comes with a very important property:contentLoaderInfo.This property is needed to associate the loading events with the
load()method ofLoaderclass.The following is one example, which tracks the
COMPLETEevent of loading process.loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);When the target image is completely loaded, the “loadComplete” method is called. This method can have your after-loading logic, e.g. adding that image to the stage.
We are definitely going to use this property very soon. But, before that, let us see what exactly it does with
Loaderclass.The
Loaderclass does not have any information about loading progress. In fact, this information is retrieved from thecontentLoaderInfowhich further utilizes theLoaderInfoobject.What is loaderInfo? What is contentLoaderInfo?
To understand these terms, we shall experiment with our “MyLoader.as” Class.
This time we shall actually use
COMPLETEevent. So, let us add our new functionloadCompleteand some more lines to the code like so:The above line makes the
Eventclass available to our program. Thus we can useCOMPLETEevent.private function loadComplete(evt:Event):void { trace("Load Complete"); trace(evt.target); }This
loadCompletefunction will be called when the target image is completely loaded. It will trace “Load Complete” to make sure that this function is invoked. It will also trace the type of the target (Loader).Now, to associate this
COMPLETEevent with the Loader, we shall modify the constructor method in our code like so:public function MyLoader() { myURL = "MyImage.png"; loader = new Loader(); addChildAt(loader,0); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete); loadButton.addEventListener(MouseEvent.CLICK, loadAsset, false, 0, true); }Test the movie and press the “Start Loading” button.
The output window will show the results, like so:
Load Complete[object LoaderInfo]It returns
LoaderInfoobject as a target. So, we can say that,contentLoaderInfoutilizes theLoaderInfoobject.What is loaderInfo?
So, let us experiment more.
Modify the
loadCompletemethod like so:private function loadComplete(evt:Event):void { trace("contentLoaderInfo:" + loader.contentLoaderInfo.height); trace("loaderInfo:" + loader.loaderInfo.height); }Test the movie and press “Start Loading” button.
The output window will show the result like so:
contentLoaderInfo:600loaderInfo:640Here,
contentLoaderInforeturned the height (600) of the target image.And,
loaderInforeturned the height (640) of the main (root) SWF.If you want to experiment more, replace “height” property with “url”
This will make
loaderInfogive the information about root SWF andcontentLoaderInfogive the information about the content being loaded by the Loader object. But both utilize theLoaderInfoobject.OK, now we know how to associate the loading events using
contentLoaderInfowith loader object. Next we shall start exploring all those events one by one in the proper sequence.Step 9: Which Events to Use?
We already have discussed the required events in Step 3. But, I am listing them again, just for a revision.
In the previous step, we used
Event.COMPLETE. But remember, this is the last event to be fired, so we must track other events beforeEvent.COMPLETE.Also, while loading an image there is no need to track all the above events.
Loading an image (locally) will require just these events:
If image is loaded remotely (via HTTP request) add
HTTPStatusEvent.HTTP_STATUSto the above list.But, for our example, we shall track all above events for the sake of learning. Also, you can any time change the URL to load a SWF – because, most of the time, tracking the loading process of a SWF requires all these events.
So, let us start with
IOErrorEvent.IO_ERROR.Step 10: IOErrorEvent.IO_ERROR
To track
IOErrorEvent.IO_ERROR, first add an import statement like so:We shall add a new method
loadErrorlike so:private function loadError(evt:IOErrorEvent):void { monitor.errorEventTF.text = "URL: '" + myURL + "' not found"; }In case the target image is not found, then
IOErrorEventwill be triggered. The aboveloadErrormethod will be invoked. This method will display a message about the URL in the dynamic textfield named “errorEventTF” which is inside the “monitor” MovieClip, already placed on the stage.We shall also modify the constructor method to associate it with the
loaderobject.public function MyLoader() { myURL = "MyImage.png"; loader = new Loader(); addChildAt(loader,0); loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loadError); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete); loadButton.addEventListener(MouseEvent.CLICK, loadAsset, false, 0, true); }To test the
IOErrorstatus, put an incorrect url (e.g.myURL = "MyImage1.png").Test the movie and click “Start Loading” button to see the status of
IOErrorevent. The monitor is displaying the incorrect URL.Step 11: Event.OPEN
To track
Event.OPEN, we shall add a new methodloadStart()like so:private function loadStart(evt:Event):void { monitor.openEventTF.text = String("Path opened"); }We shall also modify the
constructormethod to associate it with theloaderobject.public function MyLoader() { myURL = "MyImage.png"; loader = new Loader(); addChildAt(loader,0); loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loadError); loader.contentLoaderInfo.addEventListener(Event.OPEN, loadStart); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete); loadButton.addEventListener(MouseEvent.CLICK, loadAsset, false, 0, true); }Test the movie and click “Start Loading” button to see the status of
OPENevent.Step 12: ProgressEvent.PROGRESS
We shall track loading progress of target image by updating the total number of bytes currently loaded.
To track
ProgressEvent.PROGRESSevent, first we shall add a new variabletotBytesLoadedlike so:Then, add import statement for
ProgressEventClass like so:Now, we shall add a new method
loadProgresslike so:private function loadProgress(evt:ProgressEvent):void { totBytesLoaded = Math.ceil(evt.bytesLoaded / 1024); monitor.progressEventTF.text = "Bytes loaded :" + String(totBytesLoaded) + "kb"; }We shall also modify the
constructormethod to associate it with theloaderobject.public function MyLoader() { myURL = "MyImage.png"; loader = new Loader(); addChildAt(loader,0); loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loadError); loader.contentLoaderInfo.addEventListener(Event.OPEN, loadStart); loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, loadProgress); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete); loadButton.addEventListener(MouseEvent.CLICK, loadAsset, false, 0, true); }Test the movie and click “Start Loading” button to see the status of
PROGRESSevent.You might not see any visible load progress, since we are loading the target image locally. But, if it is going to be loaded remotely then you will have a visible loading progress.
You can still check the same local image, as if it is being loaded remotely. But, How?.
In Flash IDE test player, there is an option “Simulate Download”. This option must be checked to see how the download progress will look like for the same local image. This is just a simulation. It will give you only a rough idea about progress.
You can also simulate various connection speeds under “Download Settings”, available below “Simulate Download”.
Once “Simulate Download” is checked, click the “Start Loading” button. Now you will have the visible loading progress. Observe the “Progress” reading in our monitor window.
You can also add a progress bar for visual appearance of a loading progress.
I have not added a progress bar, but if you wish to add it, then here are some lines to add a progress bar.
Before coding for progress bar, drag the ProgressBar component from the Component panel to the Library to make it available for us.
After that, add import statement for ProgressBar like so:
import fl.controls.ProgressBar;Then, declare a var to hold progress bar like so:
var myProgressBar:ProgressBar;Now, instantiate it and specify a source as
contentLoaderInfoof aloaderobject like so:myProgressBar = new ProgressBar();myProgressBar.source = loader.contentLoaderInfo;myProgressBar.x = stage.stageWidth/2 - myProgressBar.width/2;myProgressBar.y = stage.stageHeight/2 - myProgressBar.height/2;addChild(myProgressBar);You can add these lines inside
constructormethod or you can create your own method and call it inside aconstructormethod.Do not forget to remove this progress bar inside
loadCompletemethod like so:removeChild(myProgressBar);Step 13: Event.INIT
To track
Event.INITevent, we shall add a new methodinitialised()like so:private function initialised(evt:Event):void { monitor.initEventTF.text = String("Initialized"); }We shall also modify the
constructormethod to associate it withloaderobject.public function MyLoader() { myURL = "MyImage.png"; loader = new Loader(); addChildAt(loader,0); loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loadError); loader.contentLoaderInfo.addEventListener(Event.OPEN, loadStart); loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, loadProgress); loader.contentLoaderInfo.addEventListener(Event.INIT, initialised); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete); loadButton.addEventListener(MouseEvent.CLICK, loadAsset, false, 0, true); }Test the movie and click “Start Loading” button to see the status of
INITevent.Step 14: HTTPStatusEvent.HTTP_STATUS
This
HTTPStatusEventis not required for loading images locally. We can simply replace the local path with a remote URL to check the status of this event.For the sake of completeness, we shall associate this event, even for loading images locally (in which case it will return “0″ as a status code). Check Step 3 for more info on this event.
To associate
HTTPStatusEvent, add the import statement like so:Add a new line inside
constructormethod like so:Now, put the new function
httpStatuslike so:private function httpStatus(evt:HTTPStatusEvent):void { monitor.httpEventTF.text = String(evt.status); }Test the movie and click on “Start Loading” button. The status will show “0″ since the target is local.
Step 15: Event.COMPLETE
Finally, it’s time for
Event.COMPLETEevent. We already have added thisCOMPLETEevent in Step 8, while learningLoaderInfoandcontentLoaderInfo.This is our destination. Treat this as an entry point for the code after loading is complete. Here you can start further processing.
Important tasks can be done here are listed down,
ENTER_FRAMEevent).Here, we shall modify the
loadComplete()method by adding a line,addChildAt(loader,0);We already have added this line in a
constructormethod. But, instead of adding it in aconstructormethod, we must add it after the loading process is completed. This is a good practice.So, we shall modify the
constructormethod, by removing the statementaddChildAt(loader,0);…to add the loaded image to the bottom of the display list.
The modified
constructorwill look like so:public function MyLoader() { myURL = "MyImage.png"; loader = new Loader(); loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loadError); loader.contentLoaderInfo.addEventListener(Event.OPEN, loadStart); loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, loadProgress); loader.contentLoaderInfo.addEventListener(Event.INIT, initialised); loader.contentLoaderInfo.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatus); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete); loadButton.addEventListener(MouseEvent.CLICK, loadAsset, false, 0, true); }Now, modify
loadCompletemethod like so:private function loadComplete(evt:Event):void { monitor.completeEventTF.text = String("Load Complete"); addChildAt(loader,0); }Test the movie and click on “Start Loading” button to see the status of
COMPLETEevent.Thus, we have the complete setup for monitoring the loading process of an image as well as SWF.
Also, you can simply replace the address path to load an image or SWF from any accessible remote server.
Step 16: Loading From Internet
Now, we shall see how to load an image from internet. Of course you must have a working Internet connection.
First, we shall change our
myURLlike so:Test the movie and click on “Start Loading” button. It will take some time to load.
Since, being “Test Player”, it allowed to load the target image over internet. But, if you run “SWF” outside Flash IDE, the Flash Player Security dialog box will tell you to go to Settings.
To prevent from this warning, we have two ways around it:
1. Set “Access network only” for Local playback security under Publish Settings.
2. If you want to keep “Access local files only” for Local playback security under Publish Settings and still want to load a target from internet, then, you need to register that SWF as “Trusted” file. To make it “Trusted“, run SWF outside Flash. Right click inside player and click on Global Settings from the context menu.
Then go to the “Advanced” tab and click “Trusted Location Settings”.
Click “Add” under “Trusted_Location_Settings”.
Now, under “Add Site”, click “Add File” and browse for this same SWF (i.e LoaderClassExample.swf). After specifying the path for this SWF, click on “Confirm” button.
Now close all boxes and re-open SWF. Click on “Start Loading” button and the loading is started successfully.
If suitable, you can also create “Projector.exe” to avoid all above settings. Projectors are always registered as trusted files.
So Why Doesn’t the Example Load?
Editor’s note:
You may be wondering why the example SWF (at the top of the page) doesn’t load the image when embedded in the page, but does load it when loaded in the browser directly.
This is because we host our SWFs and images on a different domain to the tutorial; they’re on s3.com rather than tutsplus.com. When the SWF tries to load the image, it’s looking for it at
http://active.tutsplus.com/tutorials/actionscript/MyImage.png, because it’s using a relative path to this page.However, the image is actually somewhere on s3.com, which means the SWF can’t find it. To get around this, we can use the SWF’s own
loaderInfo.urlproperty to find the URL of the actual SWF, and use this to construct a path to the image.(Alternatively, we could hard-code in an absolute path to the image, but that’s pretty inflexible.)
For more info, take a look at Step 21 onwards of this tutorial.
Conclusion:
Friends, that’s it for now. In this tutorial we saw how to load assets (in our case, an image) and monitored the loading process. We saw various events along with
contentLoaderInfo,loaderInfo, absolute/relative paths andLoaderContext. We also experimented with loading an image from internet.I hope you found this information helpful. Please add comments to improve it more.
So far, we’ve got a never-ending stream of enemies that our avatar must avoid; one touch, and it’s game over. But so what? Since there’s no way to track the player’s progress, they have no idea whether they did better in their latest round than they ever did before. In this tutorial, you’ll learn how to keep score, how to display it on the canvas, and how to let the player know when they’ve beaten their own records.
Refresher
In the first and second parts of this tutorial, we’ve covered a number of concepts: drawing images to the canvas, detecting mouse actions, using
ifandwhilestatements, storing variables in arrays, and the idea of variable scope.You can download the source files from the series up to this point if you’d like to dive straight in here, though I recommend reading all parts in order.
Our game’s JavaScript file initialises a bunch of variables (including two arrays to store enemy x- and y-coordinates) outside of all functions, so that their contents are available to all functions. It also contains a function called
setUpGame()which is run when the player first clicks the canvas; this loads the images, starts listening for any mouse movements, and sets up a “tick” function to be run every 25 milliseconds.When the mouse is moved, we move the position of the avatar, as stored in two variables – one for the x-coordinate and one for the y-coordinate – but we don’t immediately redraw the avatar’s image at this new location; all redrawing is handled in the tick function.
The tick function does four things:
All clear?
Warm Up Challenge
If it’s been a while since you read the second part of the series (or if you just want to check that you understand what’s going on), have a go at these little exercises. They’re completely optional and separate to the actual tutorial, so I recommend working on a copy of your project rather than the original. You can complete all of these exercises using only information from the series so far.
Easy
Swap the avatar and enemy images around so that the player controls a smiley face that’s avoiding the falling skulls.
(How many different ways can you figure out how to do this? I can think of three, off the top of my head.)
Medium
At the moment, there’s a flat one-in-twenty chance that a new enemy will be created in any given tick. I want you to make it so that there’s a one-in-one chance on the first tick, a one-in-two chance on the second, a one-in-three chance on the third, and so on.
To make this more challenging, do it in reverse: a one-in-1,000 chance on the first tick, a one-in-999 chance on the second, a one-in-998 chance on the third, and so on. (After the thousandth tick, make it a steady one-in-one chance.)
Hard
Rather than waiting for the enemies to appear one by one, have the game start with twenty enemies already on screen.
To make this more challenging, make them spread out around the canvas, and don’t allow them to overlap the avatar or each other.
Keeping Time
What’s the simplest way to measure how well the player is doing this round? The easiest thing I can think of is to keep track of how long it’s been since they hit an enemy. And since hitting an enemy means game over, we only need to keep track of how long they’ve been playing.
To do this, we’ll just create a variable, set it to 0 when the round starts, and increment it every tick. Let’s call this variable
ticksSurvived. And think: since we need to access it over and over again, it needs to be defined outside of all the functions, at the top of the JS file:Now, we’ll give
handleTick()yet another task to do: incrementticksSurvived. Put this after the collision detection; after all, if the avatar hits an enemy, it hasn’t actually survived the tick:function handleTick() { //... currentEnemyNumber = 0; while (currentEnemyNumber < numberOfEnemies) { if ( ( (avatarX < enemyXPositions[currentEnemyNumber] && enemyXPositions[currentEnemyNumber] < avatarX + 30) || (enemyXPositions[currentEnemyNumber] < avatarX && avatarX < enemyXPositions[currentEnemyNumber] + 30) ) && ( (avatarY < enemyYPositions[currentEnemyNumber] && enemyYPositions[currentEnemyNumber] < avatarY + 33) || (enemyYPositions[currentEnemyNumber] < avatarY && avatarY < enemyYPositions[currentEnemyNumber] + 30) ) ) { alert("You hit an enemy!"); } currentEnemyNumber = currentEnemyNumber + 1; } ticksSurvived = ticksSurvived + 1; }To display this, for now, we’ll just alter the alert that appears when the avatar hits an enemy:
function handleTick() { //... currentEnemyNumber = 0; while (currentEnemyNumber < numberOfEnemies) { if ( ( (avatarX < enemyXPositions[currentEnemyNumber] && enemyXPositions[currentEnemyNumber] < avatarX + 30) || (enemyXPositions[currentEnemyNumber] < avatarX && avatarX < enemyXPositions[currentEnemyNumber] + 30) ) && ( (avatarY < enemyYPositions[currentEnemyNumber] && enemyYPositions[currentEnemyNumber] < avatarY + 33) || (enemyYPositions[currentEnemyNumber] < avatarY && avatarY < enemyYPositions[currentEnemyNumber] + 30) ) ) { alert("You hit an enemy! Ticks survived: " + ticksSurvived); } currentEnemyNumber = currentEnemyNumber + 1; } ticksSurvived = ticksSurvived + 1; }That’s a bit weird; we’ve used the
+operator to add a string to a number. We can add another string to the end:function handleTick() { //... currentEnemyNumber = 0; while (currentEnemyNumber < numberOfEnemies) { if ( ( (avatarX < enemyXPositions[currentEnemyNumber] && enemyXPositions[currentEnemyNumber] < avatarX + 30) || (enemyXPositions[currentEnemyNumber] < avatarX && avatarX < enemyXPositions[currentEnemyNumber] + 30) ) && ( (avatarY < enemyYPositions[currentEnemyNumber] && enemyYPositions[currentEnemyNumber] < avatarY + 33) || (enemyYPositions[currentEnemyNumber] < avatarY && avatarY < enemyYPositions[currentEnemyNumber] + 30) ) ) { alert("You hit an enemy! You survived " + ticksSurvived + " ticks."); } currentEnemyNumber = currentEnemyNumber + 1; } ticksSurvived = ticksSurvived + 1; }Maybe this doesn’t seem odd to you, but some programming languages hate this. JavaScript doesn’t care. It knows that
"some" + "string"equals"somestring", and assumes that you want to treatticksSurvivedas a string in this situation.(Some things to try out:
12 + 6"12" + "6""12" + 612 + "6"Do they all do what you expect them to do?)
Anyway, let’s give this new code a try.
Click to try it out.
Great!
As before, when you click OK, the alert box appears again, because all it does is pause the ticks rather than actually stopping them. But note that the number in the box increases by one; this is proof that JavaScript doesn’t turn the number into a string permanently; it only uses it as a string for the purposes of adding it to another string.
While we’re on the subject of this alert box: don’t you find it annoying?
Try Again
At the moment, the only way to start a new round is to refresh the page. Let’s make it easier and less irritating to have another go, by making the “OK” button on the alert box reset the game.
Since the alert effectively pauses the game, whatever we put on the line after the alert will run once the player hits OK. Let’s make it call a new function,
startNewGame():currentEnemyNumber = 0; while (currentEnemyNumber < numberOfEnemies) { if ( ( (avatarX < enemyXPositions[currentEnemyNumber] && enemyXPositions[currentEnemyNumber] < avatarX + 30) || (enemyXPositions[currentEnemyNumber] < avatarX && avatarX < enemyXPositions[currentEnemyNumber] + 30) ) && ( (avatarY < enemyYPositions[currentEnemyNumber] && enemyYPositions[currentEnemyNumber] < avatarY + 33) || (enemyYPositions[currentEnemyNumber] < avatarY && avatarY < enemyYPositions[currentEnemyNumber] + 30) ) ) { alert("You hit an enemy! You survived " + ticksSurvived + " ticks."); startNewGame(); } currentEnemyNumber = currentEnemyNumber + 1; }So what do we need
startNewGame()to do?Perhaps we should just call
setUpGame()again – but no, nothing in that needs doing twice: we don’t need to load the images or add the mouse event listener or set up the ticks again.Have a think about what would be needed, and try out your ideas. My solution is below; take a look when you’re ready.
function startNewGame() { enemyXPositions = []; enemyYPositions = []; ticksSurvived = 0; }That’s all. You can do more if you wish, but this is the minimum required.
Note that we don’t have to do anything with the canvas – we don’t need to clear it, or draw anything to it, or manipulate the images – because this is all done in
handleTick(). We don’t have to go through all the elements in the arrays, one by one, either, because the arrays are simply a list of coordinates that we use to stamp the enemy images onto the canvas; the enemies don’t exist as actual objects.Click to try it out.
So this is great – the player can play the game over and over again to keep trying for a better score. Except… how do they know whether they’ve beaten their previous score? At the moment, they just have to remember they top score so far, or write it down. We can do better than that.
Remember the Best Score
How should we store the top score? In another variable, of course!
Naturally, it has to live outside of the functions; by now, I’m sure you understand why.
To begin with, we set it to 0, of course – the player hasn’t even completed a round. When the avatar hits an enemy, let’s update the new score as required:
while (currentEnemyNumber < numberOfEnemies) { if ( ( (avatarX < enemyXPositions[currentEnemyNumber] && enemyXPositions[currentEnemyNumber] < avatarX + 30) || (enemyXPositions[currentEnemyNumber] < avatarX && avatarX < enemyXPositions[currentEnemyNumber] + 30) ) && ( (avatarY < enemyYPositions[currentEnemyNumber] && enemyYPositions[currentEnemyNumber] < avatarY + 33) || (enemyYPositions[currentEnemyNumber] < avatarY && avatarY < enemyYPositions[currentEnemyNumber] + 30) ) ) { alert("You hit an enemy! You survived " + ticksSurvived + " ticks."); if (ticksSurvived > mostTicksSurvived) { mostTicksSurvived = ticksSurvived; } startNewGame(); } currentEnemyNumber = currentEnemyNumber + 1; }Let’s also tell the player that they beat their old high score:
while (currentEnemyNumber < numberOfEnemies) { if ( ( (avatarX < enemyXPositions[currentEnemyNumber] && enemyXPositions[currentEnemyNumber] < avatarX + 30) || (enemyXPositions[currentEnemyNumber] < avatarX && avatarX < enemyXPositions[currentEnemyNumber] + 30) ) && ( (avatarY < enemyYPositions[currentEnemyNumber] && enemyYPositions[currentEnemyNumber] < avatarY + 33) || (enemyYPositions[currentEnemyNumber] < avatarY && avatarY < enemyYPositions[currentEnemyNumber] + 30) ) ) { alert("You hit an enemy! You survived " + ticksSurvived + " ticks."); if (ticksSurvived > mostTicksSurvived) { alert("New high score!"); mostTicksSurvived = ticksSurvived; } startNewGame(); } currentEnemyNumber = currentEnemyNumber + 1; }Take a look:
Click to try it out.
Excellent. Now let’s give the player a little more information on how much better they did:
while (currentEnemyNumber < numberOfEnemies) { if ( ( (avatarX < enemyXPositions[currentEnemyNumber] && enemyXPositions[currentEnemyNumber] < avatarX + 30) || (enemyXPositions[currentEnemyNumber] < avatarX && avatarX < enemyXPositions[currentEnemyNumber] + 30) ) && ( (avatarY < enemyYPositions[currentEnemyNumber] && enemyYPositions[currentEnemyNumber] < avatarY + 33) || (enemyYPositions[currentEnemyNumber] < avatarY && avatarY < enemyYPositions[currentEnemyNumber] + 30) ) ) { alert("You hit an enemy! You survived " + ticksSurvived + " ticks."); if (ticksSurvived > mostTicksSurvived) { alert("You beat your old high score by " + (ticksSurvived - mostTicksSurvived) + " ticks!"); mostTicksSurvived = ticksSurvived; } startNewGame(); } currentEnemyNumber = currentEnemyNumber + 1; }Note here that
ticksSurvivedandmostTicksSurvivedare treated as numbers when subtracting one from the other, but the resulting expression(ticksSurvived - mostTicksSurvived)is treated as a string when added to the other strings! This can get really confusing, if you’re not careful.Click to try it out.
All right, so now the player knows how well they were doing, after they get game over, we should give them an indication of how well they are doing, while they’re still playing.
Drawing the Score to the Canvas
It’s really easy to write text into a canvas – and no, we don’t need to piece it together from different images of different letters!
Remember that to draw an image to the canvas we call
canvas.getContext("2d").drawImage(). Writing text is very similar: we callcanvas.getContext("2d").fillText().We must pass three arguments to
fillText():There’s an optional fourth argument:
…but we won’t worry about that for now.
To test this out, head to
handleTick(), find the line where we draw the avatar to the canvas, and draw a sample line of text just afterwards:gameCanvas.width = 400; //this erases the contents of the canvas gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY); gameCanvas.getContext("2d").fillText("Testing, testing, one, two, three.", 100, 100);Try it out:
Click to try it out.
Note that, since this gets drawn after the avatar and before the enemies, the enemies get drawn on top of it, while the avatar goes underneath:
Click to try it out.
Like I say, the first argument required is a string, and – as we’ve seen – we can construct a string by adding a string to a number. So, this should work:
gameCanvas.width = 400; //this erases the contents of the canvas gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY); gameCanvas.getContext("2d").fillText("Score: " + ticksSurvived, 100, 100);…and indeed it does!
Click to try it out.
But it’s a bit of a mess, floating there underneath the enemies with that tiny font. Let’s sort that out.
Tidy Up the Text
First, let’s move the text so that the enemies move underneath it. This just means drawing it after all of the enemies are drawn, so move the relevant line down:
gameCanvas.width = 400; //this erases the contents of the canvas gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY); currentEnemyNumber = 0; while (currentEnemyNumber < numberOfEnemies) { gameCanvas.getContext("2d").drawImage(enemyImage, enemyXPositions[currentEnemyNumber], enemyYPositions[currentEnemyNumber]); currentEnemyNumber = currentEnemyNumber + 1; } gameCanvas.getContext("2d").fillText("Score: " + ticksSurvived, 100, 100);Now, let’s move it to the top-left of the screen. That’s,
(0, 0)so this should work, right?gameCanvas.width = 400; //this erases the contents of the canvas gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY); currentEnemyNumber = 0; while (currentEnemyNumber < numberOfEnemies) { gameCanvas.getContext("2d").drawImage(enemyImage, enemyXPositions[currentEnemyNumber], enemyYPositions[currentEnemyNumber]); currentEnemyNumber = currentEnemyNumber + 1; } gameCanvas.getContext("2d").fillText("Score: " + ticksSurvived, 0, 0);Hmm. Nope. We can’t see the score at all. This is because it’s using
(0, 0)to place the bottom-left corner of the text. We need to move the text down a bit, then:gameCanvas.width = 400; //this erases the contents of the canvas gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY); currentEnemyNumber = 0; while (currentEnemyNumber < numberOfEnemies) { gameCanvas.getContext("2d").drawImage(enemyImage, enemyXPositions[currentEnemyNumber], enemyYPositions[currentEnemyNumber]); currentEnemyNumber = currentEnemyNumber + 1; } gameCanvas.getContext("2d").fillText("Score: " + ticksSurvived, 0, 10);Check it out:
Click to try it out.
Okay, so far so good. Now, let’s change the font itself. We do this by setting the canvas’s context’s
fontproperty to a CSS string representing the font. If you’re not familiar with CSS, don’t worry; at this stage, all you need to know is that it contains the font’s size and the fontface:gameCanvas.width = 400; //this erases the contents of the canvas gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY); currentEnemyNumber = 0; while (currentEnemyNumber < numberOfEnemies) { gameCanvas.getContext("2d").drawImage(enemyImage, enemyXPositions[currentEnemyNumber], enemyYPositions[currentEnemyNumber]); currentEnemyNumber = currentEnemyNumber + 1; } gameCanvas.getContext("2d").font = "10px Impact"; gameCanvas.getContext("2d").fillText("Score: " + ticksSurvived, 0, 10);(“px” stands for “pixels”.)
Note that we have to set the font before drawing the text!
Take a look:
Click to try it out.
It works – but the Impact font is really hard to read at that size. Let’s make it bigger:
gameCanvas.getContext("2d").font = "18px Impact"; gameCanvas.getContext("2d").fillText("Score: " + ticksSurvived, 0, 10);Click to try it out.
Unfortunately, this doesn’t work so well, because the font is still 10px from the top of the canvas, but now it’s 16px tall!
We could keep changing the y-position of the font to fix this, but there’s an alternative: we’ll make it so that the position we specify determines the top-left corner of the text, rather than the bottom-left corner. It’s easy:
gameCanvas.getContext("2d").font = "18px Impact"; gameCanvas.getContext("2d").textBaseline = "top"; gameCanvas.getContext("2d").fillText("Score: " + ticksSurvived, 0, 10);Of course, now, there’s a 10px gap between the top of the text and the top of the canvas:
Click to try it out.
…but that’s easy to fix now:
gameCanvas.getContext("2d").font = "18px Impact"; gameCanvas.getContext("2d").textBaseline = "top"; gameCanvas.getContext("2d").fillText("Score: " + ticksSurvived, 0, 0);Well, actually, I like a bit of padding, so let’s do this:
gameCanvas.getContext("2d").font = "18px Impact"; gameCanvas.getContext("2d").textBaseline = "top"; gameCanvas.getContext("2d").fillText("Score: " + ticksSurvived, 5, 5);Click to try it out.
Much better.
Different Fonts
Perhaps you’re wondering whether there’s a list of fonts we can use. Well, yes and no. See, if you pick a font that the player doesn’t have installed on their computer, then the text will just be displayed in the default font. Remember, JavaScript is drawing the text on the fly, using the player’s computer’s resources.
We’re fine using Impact, because it’s installed on every computer – but does that mean we can’t use any font other than the few in this list?
Fortunately, no. We can use any font we like, as long as we give the user access to it somehow. And for this, we’re going to use that CSS file – you know, the one we haven’t touched since the start of the series.
Suppose we have a font called “Really-Awesome”. This will exist on your computer, somewhere, as a file – probably a .TFF file (“True Type Font”). Let’s suppose that file is called
ReallyAwesomeFont.ttf.Now suppose you upload this font to your website – reallyawesomewebsite.com – so that there’s a direct URL to it:
http://reallyawesomewebsite.com/fonts/ReallyAwesomeFont.ttf.You can then let the player’s browser know about it by adding this to your CSS file:
@font-face { font-family: 'Really-Awesome'; src: url('http://reallyawesomewebsite.com/fonts/ReallyAwesomeFont.ttf'); }With this line in your CSS, you can alter your code like so:
gameCanvas.getContext("2d").font = "18px Really-Awesome"; gameCanvas.getContext("2d").textBaseline = "top"; gameCanvas.getContext("2d").fillText("Score: " + ticksSurvived, 5, 5);…and it’ll work, because their browser will look up the “Really-Awesome” font in the stylesheet, and find the URL to the TTF. Great!
I’m not going to demonstrate this because I don’t own the redistribution rights to any fonts; if I upload some and give you the link to the TTF as part of this tutorial, that’s not really fair. But there is an alternative…
Google Web Fonts
Google has assembled a large collection of fonts that you can use in your project in your CSS, using a similar principle as above. Take a look at the collection.
There are a few criteria by which you can search for fonts, and you can enter some sample text to see how it’ll be displayed:
Google Web Fonts.
I’m going to choose Iceland. When I click Quick Use, it gives me this HTML:
If you load http://fonts.googleapis.com/css?family=Iceland in your browser, you’ll see that it’s the same kind of thing as we wrote from scratch before:
@media screen { @font-face { font-family: 'Iceland'; font-style: normal; font-weight: 400; src: local('Iceland'), local('Iceland-Regular'), url('http://themes.googleusercontent.com/static/fonts/iceland/v1/F6LYTZLHrG9BNYXRjU7RSw.woff') format('woff'); } }It has a few more details, and the font is in WOFF format rather than TTF, but you get the idea.
You can use whichever font you like (and it doesn’t have to be a Google Web Font), but for the purpose of this tutorial I’m going to assume that you’re using Iceland. So, edit
game.htmland paste the font reference into it:Now, back in
main.js, change the font from Impact to Iceland:gameCanvas.getContext("2d").font = "18px Iceland"; gameCanvas.getContext("2d").textBaseline = "top"; gameCanvas.getContext("2d").fillText("Score: " + ticksSurvived, 5, 5);Take a look:
Click to try it out.
Cool!
Challenge: Show the Best Score
Now that the current score is on screen at all times, it’s only natural that the player will want to see what they’re trying to beat.
Using what you’ve learnt, draw their best score at the top-right of the canvas. This is a little trickier than it seems: you’ll have to decide what to do (if anything) when the current score overtakes the current top score!
Saving Scores Between Sessions
You’ve probably noticed by now that the high score is reset when you reload the page. This makes sense – after all, we run this line right at the start:
…but even without that line, the high score still wouldn’t persist. All variables get reset and unassigned when you leave the page.
However, there is an alternative: every browser sets aside 5MB of storage for each web site domain. You can store any string you like in this 5MB storage, and it’ll stay there even if the user closes their browser and restarts their computer.
It’s called local storage, and it’s really easy to use! To save something to it, you just need to give it two strings:
To retrieve it, you just need the name that you originally used.
A Quick Example
Here’s a quick example: let’s add something to the local storage right at the start of the
setUpGame()function:function setUpGame() { localStorage.setItem("exampleItem", "This is a great example.");Save the file, and load your game. Then close the tab.
Now, edit your JS file again, delete the line you just added, and replace it with a line that should retrieve the item:
function setUpGame() { alert(localStorage.getItem("exampleItem"));So, just to be clear, there is now nothing in the code that sets the value of
"exampleItem".When you load the game this time, you should see an alert with
"This is a great example."– proof that the string has been saved between sessions.We can remove this item from the local storage using
localStorage.removeItem():function setUpGame() { localStorage.removeItem("exampleItem");I recommend you do this now, and then delete the line entirely.
You can also clear everything in the local storage at once, using
localStorage.clear()(no arguments required). Okay – not quite everything. Your page can only affect the local storage space assigned to the domain on which it is hosted; I can’t clear your local storage for google.com from a page hosted at tutsplus.com, and vice-versa.Saving the Best Score
Now that we’ve seen an example, let’s put it into practice.
Whenever a new high score is set, let’s save it to the local storage. It only gets set in one place: when the avatar collides with an enemy and the current score is higher than the best score. So, add the call to
localStorage.setItem()in the appropriate place:while (currentEnemyNumber < numberOfEnemies) { if ( ( (avatarX < enemyXPositions[currentEnemyNumber] && enemyXPositions[currentEnemyNumber] < avatarX + 30) || (enemyXPositions[currentEnemyNumber] < avatarX && avatarX < enemyXPositions[currentEnemyNumber] + 30) ) && ( (avatarY < enemyYPositions[currentEnemyNumber] && enemyYPositions[currentEnemyNumber] < avatarY + 33) || (enemyYPositions[currentEnemyNumber] < avatarY && avatarY < enemyYPositions[currentEnemyNumber] + 30) ) ) { alert("You hit an enemy! You survived " + ticksSurvived + " ticks."); if (ticksSurvived > mostTicksSurvived) { alert("You beat your old high score by " + (ticksSurvived - mostTicksSurvived) + " ticks!"); mostTicksSurvived = ticksSurvived; localStorage.setItem("bestScore", mostTicksSurvived); } startNewGame(); } currentEnemyNumber = currentEnemyNumber + 1; }When the page is first loaded, we should check to see whether there already is a high score saved in the local storage, and assign
mostTicksSurvivedto that value, if so. (You don’t need to repeat this check once the page is loaded, unless you’re worried about the user playing the game in two separate tabs at once.)How do we check whether a value exists? All we have to do is put it inside an
ifcondition:if (localStorage.getItem("thisItemDoesNotExist")) { alert("This alert will never appear!"); }The
alert()above never gets called becauselocalStorage.getItem("thisItemDoesNotExist")does not exist. Easy, right? So at the start ofsetUpGame(), we can just write:function setUpGame() { if (localStorage.getItem("bestScore")) { mostTicksSurvived = localStorage.getItem("bestScore"); }Have a go. Load the page, set a high score, then beat it. Reload the page, then get a lower score – does it tell you you beat your old score?
Click to try it out.
Great! By the way, if you took the challenge earlier then you’ll be able to see your previous best score in the top-right corner of the canvas, and this will now carry over from session to session.
Sidenote: Strong and Weak Typing
We’re about done for this part of the series, but I just want to point out that, once again, we’ve been treating a string as a number and a number as a string: local storage only saves string values, yet we save a number to it when we save the best score, and use the value we retrieve from it as a number later on.
This is acceptable, because JavaScript is what’s called “weakly-typed”. Other programming languages are “strongly-typed”, which means that if you say that something is a number, then it stays a number; if you say something is a string, then it stays a string.
In a strongly-typed language, if you want to add the string
"Score: "to the number32, then you have to explicitly tell the language to treat32as a string, perhaps like so:"Score: " + (32 as String)Also, in a strongly-typed language, when you define a variable, you also specify what type it is:
But JavaScript doesn’t worry about these things. This doesn’t make it better or worse than a strongly-typed language, just different.
Wrapping Up
That’s it for this part of the tutorial. Now your game has both a game over condition and a means of keeping score. Plus, you learned about drawing text to a canvas, choosing fonts, and using the local storage.
If you’d like to challenge yourself before the next part, have a go at making these changes:
Enjoy!
Here’s a handy tool you can use to create tweens without having to guess all the numbers involved. Just click and drag to create the graph of the tween, tweak your desired timing, and export to any of four formats: Flash’s native tweening engine, Tweener, TweenLite, or TweenMax. And if you’re a Tuts+ Premium member, you can grab the source code for the tool itself!
Use Easer
Easer is simple to use:
Easerclass from the source download.Example Code
import com.activetofocus.easer.Easer; import caurina.transitions.Tweener; var easerArr:Array = [{Nx:69,My:0,Ny:-263,Mx:0},{Nx:360,My:-210,Ny:-8.5,Mx:183},{Nx:442,My:-135,Ny:-259,Mx:397},{My:-200,Mx:520}]; Tweener.addTween(mc,{y:29, time:2, transition:Easer.to(easerArr)});Get Easer’s Source
The download link at the top of this page is for the files required to use Easer. If you’d like to modify Easer, or just take it apart to see how it works, then you can get the source files for Easer itself from Tuts+ Premium.
Each month, we bring together a selection of the best tutorials and articles from across the whole Tuts+ network. Whether you’d like to read the top posts from your favourite site, or would like to start learning something completely new, this is the best place to start!
Huge Improvements to Tuts+ Premium
As many of you know, Tuts+ is accompanied by an online educational membership called Tuts+ Premium. We’re very excited to announce that the membership has received a huge upgrade, including a new library of courses, 27 top-selling educational eBooks, member forums, and a completely redesigned UI.
You can check out all the changes here, and find out about all the fantastic new content available!
Our new premium website is a drastic improvement over the old system. It includes a slick and polished user interface and presents all of our tutorials the way that you would expect, from within your browser. We are now providing courses and ebooks from within Tuts+ Premium to make it even more valuable. So what are you waiting for? Head over to the new Tuts+ Premium site and let us know what you think.
Take a Tour of Tuts+ Premium
Psdtuts+ — Photoshop Tutorials
Create Detailed Vintage Typography with Illustrator and Photoshop
In this tutorial we will demonstrate how to customize a typeface in Illustrator and then use Photoshop to create a stunning vintage detailed typographic design. Let’s get started!
Visit Article
Realistic Portrait Retouching With Photoshop
Today’s designers and photographers need to have a varied skill set that sets them apart from the pack. One common skill that both can use is portrait photography retouching. This tutorial will make you a more effective retoucher and help keep your portraits looking clean and realistic. Subtlety is – going too far will only make the results noticeable. The best comment you can get after presenting a final image is "I didn’t even notice you did that". Let’s get started!
Visit Article
How to Paint Clouds With Photoshop
Photoshop is an excellent tool for manipulating photographs but it can also be used as a means to create stunning digital art. This tutorial is part of a 25-part video tutorial series demonstrating everything you will need to know to start producing digital art in Photoshop. Digital Art for Beginners, by Adobe Certified Expert and Instructor, Martin Perhiniak will begin by teaching you how to draw in Photoshop. At the conclusion of this series you will know all you need to produce your own concept art and matte paintings in Photoshop.
Visit Article
Nettuts+ — Web Development Tutorials
How to Create a Sublime Text 2 Plugin
Sublime Text 2 is a highly customizable text editor that has been increasingly capturing the attention of coders looking for a tool that is powerful, fast and modern. Today, we’re going to recreate my popular Sublime plugin that sends CSS through the Nettuts+ Prefixr API for easy cross-browser CSS.
Visit Article
30 Days to Learn HTML and CSS: a Free Tuts+ Premium Course
If you’ve ever wanted to learn how to build websites and web apps, HTML and CSS are the first skills you should learn. They are so fundamental that we believe everyone has the right to learn these skills for free.
Visit Article
Dig into Dojo
Maybe you saw that tweet: “jQuery is a gateway drug. It leads to full-on JavaScript usage.” Part of that addiction, I contend, is learning other JavaScript frameworks. And that’s what this four-part series on the incredible Dojo Toolkit is all about: taking you to the next level of your JavaScript addiction.
Visit Article
Vectortuts+ — Illustrator Tutorials
How to Create a Vector Stamp Set in Illustrator
In the following tutorial you will learn how to create a vintage, vector stamp set in Adobe Illustrator. Learn how to build a stamp illustration, shape by shape. We’ll construct the stamp border vector shape, highlight the edges, create the branding, and give the vector postage stamp a vintage texture, and final illustrator stamp effect. Let’s get started with making this vector stamp in Illustrator and then transform this into a vector stamp set.
Visit Article
Create a Vector Bamboo Forest with Blends, Brushes and Profiles
In today’s tutorial you’ll learn how to create a bamboo vector illustration with the help of custom art and pattern brushes, blends and profiles on strokes in Adobe Illustrator CS5. Vector bamboo is an excellent element to use in your work to give a classic Japanese fine art print feel, for use as bamboo forest art background in a larger illustration, and to make your vector pictures stand out with stylish bamboo art. So let’s jump straight into it!
Visit Article
Massive Collection of Vintage Vector Graphics: Floral Borders, Corners, and Frames
Welcome design rockstar, today you’re in for a real treat. We have a massive collection of vector graphics compiled for free download today. We’ve assembled vintage vector floral graphics that have antique floral borders, classic ornate corners, and decorative frames.
Visit Article
Webdesigntuts+ — Web Design Tutorials
Bring Your Forms Up to Date With CSS3 and HTML5 Validation
Let’s look at how to create a functional form which validates users’ data, client-side. With that done, we’ll cover prettying it up using CSS, including some CSS3!
Visit Article
Design a Clean Launch Email for a Mobile App
During this tutorial we’ll be using Adobe Photoshop to create a unique and clean email, announcing the launch of a new mobile app. We’ll look at using smart objects, clipping masks, warping text, and even examine the psychology of our users.
Visit Article
Financial Best Practices for Web Design Freelancers
A lot of web designers start their careers as employees for larger design firms. However, it seems inevitable that most designers will at least consider going out on their own as freelancers, either for side jobs or full time. One major key to any successful freelancing career, though, is to stay on top of your finances.
Visit Article
Phototuts+ — Photography Tutorials
Amazing Animal Photos
Animals are, without a shadow of a doubt, a fascinating photography subject. They’re at their most intriguing when they’re unpredictable and wild, allowing you to capture something of their very nature. This collection is a look at some outstanding shots of animals, of every kind, in the hope that it inspires and encourages you in your endeavors to immortalize something of the creatures with which we share our existence.
Visit Article
The Best Way To Learn Sports Photography
Whether you’re a sports fan or not, it’s hard to deny the huge popularity of sports in modern culture. In today’s tutorial, instead of giving you tips about directly shooting sports, we’re focusing on giving you tips on learning how to shoot it. We hope you enjoy this new approach.
Visit Article
The Camera Swap Survival Guide
In photography circles, you run into two types of people. Those who love their gear and those who would just as soon forget about. I love making photographs, but I also love cameras and the other gear used in the process. Therefore, I love camera swaps and flea markets. The variety of tools available for our craft is astounding and frankly overwhelming. But I’m here to help. I’ve been going to swaps for 12 years. I’ve been ripped off more times than I care to admit, but after all this time, I’m finding more treasure than fool’s gold. So today, I’ll guide you through the jungle that is the camera swap.
Visit Article
Cgtuts+ — Computer Graphics Tutorials
Facial Animation With Morph Targets In Cinema 4D, Part 1 – Cg Premium Content
In this new two part Premium tutorial, Aleksey Voznesenski will give you an introduction to mesh animation in Cinema 4D, in this case a face with the use of the pose morph tag. We’ll also actually model the character, add materials, do some lighting, Faking GI is always fun, and then animate the little bugger.
Visit Article
Making Of: The Abandoned Lobby in Maya and Mentalray, A Lighting & Rendering Overview
In this overview tutorial, author Pratik Gulati will give you valuable insight into Lighting and Rendering a scene in Maya using Mentalray. Using his “Abandoned Backyard Lobby” image as a basis, Pratik will discuss how he approached the Lighting setup and walk you through the render settings for achieving a very realistically lit interior scene with Mentalray.
Visit Article
Quick Tip: Creating A Simple 3D Puzzle In Maya
In this quick tip, author Abed Ibrahim will show you a great way to create a simple 3D puzzle in Maya. Although simple in approach, this valuable technique can be used in a variety of projects, ranging from very simple puzzles to extremely complex scenes, or even animations.
Visit Article
Aetuts+ — After Effects Tutorials
Design A Fantastic Floating Card Logo Reveal – AE Premium
In this tutorial we’ll break down a project I created called Motion Cards. We’ll create 3 scene’s in which we’ll animate some falling customizable placeholders that will be re purposed using Trapcode Particular for a dynamic logo reveal.
Visit Article
Regenerate Like A “Time Lord” From Dr. Who
In this tutorial, we’ll be creating a regeneration effect, inspired by the Doctor Who TV show. I say inspired by, because it’s not screen-accurate, but we’ll be using AE’s built-in tools, alongside some stock footage, VCP’s Optical Flares and Trapcode Particular to pull off a pretty convincing regeneration.
Visit Article
An Apple Box At Its Core
In today’s video, I’ll talk about the Apple Box… a rectangular multi-purpose block used on production sets dating back to practically the beginning of film. This video is for exposure more than anything. A year ago, I didn’t even realize those boxes had a name, and now I end up pretty much using them at least once on every shoot I do. Hope you enjoy it.
Visit Article
Audiotuts+ — Audio & Production Tutorials
Why You Should Use Metering Tools while Mixing and Mastering
Last AES convention I met a manufacturing rep for a very prestigious equipment company. He was demoing a piece of equipment for me, showing me all the great knobs and buttons that it had to offer. Then he said something along the lines of, ’We don’t include a screen for these things since you’re supposed to mix with your ears.” That’s when I took my exit stage left. I think imposing a viewpoint like that is just plain wrong.
Obviously, you’re supposed to mix with your ears, but I don’t think looking at meters is going to destroy your mix.
Visit Article
Adding Flavor to Chords -” Sixth Chords -” Basix
Today we are going to cover how to add some flavor to your chords and compositions using sixths in both major and minor. Just like the 7ths chord tutorial, we will approach this chord concept from the ground up and look it from all possible angles. If you have heard of these types of chords but never knew exactly how they were constructed then this tutorial is for you. We will cover everything from basic structure to different voicings of the chords and try to apply them in a creative context. The sixth chords are waiting, you ready?
Visit Article
Podcasts that Musicians Should Subscribe To (And Why!)
Rather than listening to music as I drive, shave and garden, I regularly listen to podcasts. That way Im not just being entertained, but educated as well. In this post we introduce you to the Top 20 podcasts musicians should subscribe to.
A podcast is like a radio show, only distributed via RSS over the internet. Some are professionally produced, but many are done by amateurs. Most are about a particular interest or topic. And there are a ton of them on music and recording -” some featuring indie music, others exploring music technology, and many explaining techniques for playing and recording music. In this article we’ll focus on the podcasts that teach you about producing music, and not the ones that help you discover new music. Maybe we’ll cover those in a future post.
Visit Article
Activetuts+ — Flash, Flex & ActionScript Tutorials
TimelineLite Ultimate Starter Guide: Introduction
TimelineLite is the ultimate tool for creating elaborate and precise sequences of scripted animation. It is an integral part of the GreenSock Tweening Platform that allows you to make the most of TweenLite and TweenMax. This series of screencasts will walk you step by step through everything you need to know to take your AS3 tweening skills to the next level.
Visit Article
Getting Started With Scoreoid
Games are becoming ever more popular, especially casual games on mobile devices and tablets. In these games, the importance of leaderboards is multiplied – and game developers need a simple cross-platform solution for this. In this tutorial we will cover Scoreoid and how it can help you with game development.
Visit Article
Effectively Organize Your Game’s Development With a Game Design Document
Have you ever dived right in to developing a game, but found yourself having to constantly change aspects of the design or the gameplay due to a lack of planning? You should consider using a game design document: a guiding vision of the game as a whole, pulling together ideas and plans for the design, development, and business sides of your game.
Visit Article
Wptuts+ — WordPress Tutorials
Cardinal Sins of WordPress Theme Development
We talk alot on this site about tips and tricks for getting what you want out of WordPress… but today we’re going to take a step back from the technical stuff to look at some practices, bad habits, and coding faux pas that would be better left in our past. So, forgive the heavy-handed post title (haha!), we’re about talk bring up 5 surprisingly common practices that are blemishes on the platform.
Visit Article
DIY WordPress Framework Part 4: Using the Framework as a Boiler Plate
Last time we used our framework as a child theme, creating a totally new theme that depends on the framework. Today we’re going to use our framework as a boilerplate, copying the folder and making edits right to it.
Visit Article
WordPress Monthly News: November 2011
For some, WordPress is a livelihood whereas, for others, it’s just for fun. Nonetheless, anyone involved with WordPress needs to stay on track with the latest developments in the blogging world. This is our monthly article covering the latest developments in the WordPress world from news to the latest new kick-ass themes on our marketplaces.
Visit Article
Mobiletuts+ — Mobile Development Tutorials
jQuery Mobile 1.0
The official release of jQuery Mobile 1.0 was recently announced, and this tutorial will provide you with an overview of how this popular framework can assist you in your cross-platform and web based app development!
Visit Article
Building a Caterpillar Game with Cocos2D
In this series, we will be recreating the popular Atari game Centipede using the Cocos2D game engine for iOS. Centipede was originally developed for Atari and released on the Arcade in 1980. Since then, it has been ported to just about every platform imaginable. For our purposes, we will be calling the game Caterpillar.
Visit Article
Android SDK: Using the Text to Speech Engine
This tutorial will teach you to give your applications a voice with the Android SDK text to speech engine!
Visit Article
FreelanceSwitch — Articles and Resources for Freelancers
10 Free Apps for Working with Video
Video has become a must-have for every website or blog aiming to enhance its presence online. It’s a common practice that video-related orders are often assigned to freelancers, and these are not only simple video editing tasks, but the preparation of how-to’s, demos, screencasts, video embedding and distribution as well.
Here is a roundup of the most popular video software and services recommended for freelancers’ everyday use.
Visit Article
30 Top Facebook Apps for Business
Facebook is a good place to communicate with friends and get new job offers. Freelancers should not miss an employment opportunity on Facebook. The social network app section is a real treasure box with multiple handy applications.
These are some of the best Facebook applications that help freelances boost their productivity, enhance their business pages, show off their portfolio and attract new contacts. Since many freelancers work with social media as experts, account managers and analysts, the list below has both apps for personal profiles and Facebook pages.
Visit Article
The Latest Google Update is Fantastic News for Freelance Writers
On November 3, 2011 Google published a new article on their blog informing readers that fresh new content is now being seen as highly valuable on their blog or website. This “freshness update” is a new addition to Google’s “Caffeine web indexing system”.
Now blog owners who update their sites regularly will be rewarded with a higher search engine ranking. Let’s look at how freelance writers can put this new update to use in their business.
Visit Article
Most programmers have never touched design. If you have always wanted to beautiful graphics in your Flash projects but never knew how to use Adobe Photoshop or Illustrator, this tutorial is for you. Nice graphics can be created entirely with code. Drawing graphics for components with code even has a few advantages.
Prerequisites
This tutorial requires that you know how to perform basic tweens using Greensock’s TweenLite or TweenMax. You can download the latest version of TweenLite from the Greensock website. I will be using Flash CS5 Professional to complete this project. You may use any Flash IDE you’d like (such as FlashDevelop or FDT). It would also help to have a fair understanding of inheritance.
A Brief Introduction
Before we get started, let’s take a look at what we’ll be learning and why it’s important. When designing Flash applications there are many ways to generate graphics. You can use an external tool such as Adobe Illustrator or draw complex graphics with Adobe Photoshop. You can even use 3D utilities such as Cinema 4D to create eye-popping 3D bitmaps to import into Flash. These techniques are great for creating a well designed static application – static meaning not very dynamic, or in most cases, not dynamic at all.
If you use one of the above methods to generate graphics for Flash, you may find it a little difficult to resize the graphics without some loss of quality or, even worse, distortion. On the sunny side, you can import a vector file that was generated with Adobe Illustrator into your Flash application and scale the image infinitely without distorting the image – but when you want to change the overall ratio of the image, you will notice that your image becomes a little deformed.
What we are going to do today is learn how efficient it can be to draw our application’s graphics with code. We are going to create a beautiful animated button that is completely generated with ActionScript.
Step 1: Pros and Cons
Before we dig in, I’d like to discuss the importance of planning for your application and answering simple questions to decide whether to draw the graphics with code or by hand (e.g. with Adobe Photoshop). To help us answer the questions below, let’s first examine the pros and cons to drawing with code.
The Pros
The Cons
You should now find the following questions pretty easy to answer. There are three questions you need to ask yourself before you start your project:
Does your application require graphics to be static or dynamic?
Do the graphics or components in your application need to be exclusive to Flash or be consistent across multiple platforms?
Is the graphic design of your application more detailed or quite simple?
Now examine your test results. If your test results closely resemble the results below, than the techniques we are getting ready to learn are for you?
If your results do not look like the results above, you may want to consider other methods. But for the rest of us, let’s proceed.
Step 2: Brainstorming
So here’s the scenario. A client has just asked us to create an animated button for their Flash site and AIR app. The button must be consistent with the science fiction theme of the client’s website. The button’s height can be a fixed size but the button must be able to accomodate for different widths. The button must have a toggle state that has a blatant difference between it’s active and inactive states. Finally the button must have an API that’s easy to work with.
Now that we know that we’ve got this themed component to create, we need to think about how we are going to go about creating it. Well, we know that we’re going to be drawing a lot of graphics with code so that the component will be easily and efficiently editable. Why not start off with a base class that will add high level functionality on top Flash’s drawing API?
We don’t want to draw graphics over and over again by writing the same lines of code so we’ll have a class with particular properties and methods specifically designed for reusing the code for us. We’ll extend this class to create the main shapes that we’ll need for our button.
Step 3: The Editable Shape Class
Now it’s time to get to it! Create a new class called
EditableShapeand add the following classes to the classpath.Now create the class declaration. The class should extend the
flash.display.Spriteclass.public class EditableShape extends Sprite {Create the following variables(properties) before the class constructor.
Within the class constructor add the following code and I’ll explain what we just did.
The primary function of the constructor is to instantiate all of the properties that we have defined. We also gave each property a default value. You will be able to see what the result of these values are later. Each property that begins with an underscore will be given read/write access to outside code. The reasons these properties are not public properites is because we want to respond to the change in value of each particular property. We shall do this within the setter methods that we’ll create later.
Each property is based on particular value that must be passed into any of the
graphicsobject methods. One example is the_fillGradientColorsproperty. It is an array ofuintsthat will be passed into thegraphics.beginGradientFillmethod. Another example is the_pixelHintingproperty. It will be passed into thegraphics.lineStylemethod’spixelHintingparameter.The last line of the constuctor calls the
initmethod. Theinitmethod is very simple. It calls theupdatemethod.Step 4: The Update Method
The
updatemethod is probably the most important method within theEditableShapeclass. The main objective of this method is to clear any existing graphics and redraw them based upon the latest data. We will call this method everytime a property has changed so the changes can be seen immediately by the user if necassary. Create theupdatemethod.protected function update():void { if ( _matchGradientSize ) { _lineGradientWidth = _width; _lineGradientHeight = _height; _fillGradientWidth = _width; _fillGradientHeight = _height; } lineGradientBox.createGradientBox( lineGradientWidth, lineGradientHeight, toRadians( lineGradientRotation ), lineTx, lineTy ); fillGradientBox.createGradientBox( fillGradientWidth, fillGradientHeight, toRadians( fillGradientRotation ), fillTx, fillTy ); super.graphics.clear(); super.graphics.lineStyle( lineThickness, 0, 0, pixelHinting ); super.graphics.lineGradientStyle( lineGradientType, lineColors, lineAlphas, lineRatios, lineGradientBox, lineSpreadMethod ); if ( !_bitmapData || !_useBitmapFill ) super.graphics.beginGradientFill( fillGradientType, fillColors, fillAlphas, fillRatios, fillGradientBox, fillSpreadMethod ) else super.graphics.beginBitmapFill( _bitmapData ); draw(); }The
Matrixobjects we created in the constructor are used to manipulate gradients. We needed two of them. One for the fill gradient and the other for the line gradient.We will be overriding the
get graphicsmethod so that we can forbid access to this property to outside code. We don’t want any outside code tampering with our graphics at all. Because we are going to override theget graphicsmethod, we will need to access the method from the subclass. This is accomplished through thesuperobject. We will also create a protected method calledgetGraphicslater on. This method is needed to give subclasses access to thegraphicsobject without granting access to the object to outside code.In conclusion, the update method re-initializes the graphics and it does this based on the current values of the properties within the
EditableShapeinstance. The last line of code calls thedrawmethod which is an abstract method in theEditableShapeclass. The method should be overriden by a subclass and is intended for drawing a particular shape using thegraphicsobject within its implementation.Step 5: Clones and Copies
Create the draw method.
protected function draw():void { // Abstract }Again the
drawmethod is just a simple abstract method that we can ignore for now.We will need to copy the properties of one shape onto another. There may also be times when you’d like a complete clone of a particular shape. We’ll create two methods to accomplish this for us. Doing so will save us a lot of time in the long run. Instead of always re-writing code we’ll just call the appropriate method which copies or clones a shape. Create the
copyandclonemethods.public function copy( shape:EditableShape ):void { _width = shape.width; _height = shape.height; _matchGradientSize = shape.matchGradientSize; _fillGradientType = shape.fillGradientType; _fillSpreadMethod = shape.fillSpreadMethod; _fillColors = shape.fillColors; _fillAlphas = shape.fillAlphas; _fillRatios = shape.fillRatios; _fillGradientWidth = shape.fillGradientWidth; _fillGradientHeight = shape.fillGradientHeight; _fillGradientRotation = shape.fillGradientRotation; _fillTx = shape.fillTx; _fillTy = shape.fillTy; _lineGradientType = shape.lineGradientType; _lineSpreadMethod = shape.lineSpreadMethod; _lineThickness = shape.lineThickness; _lineColors = shape.lineColors; _lineAlphas = shape.lineAlphas; _lineRatios = shape.lineRatios; _lineGradientWidth = shape.lineGradientWidth; _lineGradientHeight = shape.lineGradientHeight; _lineGradientRotation = shape.lineGradientRotation; _lineTx = shape.lineTx; _lineTy = shape.lineTy; _useBitmapFill = shape.useBitmapFill; if ( _bitmapData ) _bitmapData = shape.bitmapData.clone(); if ( filters ) filters = shape.filters; alpha = shape.alpha; update(); } public function clone():EditableShape { var c:Class = Class( getDefinitionByName( getQualifiedClassName( this ) ) ); var shape:EditableShape = new c(); shape.width = _width; shape.height = _height; shape.matchGradientSize = _matchGradientSize; shape.fillGradientType = _fillGradientType; shape.fillSpreadMethod = _fillSpreadMethod; shape.fillColors = _fillColors; shape.fillAlphas = _fillAlphas; shape.fillRatios = _fillRatios; shape.fillGradientWidth = _fillGradientWidth; shape.fillGradientHeight = _fillGradientHeight; shape.fillGradientRotation = _fillGradientRotation; shape.fillTx = _fillTx; shape.fillTy = _fillTy; shape.lineGradientType = _lineGradientType; shape.lineSpreadMethod = _lineSpreadMethod; shape.lineThickness = _lineThickness; shape.lineColors = _lineColors; shape.lineAlphas = _lineAlphas; shape.lineRatios = _lineRatios; shape.lineGradientWidth = _lineGradientWidth; shape.lineGradientHeight = _lineGradientHeight; shape.lineGradientRotation = _lineGradientRotation; shape.lineTx = _lineTx; shape.lineTy = _lineTy; shape.useBitmapFill = _useBitmapFill; if ( _bitmapData ) shape.bitmapData = _bitmapData.clone(); shape.filters = filters; shape.alpha = alpha; return shape; }The
copymethod takes theshapeparameter and sets all of the properties equal to that of the currentEditableShape.We will not be using the
clonemethod in this tutorial but I just threw it in as an extra bonus. The method returns a cloned copy of the currentEditableShapeclass.Step 6: Finishing Up the EditableShape
The last thing we need to do is grant read/write access to all of the shape’s main properties. Write the following getter and setter methods within the
EditableShapeclass.public override function set width(value:Number):void { _width = value; update(); } public override function get width():Number { return _width; } public override function set height(value:Number):void { _height = value; update(); } public override function get height():Number { return _height; } public function set fillGradientType( value:String ):void { switch ( value.toLowerCase() ) { case "linear" : case "radial" : _fillGradientType = value.toLowerCase(); update(); break; default : //Do nothing } } public function get fillGradientType():String { return _fillGradientType; } public function set fillSpreadMethod(value:String):void { switch ( value.toLowerCase() ) { case "pad" : case "reflect" : case "repeat" : _fillSpreadMethod = value.toLowerCase(); update(); default : //Do nothing } } public function get fillSpreadMethod():String { return _fillSpreadMethod; } public function set fillColors(array:Array):void { var a:Array = []; for each( var color:uint in array ) { if ( color is uint ) { a.push( color ); } } _fillColors = a; update(); } public function get fillColors():Array { return _fillColors; } public function set fillAlphas( array:Array ):void { var a:Array = []; for each( var nAlpha:Number in array ) { if ( nAlpha is Number) { a.push( nAlpha ); } } _fillAlphas = a; update(); } public function get fillAlphas():Array { return _fillAlphas; } public function set fillRatios(array:Array):void { var a:Array = []; for each(var ratio:Number in array) { if (ratio is int) { a.push(ratio); } } _fillRatios = a; update(); } public function get fillRatios():Array { return _fillRatios; } public function set fillGradientWidth(value:Number):void { _fillGradientWidth = value; update(); } public function get fillGradientWidth():Number { return _fillGradientWidth; } public function set fillGradientHeight(value:Number):void { _fillGradientHeight = value; update(); } public function get fillGradientHeight():Number { return _fillGradientHeight; } public function set fillGradientRotation(degrees:Number):void { _fillGradientRotation = degrees; update(); } public function get fillGradientRotation():Number { return _fillGradientRotation; } public function set fillTx(value:Number):void { _fillTx = value; update(); } public function get fillTx():Number { return _fillTx; } public function set fillTy(value:Number):void { _fillTy = value; update(); } public function get fillTy():Number { return _fillTy; } public function set lineGradientType(value:String):void { switch (value.toLowerCase()) { case "linear" : case "radial" : _lineGradientType = value.toLowerCase(); update(); break; default : //Do nothing } } public function get lineGradientType():String { return _lineGradientType; } public function set lineSpreadMethod(value:String):void { switch (value.toLowerCase()) { case "pad" : case "reflect" : case "repeat" : _lineSpreadMethod = value.toLowerCase(); update(); default : //Do nothing } } public function get lineSpreadMethod():String { return _lineSpreadMethod; } public function set lineThickness(value:Number):void { _lineThickness = value; update(); } public function get lineThickness():Number { return _lineThickness; } public function set lineColors(array:Array):void { var a:Array = []; for each(var color:uint in array) { if (color is uint) { a.push(color); } } _lineColors = array; update(); } public function get lineColors():Array { return _lineColors; } public function set lineAlphas(array:Array):void { var a:Array = []; for each(var nAlpha:Number in array) { if (nAlpha is Number) { a.push(nAlpha); } } _lineAlphas = a; update(); } public function get lineAlphas():Array { return _lineAlphas; } public function set lineRatios(array:Array):void { var a:Array = []; for each(var ratio:Number in array) { if (ratio is int) { a.push(ratio); } } _lineRatios = a; update(); } public function get lineRatios():Array { return _lineRatios; } public function set lineGradientWidth(value:Number):void { _lineGradientWidth = value; update(); } public function get lineGradientWidth():Number { return _lineGradientWidth; } public function set lineGradientHeight(value:Number):void { _lineGradientHeight = value; update(); } public function get lineGradientHeight():Number { return _lineGradientHeight; } public function set lineGradientRotation(degrees:Number):void { _lineGradientRotation = degrees; update(); } public function get lineGradientRotation():Number { return _lineGradientRotation; } public function set lineTx(value:Number):void { _lineTx = value; update(); } public function get lineTx():Number { return _lineTx; } public function set lineTy(value:Number):void { _lineTy = value; update(); } public function get lineTy():Number { return _lineTy; } public function set matchGradientSize(value:Boolean):void { _matchGradientSize = value; update(); } public function get matchGradientSize():Boolean { return _matchGradientSize; } public function set bitmapData( value:BitmapData ):void { _bitmapData = value; update(); } public function get bitmapData():BitmapData { return _bitmapData; } public function set useBitmapFill( value:Boolean ):void { _useBitmapFill = value; update(); } public function get useBitmapFill():Boolean { return _useBitmapFill; } public function set firstFillColor( value:uint ):void { _fillColors[ 0 ] = value; update(); } public function get firstFillColor():uint { if ( _fillColors.length > 0 ) { return _fillColors[ 0 ]; } else { return 0; } } public function set pixelHinting( value:Boolean ):void { _pixelHinting = value; update(); } public function get pixelHinting():Boolean { return _pixelHinting; } public override function get graphics():Graphics { return null; } protected function getGraphics():Graphics { return super.graphics; }All of the getter methods just return the corresponding properties value. But for the setter methods we correct or filter out any unwanted input. We also call the
updatemethod so that there is an immediate reaction to the new value or values.Step 7: Drawing Rectangles
The majority of our button will consist of rectangle shapes. Unless we want to bore our client’s users with a bunch of lame blocky shapes, we will want to be able to round the edges of our rectangles. The
EditableShapeclass is meant to be extended, meaning that it should have subclasses that finalize the primary function of the class. TheEditableShapeclass takes care of all of the hard work for us. All we have to do now is draw the shape within a subclass. Create a new class calledRectangleShapewhich extends theEditableShapeclass. The class declaration should look as follows.public class RectangleShape extends EditableShape {Add the following private properties to the class directly after the class declaration.
I mentioned before that we will be rounding the edges to our rectangles. As you have probably guessed already, we will be granting read/write access to these properties just as we did with all of the other inherited properties. This will allow us to react to any change the
ellipseWidthandellipseHeightproperties immediately after they have been altered.Reminder: This is accomplished by calling the
updatemethod.Create the class constructor. Give the
_ellipseWidthand_ellipseHeightproperties a default value of0. We don’t want the edges to be rounded by default.public function RectangleShape() { _ellipseWidth = 0; _ellipseHeight = 0; super(); }You should remember the
drawmethod from theEditableShapeclass was an abstract method. That is a method that has an empty implementation. We are going to override this method to give it the proper functionality. The methods name is self explanatory. The method will do the actual drawing of the shape.Important: We call the base class’s constructor,
super(), after we have assigned a value to the_ellipseWidthand_ellipseHeightproperties. Remember the the constructor initializes the shape by calling theinitmethod which then calls theupdatemethod which finally calls thedrawmethod. You will see in a minute that thedrawmethod requires these properties. Forgeting to call the constructor last will result in an argument error thrown by thedrawRoundRectmethod of thegraphicsobject.protected override function draw():void { getGraphics().drawRoundRect( 0, 0, width, height, _ellipseWidth, _ellipseHeight ); }I have to apologize to all of you who were expecting ten or more lines of code here in the
drawmethod. That’s not the case here. Remember that the base class(EditableShape) does most of the work for us. It handles colors, gradients, alphas, line properties and so on. All we needed to do is draw the shape and that’s what we’ve done. Thegraphicsobject can no longer be accessed by outside code since the property has been overriden so we had to access it through the protected method,getGraphics. Finally we call thedrawRoundRectmethod on thegraphicsobject and we pass in the appropriate parameters.Step 8: Overriding the Copy and Clone Methods
There are two more methods we need to override before we can conclude this class. The first method is the
copymethod. This is the method from theEditableShapeclass that takes anEditableShapeas a parameter and causes the properties of the parent shape onto the parameter shape. We need add some functionality to this method. We will be overriding this method but that doesn’t mean that we have to forget everything that the base class does with this method. We’ll use thesuperobject to call the method from the base class so that it inherits its original functionality while adding new functionality to it.public override function copy( shape:EditableShape ):void { super.copy( shape ); if ( shape is RectangleShape ) { var rect:RectangleShape = shape as RectangleShape; ellipseWidth = rect.ellipseWidth; ellipseHeight = rect.ellipseHeight; } }Can you tell what just happened? If the parameter,
shape, is aRectangleShapewe also copy theellipseWidthandellipseHeightproperties. If you have a collection ofRectangleShapes, that you are using to draw a graphic with, and you’d like them all to have the sameellipseWidthandellipseHeightproperties, this will keep you from writing too many extra lines of code.The second method we need to override is the
clonemethod.public override function clone():EditableShape { var shape:RectangleShape = super.clone() as RectangleShape; shape.ellipseWidth = _ellipseWidth; shape.ellipseHeight = _ellipseHeight; return shape; }The
clonemethod also inherits the implementation of the base class. But with this implementation we have added theellipseWidthandellipseHeightproperties.Step 9: More Access
Write the following getter and setter methods:
public function set ellipseWidth( value:Number ):void { if ( value < 0 ) value = 0; _ellipseWidth = value; update(); } public function get ellipseWidth():Number { return _ellipseWidth; } public function set ellipseHeight( value:Number ):void { if ( value < 0 ) value = 0; _ellipseHeight = value; update(); } public function get ellipseHeight():Number { return _ellipseHeight; }We have not only granted the read/write access that is needed by outside code, but we have also made sure that the
ellipseWidthandellipseHeightare never below zero. There is also an immediate reaction to these properties being set or changed.Note: If you do not want to correct any values for the
ellipseWidthorellispeHeightproperties, you do not have to. There may be a time when you’d want theellipseWidthorellipseHeightto be less than zero.Now that we have our first shape complete. Let’s take a quick look at what our shape looks like when we first create it. You will need to create a document class and create a new instance of the
RectangleShapeobject within it. Add the newRectangleShapeto thestage. Here’s my code:Let’s look at the result.
This is the what our shape looks like by default. Feel free to play around with it a bit so that you get a good feel for it. Try changing the colors or modifying the size.
Step 9: Drawing Circles
We are going to create a sort of bubbling effect for the over state of our button. This means that we will need bubbles. Create the
EclipseShapeclass. Make sure that it extends theEditableShapeclass.package { public class EllipseShape extends EditableShape { public function EllipseShape() { super(); } protected override function draw():void { getGraphics().drawEllipse( 0, 0, width, height ); } } }Once again the power of inheritance has allowed us to recycle old code instead of writing the same code over and over again. This class only contains inherited properties and methods. We had to override the
drawmethod in order to create the actual eclipse shape though. ThedrawEclipsemethod uses the inheritedwidthandheightproperties to draw our shape. And once again we access thegraphicsobject using the protectedgetGraphicsmethod.We now have all of the shapes we need to draw our button.
Step 10: Starting the Button
Create a new class that extends
flash.display.Sprite. You can call the class whatever you’d like. I’m going to use the nameMyButton. Import the following classes.import flash.display.Sprite; import flash.text.TextField; import flash.text.TextFormat; import flash.filters.BlurFilter; import flash.filters.GlowFilter; import flash.events.MouseEvent; import flash.filters.DropShadowFilter; import flash.system.System; import flash.filters.BevelFilter; import com.greensock.TweenMax; public class MyButton extends Sprite {Define the following properties.
Just like before, any property that starts with an underscore is either expected to cause an immediate reaction when set or the property will be read-only.
The
_widthproperty will be used to override thewidthproperty of the base class and we redraw the shape everytime thewidthproperty is set.The
activeproperty is going to be used to swap the toggle states of the button later.You will notice several properties that are
RectangleShapes, several properties that areTextFields, and aSprite. We will see the role each of these properties play later.We’ll be storing all of our bubbles in the
circlesArray. To improve performance we will need to pause various tweens. In order to do this we will need to store these tweens in an array so that they can be accessed whenever we need them.Last we have the
circleBlurproperty which is aBlurFilterthat is to be applied to each bubble, and we have the_toggleproperty which affects the behavior of the button.Create the following public constants.
The height of our button will not change so it’s appropriate to store the value of the button’s height in a class constant. The size of the button’s ellipse is relative to the button’s height. If the button becomes too large or too small, our button may lose the Pill Button look that we are going for. Also the number of bubbles is predetermined so we store this value in a class constant as well.
Step 11: The Base
When ever I am drawing graphics for a component like a text field or a button, I always, always, always… start with a base shape. This is a shape that acts as the foundation of the shape. It is the backbone of the component. How the graphic is structured is entirely dependent on how I construct the base.
You will notice a property that we have defined called
toggleBase. I am not talking about this property. I am talking about thebaseproperty itself. This shape will define the outline of the button. Let’s create this property and initialize its values now. Add the following code to the constructor of the class that you have defined as your button.public function MyButton() { super(); buttonMode = true; mouseChildren = false; _width = 100; base = new RectangleShape(); base.height = BUTTON_HEIGHT; base.ellipseHeight = base.ellipseWidth = BUTTON_HEIGHT; base.fillGradientRotation = 90; base.fillColors = [ 0xAAAAAA, 0x444444 ]; base.lineColors = [ 0x222222 ]; base.lineAlphas = [ .5 ]; base.lineRatios = [ 1 ]; addChild( base ); }The default
fillGradientRotationis 0. This value is in degrees but is converted into radians when passed into the gradient box(Matrix). Along with initializing the button’s size, we have to set the rotation of the gradient. In this case we want this value to be 90 degrees. We also set thefillColors(as anArray), and thelineColors.It is important to remember that the default value for these properties is an
Arraythat has alengthof two. All of theArrayproperties(lineColor,lineAlpha, andlineRatios) must be the samelength. If they aren’t, the graphics will not draw. This is why we set thelineAlphasandlineRatiosproperties.If you test the Flash movie, you should get this.
This is the overrall shape we are going for, “The Gel Pill”. Let’s make it shine a little bit.
Step 12: The Reflection
We’ve got the main structure of the button down already but now we need to add the small thing that I love to preach about. We need lots and lots of depth. We can start by adding the reflection. When light hits a glossy surface, it reflects off of the surface. Instantiate and initialize the
ref(short for reflection) property.We have turned the
alphaproperty down a little bit so the the reflection isn’t so intense but we have basically followed the same steps to draw therefshape as we did with thebaseshape. And of course we have sized and positioned the reflection relative to the size of the button’sheight. Let’s see what we have now.Not too bad. Still needs a lot more depth though. We’ll add some more later.
Step 13: The Inner Light
Sometimes when you are creating art, you notice that it’s those tiny little details that make the biggest difference in the appearance of your work. The
lightproperty will add the tiny detail into our button that makes all of the difference. This property will also be used a little differently in our button’s over and out states, but for now it will make our button appear to have a semi-transparent surface.Add the following code to your button’s constructor method.
The constructor should now look like this:
public function MyButton() { super(); buttonMode = true; mouseChildren = false; _width = 100; base = new RectangleShape(); base.height = BUTTON_HEIGHT; base.ellipseHeight = base.ellipseWidth = BUTTON_HEIGHT; base.fillGradientRotation = 90; base.fillColors = [ 0xAAAAAA, 0x444444 ]; base.lineColors = [ 0x222222 ]; base.lineAlphas = [ .5 ]; base.lineRatios = [ 1 ]; ref = new RectangleShape(); ref.fillGradientRotation = 90; ref.ellipseHeight = ref.ellipseWidth = ref.height = BUTTON_HEIGHT / 2; ref.fillColors = [ 0xFFFFFF, 0xFFFFFF ]; ref.fillAlphas = [ 1, 0 ]; ref.lineColors = [ 0 ]; ref.lineAlphas = [ 0 ]; ref.lineRatios = [ 0 ]; ref.alpha = .5; ref.x = ref.ellipseHeight / 2; ref.width = _width - ref.height; light = new RectangleShape(); light.copy( ref ); light.x = ref.x; light.y = BUTTON_HEIGHT - light.height; light.width = _width - light.height; light.fillColors = [ 0xFFFFFF, 0xDDDDDD ]; light.filters = [ new GlowFilter( 0xFFFFFF, .8, 10, 10, 2, 3 ), new BlurFilter( 6, 6, 3 ) ]; light.alpha = lightAlpha = .3; addChild( base ); addChild( light ); addChild( ref ); }We have added a
GlowFilterand aBlurFilterto this object so that we can achieve a specific look. We don’t want the shape to look like a shape at all but empty space within the button. Notice that we have used thecopymethod to mimic the properties of therefobject. There’s no need to write the same lines of code for thelightobject. Just mimic therefproperty and make just a few modifications and you have yourself a new shape.See how we are beginning to see more depth as our shape is being constructed? The pill doesn’t look fully opaque and it isn’t very transparent either. We want an “Out of This World” kind of look so just in between is perfect.
Step 14: Simple Text
Now that we have a good foundation down on the table, it’s appropriate to begin adding the first
TextFieldto the button. Initialize thetxtproperty. And create a mask that is the same size and ellipse as thebaseso that the text doesn’t appear outside of the button.public function TutButton() { super(); buttonMode = true; mouseChildren = false; _width = 100; base = new RectangleShape(); base.height = BUTTON_HEIGHT; base.ellipseHeight = base.ellipseWidth = BUTTON_HEIGHT; base.fillGradientRotation = 90; base.fillColors = [ 0xAAAAAA, 0x444444 ]; base.lineColors = [ 0x222222 ]; base.lineAlphas = [ .5 ]; base.lineRatios = [ 1 ]; ref = new RectangleShape(); ref.fillGradientRotation = 90; ref.ellipseHeight = ref.ellipseWidth = ref.height = BUTTON_HEIGHT / 2; ref.fillColors = [ 0xFFFFFF, 0xFFFFFF ]; ref.fillAlphas = [ 1, 0 ]; ref.lineColors = [ 0 ]; ref.lineAlphas = [ 0 ]; ref.lineRatios = [ 0 ]; ref.alpha = .5; ref.x = ref.ellipseHeight / 2; ref.width = _width - ref.height; light = new RectangleShape(); light.copy( ref ); light.x = ref.x; light.y = BUTTON_HEIGHT - light.height; light.width = _width - light.height; light.fillColors = [ 0xFFFFFF, 0xDDDDDD ]; light.filters = [ new GlowFilter( 0xFFFFFF, .8, 10, 10, 2, 3 ), new BlurFilter( 6, 6, 3 ) ]; light.alpha = lightAlpha = .3; txt = new TextField(); txt.selectable = false; txt.type = "dynamic"; txt.wordWrap = false; txt.multiline = false; txt.autoSize = "left"; txt.defaultTextFormat = new TextFormat( null, 27 ); txt.textColor = 0x333333; txt.text = "label"; txt.height = BUTTON_HEIGHT; txtMask = new RectangleShape(); txtMask.copy( base ); txtMask.filters = []; txt.mask = txtMask; addChild( base ); addChild( light ); addChild( ref ); addChild( txt ); addChild( txtMask ); }We have once again utilized the
copymethod by creating a copy of the base object. We could have very well have used theclonemethod also. The result would be the same. Also note that we have set thefiltersarray of thetxtMaskobject to a blank array. This is because when thecopymethod used thefiltersproperty is also inherited. We have just simply overriden this property. Let’s see where we’re at.We will position all of the
TextFieldslater on. For now ourtxtwill just have to hang out on the left side of our button.Step 15: Setting Up for the Over State
Our button will illuminate and display a sort of science fiction like animation sequence with bubbles when the mouse hovers over the button. Of course the entire sequence will be accomplished 100% with code.
But before we can do this we need to create the elements of the sequence. The first is the background object, or the
glowproperty, of the over state. Let’s add the glow object now. Add the following code to the constructor method:We will also need a container for the bubbles to be displayed in and a mask for that container so that the bubbles don’t float out of the button.
public function MyButton() { super(); buttonMode = true; mouseChildren = false; _width = 100; base = new RectangleShape(); base.height = BUTTON_HEIGHT; base.ellipseHeight = base.ellipseWidth = BUTTON_HEIGHT; base.fillGradientRotation = 90; base.fillColors = [ 0xAAAAAA, 0x444444 ]; base.lineColors = [ 0x222222 ]; base.lineAlphas = [ .5 ]; base.lineRatios = [ 1 ]; ref = new RectangleShape(); ref.fillGradientRotation = 90; ref.ellipseHeight = ref.ellipseWidth = ref.height = BUTTON_HEIGHT / 2; ref.fillColors = [ 0xFFFFFF, 0xFFFFFF ]; ref.fillAlphas = [ 1, 0 ]; ref.lineColors = [ 0 ]; ref.lineAlphas = [ 0 ]; ref.lineRatios = [ 0 ]; ref.alpha = .5; ref.x = ref.ellipseHeight / 2; ref.width = _width - ref.height; light = new RectangleShape(); light.copy( ref ); light.x = ref.x; light.y = BUTTON_HEIGHT - light.height; light.width = _width - light.height; light.fillColors = [ 0xFFFFFF, 0xDDDDDD ]; light.filters = [ new GlowFilter( 0xFFFFFF, .8, 10, 10, 2, 3 ), new BlurFilter( 6, 6, 3 ) ]; light.alpha = lightAlpha = .3; txt = new TextField(); txt.selectable = false; txt.type = "dynamic"; txt.wordWrap = false; txt.multiline = false; txt.autoSize = "left"; txt.defaultTextFormat = new TextFormat( null, 27 ); txt.textColor = 0x333333; txt.text = "label"; txt.height = BUTTON_HEIGHT; txtMask = new RectangleShape(); txtMask.copy( base ); txtMask.filters = []; txt.mask = txtMask; glow = new RectangleShape(); glow.copy( base ); glow.fillColors = [ 0xFFFFFF ]; glow.fillAlphas = [ 1 ]; glow.fillRatios = [ 0 ]; glow.lineColors = [ 0 ]; glow.lineAlphas = [ 0 ]; glow.lineRatios = [ 0 ]; glow.filters = [ new GlowFilter( 0x00BCE9, 1, 10, 10, 2, 3, false, false ), new GlowFilter( 0x00BCE9, 1, 20, 20, 2, 3, true ) ]; effectContainer = new Sprite(); effectContainer.cacheAsBitmap = true; effectMask = new RectangleShape(); effectMask.copy( base ); effectMask.filters = []; effectContainer.mask = effectMask; addChild( base ); addChild( light ); addChild( ref ); addChild( txt ); addChild( txtMask ); addChild( glow ); addChild( effectContainer ); addChild( effectMask ); }Step 16: Bubbles
Now we are going to blow a few bubbles. Add the following lines of code to the constructor method:
The
circleBluris aBlurFilterthat will be applied to all bubbles. This will give each bubble the specific science like look that we’re going for. We also need to create the arrays that will store all of the bubbles and the tweens that will animate each bubble.We need a method that will generate all of the bubbles we need.
private function createEffect():void { for ( var i:int = 0; i < NUM_OF_CIRCLES; i++ ) { var circ:EllipseShape = new EllipseShape(); resetCircle( circ ); } stopEffect(); }Use a loop to iterate a block of code that creates a new
EllipseShapeobject, or in this case a bubble, and run theresetCirclemethod on the bubble. We haven’t created this method yet but it re-initializes the parameter bubble. In the loop, the method simply initializes each bubble since they haven’t been initialized to begin with. Finally thestopEffectmethod is called. This method pauses the bubble’s animation sequence. We’ll write this method in a bit.For now, create the following methods. (Also now is a good time to import
com.greensock.TweenMax).private static function randomNumber( min:int = 0, max:int = 10 ):int { return Math.round( Math.random() * ( max - min ) + min ); } private function resetCircle( circle:EllipseShape ):void { circle.width = circle.height = randomNumber( 10, 20 ); effectContainer.addChild( circle ); circle.cacheAsBitmap = true; circle.x = randomNumber( 0, _width ); circle.y = BUTTON_HEIGHT; circle.filters = [ circleBlur ]; if ( circles.indexOf( circle ) == -1 ) { circles.push( circle ); circle.fillColors = [ 0x00BCE9 ]; circle.fillAlphas = [ 1 ]; circle.fillRatios = [ 1 ]; circle.lineAlphas = [ 0 ]; circle.lineRatios = [ 0 ]; circle.lineColors = [ 0 ]; circle.alpha = .6; } var tween:TweenMax = TweenMax.to( circle, randomNumber( 1, 6 ) * .5, { y:-BUTTON_HEIGHT, onComplete:doComplete } ); tweens.push( tween ); function doComplete():void { resetCircle( circle ); removeTween( tween ); } } private function removeTween( tween:TweenMax ):void { var a:Array = []; for each( var t:TweenMax in tweens ) { if ( t != tween ) a.push( t ); } tweens = null; System.gc(); tweens = a; }The
randomNumbermethod returns a random number based on the parameters passed into the method. The first parameter specifies a the minimum value that can be generated and the second parameter specifies the maximum value that can be generated. We need this method to generate a random location and size for a bubble when we reset a bubble.This brings me to the
resetCirclemethod. I said before that this method re-initializes the specified bubble. To be more specific, the method recycles a bubble and makes it appear like a brand new bubble that has just been created when in reality it is just another old bubble.The
tomethod from theTweenMaxclass returns the tween that the bubble contains. We push this bubble into thetweensarray. The bubble starts below the button and rises above the button. When the tween is finished it is no longer needed. The tween needs to be removed (using theremoveTweenmethod) and the bubble needs to be reset or recycled. The anonymous functiondoCompletedoes just that.Before we can see what our bubbles look like we need to create two methods that will control playback of the bubbles’ animation sequence. Besides if we added a call to the
createEffectmethod in the constructor and tested our movie now, the bubbles would be all there but we wouldn’t see them because they would be beneath the button. Remember that each bubble is added to theeffectContainerin theresetCirclemethod and that theeffectContaineris masked so that the bubbles aren’t seen outside of the button.(Plus, if you tested the movie now you’d just get a compile-time error for not implementing the
stopEffectmethod that is called when we create our bubbles.)private function playEffect():void { for each( var tween:TweenMax in tweens ) { tween.play(); } } private function stopEffect():void { for each( var tween:TweenMax in tweens ) { tween.pause(); } }Loop through the
tweensarray to play each tween and to pause each tween when needed. Very simple.Now add a call to the
createEffectmethod and theplayEffectmethod within the constructor. Test your movie. You should now have a beautiful display of bubbles. When you are finished remove theplayEffectmethod from the constructor that you just added but keep thecreateEffectmethod.Step 17: Glowing Text
Let’s add the glowing
TextFieldto the button. We’re using a seperateTextField,effectTxtinstead oftxt, so that we can do a fading transition into the next state. We need an additionalTextFieldfor this. We also need another mask for ourTextFieldjust as we did with the first one.public function MyButton() { super(); buttonMode = true; mouseChildren = false; _width = 100; base = new RectangleShape(); base.height = BUTTON_HEIGHT; base.ellipseHeight = base.ellipseWidth = BUTTON_HEIGHT; base.fillGradientRotation = 90; base.fillColors = [ 0xAAAAAA, 0x444444 ]; base.lineColors = [ 0x222222 ]; base.lineAlphas = [ .5 ]; base.lineRatios = [ 1 ]; ref = new RectangleShape(); ref.fillGradientRotation = 90; ref.ellipseHeight = ref.ellipseWidth = ref.height = BUTTON_HEIGHT / 2; ref.fillColors = [ 0xFFFFFF, 0xFFFFFF ]; ref.fillAlphas = [ 1, 0 ]; ref.lineColors = [ 0 ]; ref.lineAlphas = [ 0 ]; ref.lineRatios = [ 0 ]; ref.alpha = .5; ref.x = ref.ellipseHeight / 2; ref.width = _width - ref.height; light = new RectangleShape(); light.copy( ref ); light.x = ref.x; light.y = BUTTON_HEIGHT - light.height; light.width = _width - light.height; light.fillColors = [ 0xFFFFFF, 0xDDDDDD ]; light.filters = [ new GlowFilter( 0xFFFFFF, .8, 10, 10, 2, 3 ), new BlurFilter( 6, 6, 3 ) ]; light.alpha = lightAlpha = .3; txt = new TextField(); txt.selectable = false; txt.type = "dynamic"; txt.wordWrap = false; txt.multiline = false; txt.autoSize = "left"; txt.defaultTextFormat = new TextFormat( null, 27 ); txt.textColor = 0x333333; txt.text = "label"; txt.height = BUTTON_HEIGHT; txtMask = new RectangleShape(); txtMask.copy( base ); txtMask.filters = []; txt.mask = txtMask; glow = new RectangleShape(); glow.copy( base ); glow.fillColors = [ 0xFFFFFF ]; glow.fillAlphas = [ 1 ]; glow.fillRatios = [ 0 ]; glow.lineColors = [ 0 ]; glow.lineAlphas = [ 0 ]; glow.lineRatios = [ 0 ]; glow.filters = [ new GlowFilter( 0x00BCE9, 1, 10, 10, 2, 3, false, false ), new GlowFilter( 0x00BCE9, 1, 20, 20, 2, 3, true ) ]; effectContainer = new Sprite(); effectContainer.cacheAsBitmap = true; effectMask = new RectangleShape(); effectMask.copy( base ); effectMask.filters = []; effectContainer.mask = effectMask; effectTxt = new TextField(); effectTxt.selectable = false; effectTxt.type = "dynamic"; effectTxt.wordWrap = false; effectTxt.multiline = false; effectTxt.autoSize = "left"; effectTxt.defaultTextFormat = new TextFormat( null, 27 ); effectTxt.textColor = 0x00BCE9; effectTxt.text = "label"; effectTxt.filters = [ new GlowFilter( 0x00BCE9, 1, 16, 16, 2, 3 ) ]; effectTxtMask = new RectangleShape(); effectTxtMask.copy( base ); effectTxtMask.filters = []; effectTxt.mask = effectTxtMask; circleBlur = new BlurFilter( 5, 5, 3 ); circles = []; tweens = []; addChild( base ); addChild( light ); addChild( ref ); addChild( txt ); addChild( txtMask ); addChild( glow ); addChild( effectContainer ); addChild( effectMask ); addChild( effectTxt ); addChild( effectTxtMask ); createEffect(); }Just another
TextFieldwith a glow filter. Again we will position the text fields later when we create theupdatemethod. Now you should have something that looks like this.Now that all of the elements for the over state have been generated, we only have one more task to complete. Next we position the contents of the button.
Step 18: Positioning
Encapsulated in our button class is a function that positions the button’s contents based on the current value of the
_widthproperty. The method I am referring to is theupdatemethod. Call theupdatemethod on the last line of code of the constructor method. Then create theupdatemethod.addChild( effectTxt ); addChild( effectTxtMask ); createEffect(); update(); } protected function update():void { base.width = _width; glow.width = _width; ref.width = _width - ref.height; light.width = _width - light.height; txt.x = ( _width - txt.width ) / 2; txt.y = ( BUTTON_HEIGHT - txt.height ) / 2; effectTxt.x = txt.x; effectTxt.y = txt.y; effectMask.width = _width; txtMask.width = _width; effectTxtMask.width = _width; }Resize particalar objects and reposition others. Test the movie:
We now have centered text. Set the
alphaproperty of all of the effect base objects(effectContainer,effectTxtandglow) to zero. Re-test the movie and now you should see the original state of the button.As you may know, the Tuts+ network is accompanied by an online educational members-only site called Tuts+ Premium. We’re very excited to announce that Tuts+ Premium has received a huge upgrade, including a new library of courses, 27 top-selling educational eBooks, member forums, and a completely redesigned UI. You can check out all the changes at tutsplus.com (I recommend taking the tour) or read on to learn about what you will get from Tuts+ Premium.
Library of Courses
You’ve seen Sessions and series at Activetuts+ and on the other Tuts+ sites; the new Tuts+ Premium introduces courses.
Would you like to learn how to design your first website in 30 days? Or get to grips with the languages of the web, HTML & CSS? Perhaps you’re already familiar with that, and you’re more interested in understanding the essentials of CSS3, or maybe you’d like to study something completely unrelated to web development, like using Photoshop to create concept art. Our new Tuts+ Premium courses will show you how. We’ll be adding new courses several times per month.
45 Exclusive Flash and Browser App Tutorials
Tuts+ Premium includes 45 (and counting) in-depth tutorials on browser app and game development, covering the same kinds of topics you’re used to from Activetuts+, but in much more detail:
…and there’s plenty more, on animation, game engine development, 3D user interfaces, and special effects.
Every tutorial now has a free preview, so you can get a taste of what’s in store before you buy.
My Thoughts
Earlier this year, I got to manage the awesome team that migrated the tutorials from the old Tuts+ Premium system to this new one, so I’m very familiar with both systems and the content. I think it’s fair to say that the old system… had some flaws. It could be difficult to find tutorials unless you were looking for one specifically, and having to download and extract a ZIP file (which in some cases could be over 1GB) just to access the tut was a huge pain.
The new Premium is better in so many ways. You can filter tutorials by topic, by difficulty, and by completion time – and search across the entire library – so it’s much easier to browse for and discover content. Reading tutorials and watching screencasts is much easier too; they’re all accessible in your browser, with an interface similar to the regular Tuts+ sites.
And I must confess: before doing the migration, I didn’t realise how much great content there is on Premium. I don’t just mean the Flash and RIA Premium tutorials – although that selection is excellent, and becoming even better as we add more and more (you should see what we have planned!) – I mean all the content from the other Tuts+ sites that I wasn’t even aware of.
For example, Aetuts+ has some excellent videos about animation, like The 12 Basic Principles of Animation, that are relevant to anyone doing animation, game design, or even flashy UI design. Cgtuts+ has some incredible series on modeling 3D objects, like this next-gen armoured car, which will be useful to anyone creating assets for a Unity project. And if you want to learn JavaScript, Nettuts+ has you covered.
I’ve highlighted tutorials that are related to the topics we cover on Activetuts+, but if you want to branch out and learn something new, there’s plenty of opportunity to do that as well. Illustration, photography, digital audio, mobile app development… oh, and you have to check out Ed Lopez’s amazing digital painting tutorials.
The new extras – forums, ebooks, courses – are all great, but to me, the appeal of Tuts+ Premium lies in its tutorials. Now, at last, those tutorials are easy to find and easy to read (or watch, for screencasts). That alone is worth the $19 a month entry price.
Top-selling Educational eBooks
Tuts+ Premium memberships now include 27 top-selling eBooks worth more than $440, including:
$29Free for Tuts+ Premium members$24Free for Tuts+ Premium members$29Free for Tuts+ Premium members$8Free for Tuts+ Premium members$39Free for Tuts+ Premium members$9Free for Tuts+ Premium membersBeautiful Redesign
The new Tuts+ Premium was designed by Orman Clark and Envato designer Jacob Zinman-Jeanes. And, thank you to Simplifilm, who make a mean product demo video!
Take a Tour of Tuts+ Premium