An Introduction to FlashPunk: The Basics
Learn the basics of how FlashPunk works – an amazing library to save you time and help you create the perfect game!
Final Result Preview
Let’s take a look at the final result we will be working towards:
Use the arrow keys to move your character (the blue guy). The red/brown guy is an NPC; the shaded red area is a danger zone, and the green box is a button. You’ll learn how to create all this in this tutorial.
Step 1: What Is FlashPunk?
FlashPunk is an ActionScript 3 library created for the development of Flash games. Basically, it does all the hard work for you and lets you focus entirely on developing your game, rather than on the engine behind it. The best part about it is that you don’t need Flash Pro to work with it: you can do everything with a free code editor like FlashDevelop. Not to mention it’s way faster when it comes to drawing things on screen, since it uses blitting!
This tutorial will go through all the basics of FlashPunk. After following it, you’ll be ready to make a simple game with this amazing library!
Step 2: Initializing the Engine
Begin by downloading the latest version of FlashPunk from the official site (this tutorial uses the version from August 30, 2011). Put the “net” folder, with all its contents, in your “src” folder.
FlashPunk has a class called Engine. This class is what starts everything in the library. Think of it as a Main class, but with special code to power up all the classes in FlashPunk. In order to use the Engine class, we will modify the Main class a little bit.
package
{
import net.flashpunk.Engine;
[Frame(factoryClass="Preloader")]
public class Main extends Engine
{
public function Main():void
{
}
}
}
Now, our class extends Engine. In Main‘s constructor, we need to make a call to the Engine constructor: this is what sets the important information about the game: width, height, framerate and whether the engine should run at a fixed framerate or not.
public function Main():void
{
super(550, 400, 30, false);
}
There is a function that can (and must be) overridden from the Engine class: the init() function. It will run only once, and will initialize everything to get the game working.
override public function init():void
{
trace("The game has started!");
}
I’m pretty sure everyone wants to put something on the screen and see this engine working! Because of that, the next few steps will cover the very basics of the elements of FlashPunk, adding depth as the tutorial goes on.
Step 3: Worlds and Entities
In FlashPunk, there are elements called Worlds and Entities. These are the main elements of the library, and you’ll work with them from the beginning to the very end of your game.
Worlds are pretty much like what is commonly known as a “screen”. Everything in your game will happen in a world: the main menu is a world that will give you access to the actual game world, where you will fight some enemies and die, which will lead you to the game over world, with your scores and statistics about how well you did. More about worlds will be explained later.
Entities are exactly what they seem to be; they live in a world and do something in it: a button is an entity; your character is an entity; enemies and bullets are entities. They are the things that give life to the game.
Given that, we will create the game world (there’s time to make the main menu world later, let’s jump to some action!) by extending FlashPunk’s World class:
package
{
import net.flashpunk.World;
public class GameWorld extends World
{
public function GameWorld()
{
}
}
}
Now that you have created a world, you need to tell FlashPunk that you want this world to be the active one. Let’s do it in Main.as:
private var _gameWorld:GameWorld;
public function Main():void
{
super(550, 400, 30, false);
_gameWorld = new GameWorld();
}
override public function init():void
{
trace("The game has started!");
FP.world = _gameWorld;
}
And don’t forget to import net.flashpunk.FP!
Step 4: Adding an Entity, and Giving It an Image
Now that we have our world, we can make an entity by extending the Entity class and adding it to our game world:
package
{
import net.flashpunk.Entity;
public class GameEntity extends Entity
{
public function GameEntity()
{
}
}
}
And in GameWorld.as:
private var _gameEntity:GameEntity;
public function GameWorld()
{
_gameEntity = new GameEntity();
add(_gameEntity);
}
Notice that if you compile and run the game, the entity doesn’t appear in the screen. That’s because it has no image yet! Every entity can have a graphic object. This graphic can be a single image, a spritesheet with animations, tiled images — pretty much anything.
We will add this little image to our entity:

An entity’s graphic can be accessed by the graphic property. That’s how we are going to put the image in it! First, embed it; then, just pass it to Image‘s constructor and FlashPunk will take care of transforming that into something visible for you. Compile and run now. Surprise! Our entity is there!
package
{
import net.flashpunk.Entity;
import net.flashpunk.graphics.Image;
public class GameEntity extends Entity
{
[Embed(source = "/../img/EntityImage.png")]
private const IMAGE:Class;
public function GameEntity()
{
graphic = new Image(IMAGE);
}
}
}
This is what you should get:

Step 5: Making the Entity Move
Now that we have our entity on the screen, what about making it move? Each Entity has a function called update(), which you must override to use. This function is called by every world in the beginning of each frame. If you need to make your entity move, that’s the place where you put your code!
override public function update():void
{
x += 10 * FP.elapsed;
y += 5 * FP.elapsed;
}
And don’t forget to import:
import net.flashpunk.FP;
See it in action! (Refresh the page if you can’t see anything here.)
You may have noticed the use of FP.elapsed. FP.elapsed gives the amount of time that elapsed since the last frame (in seconds), making it very easy to create time-based motion. However, for that to work, you must have set the fourth parameter to the Engine‘s constructor to false. Remember that (Step 2)? Setting it to false means that you want FlashPunk to run with a variable timestep, whereas setting it to true makes FlashPunk run on a fixed timestep. Doing the latter, you don’t need to use FP.elapsed. You will know that every time the update() function is called, a frame has passed.
Step 6: Move the Entity as You Wish With Keyboard Input
We’ve got the entity moving on just one direction in the last step. Introducing keyboard input: now you will be able to move the entity to where you want!
FlashPunk has a class called Input which takes care of both keyboard and mouse input. In this tutorial, we will only use keyboard input for movement. It is very easy:
override public function update():void
{
if (Input.check(Key.A) || Input.check(Key.LEFT))
{
x -= 50 * FP.elapsed;
}
else if (Input.check(Key.D) || Input.check(Key.RIGHT))
{
x += 50 * FP.elapsed;
}
if (Input.check(Key.W) || Input.check(Key.UP))
{
y -= 50 * FP.elapsed;
}
else if (Input.check(Key.S) || Input.check(Key.DOWN))
{
y += 50 * FP.elapsed;
}
}
And the import statements:
import net.flashpunk.utils.Input; import net.flashpunk.utils.Key;
Input.check() returns true if the Key passed as an argument is being pressed at the time the function has been called. There are other very useful functions, like Input.pressed(), which returns true if the key has been pressed at the time the function has been called (i.e. the key was up a frame ago and is now down), or Input.released(), which does exactly the opposite.
Another interesting thing that the Input class allows us to do is to define many keys under a single name. For example, we could define Key.UP, Key.W and Key.I as "UP", and only check for Input.check("UP"). That way, we can improve our function:
public function GameEntity()
{
graphic = new Image(IMAGE);
Input.define("UP", Key.W, Key.UP);
Input.define("DOWN", Key.S, Key.DOWN);
Input.define("LEFT", Key.A, Key.LEFT);
Input.define("RIGHT", Key.D, Key.RIGHT);
}
override public function update():void
{
if (Input.check("LEFT"))
{
x -= 50 * FP.elapsed;
}
else if (Input.check("RIGHT"))
{
x += 50 * FP.elapsed;
}
if (Input.check("UP"))
{
y -= 50 * FP.elapsed;
}
else if (Input.check("DOWN"))
{
y += 50 * FP.elapsed;
}
}
And this is what you should get:
Step 7: More About Entities
Entities can do a lot more than just move around and have images. Let’s take a look at what surprises they can hold!
Entities have a property called type. You can set this property to any string you want. This allows you to organize your entities into groups, which will prove very useful in the next step (about worlds). We can, for example, set our entity’s type to “GameEntity”:
public function GameEntity()
{
graphic = new Image(IMAGE);
Input.define("UP", Key.W, Key.UP);
Input.define("DOWN", Key.S, Key.DOWN);
Input.define("LEFT", Key.A, Key.LEFT);
Input.define("RIGHT", Key.D, Key.RIGHT);
type = "GameEntity";
}
Following on that, we have the useful world property and the added() and removed() functions. The world property allows you to access the world from within the entity’s code once the entity has been added to an world. It is like the stage property in common Flash development; the functions are like the ADDED_TO_STAGE and REMOVED_FROM_STAGE event listeners. Here’s an example of the functions working in GameEntity.as:
override public function added():void
{
trace("The entity has been added to the world!");
trace("Entities in the world: " + world.count);
}
override public function removed():void
{
trace("The entity has been removed from the world!");
}
Step 8: Deeper Look at Worlds
It is time to take a deeper look at worlds and how they work. First of all, FlashPunk can only have one world running at once, but your game can have as many worlds as you wish, as long as only one remains active every time.
Worlds have update() functions just as entities do, but their function is a little different: there is actual code in the World class. That means you’ll have to call super.update() every time you override this function.
Apart from entities, worlds can also have graphics added to them. Graphics are images that don’t need to be updated by you (FlashPunk still creates an entity to add them to the world, so the engine will still send a call to an update() function). You can add them by calling addGraphic().
The most important thing about worlds is that they have several functions to retrieve certain entities: getType(), getClass(), getAll(), getLayer() and getInstance(). That way, you can have the world return an array of all the bullets currently in the game, so that you can perform a check against all of them for collision. Very handy, I must say!
Take a look at the code added to World.as. We will use a second image as well:

[Embed(source = "/../img/EntityImage2.png")]
private const IMAGE:Class;
public function GameWorld()
{
_gameEntity = new GameEntity();
add(_gameEntity);
addGraphic(new Image(IMAGE), 0, 50, 50);
}
override public function update():void
{
super.update();
var entityArray:Array = [];
getType("GameEntity", entityArray);
for each (var entity:Entity in entityArray)
{
entity.x = entity.x > 550 ? 550 : entity.x;
entity.y = entity.y > 400 ? 400 : entity.y;
}
}
And don’t forget to import net.flashpunk.graphics.Image!
In this code, the addGraphic() function call adds another graphic similar to _gameEntity‘s graphic – think of it as a NPC! – to the world in the position (50, 50). Lines 23-31 show an example of retrieving only entities of a particular kind: we call getType() to get only entities of the “GameEntity” type (currently only one entity). After that, we iterate through every entity retrieved and prevent them from getting past the right and bottom borders. (So, the entity can move outside the screen, but not far.) Simple, isn’t it?
Step 9: Animations
Time for something more interesting! FlashPunk supports animations of all kinds. All you have to do is, instead of creating an instance of Image, create an instance of Spritemap. This class receives a spritesheet and allows you to map frames and link to animations.
In our entity’s class, embed this spritemap:

Then, create an instance of Spritemap and pass the spritesheet as a parameter to the constructor. After that, it’s all about calling the add() and play() functions!
[Embed(source = "/../img/EntitySheet.png")]
private const SHEET:Class;
private var _timeInterval:Number;
public function GameEntity()
{
graphic = new Spritemap(SHEET, 40, 20, onAnimationEnd);
Spritemap(graphic).add("Stopped", [0]);
Spritemap(graphic).add("Blinking", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 24);
Input.define("UP", Key.W, Key.UP);
Input.define("DOWN", Key.S, Key.DOWN);
Input.define("LEFT", Key.A, Key.LEFT);
Input.define("RIGHT", Key.D, Key.RIGHT);
type = "GameEntity";
Spritemap(graphic).play("Blinking");
}
private function onAnimationEnd():void
{
Spritemap(graphic).play("Stopped");
_timeInterval = 0;
}
override public function update():void
{
_timeInterval += FP.elapsed;
if (_timeInterval >= 3)
{
Spritemap(graphic).play("Blinking");
}
if (Input.check("LEFT"))
{
x -= 50 * FP.elapsed;
}
else if (Input.check("RIGHT"))
{
x += 50 * FP.elapsed;
}
if (Input.check("UP"))
{
y -= 50 * FP.elapsed;
}
else if (Input.check("DOWN"))
{
y += 50 * FP.elapsed;
}
}
The constructor of Spritemap (line 19) takes four arguments: a source to get a graphic from, the width and height of each frame of the spritesheet and a callback function to call when the animation ends (optional). In GameEntity‘s constructor, we create the Spritemap and define two animations: “Stopped”, which only contains the first frame and runs at 0 fps (stopped!) and “Blinking”, which contains all frames and runs at 24 frames per second.
The rest of the code is there to play the “Blinking” animation every three seconds.
Take a look at our entity blinking:
Step 10: Collision
With everything running well, it’s time to introduce another feature: collision detection. FlashPunk has a great collision detection system: all we need to do is set hitboxes for our entities and ask the world to check for collisions. For that, we will create another entity called Box which will contain the following graphic:

package
{
import net.flashpunk.Entity;
import net.flashpunk.graphics.Image;
public class Box extends Entity
{
[Embed(source = "/../img/BoxImage.png")]
private const IMAGE:Class;
public function Box()
{
graphic = new Image(IMAGE);
setHitbox(60, 60);
}
}
}
And inside GameWorld.as:
private var _box:Box;
public function GameWorld()
{
_gameEntity = new GameEntity();
_box = new Box();
add(_gameEntity);
add(_box);
_box.x = 200;
_box.y = 150;
addGraphic(new Image(IMAGE), 0, 50, 50);
}
The setHitbox() function sets a rectangle that will act as a hit box for the entity. The first two parameters are the width and height of the box. The next two parameters (optional) are the origin coordinates (x and y) of the rectangle. Doing the same for GameEntity:
public function GameEntity()
{
graphic = new Spritemap(SHEET, 40, 20, onAnimationEnd);
Spritemap(graphic).add("Stopped", [0]);
Spritemap(graphic).add("Blinking", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 24);
Input.define("UP", Key.W, Key.UP);
Input.define("DOWN", Key.S, Key.DOWN);
Input.define("LEFT", Key.A, Key.LEFT);
Input.define("RIGHT", Key.D, Key.RIGHT);
type = "GameEntity";
Spritemap(graphic).play("Blinking");
setHitbox(40, 20);
}
Now that we have both our entity and the box set up with hitboxes, we need to check for collisions in the world class:
override public function update():void
{
super.update();
var entityArray:Array = [];
getType("GameEntity", entityArray);
for each (var entity:Entity in entityArray)
{
entity.x = entity.x > 550 ? 550 : entity.x;
entity.y = entity.y > 400 ? 400 : entity.y;
}
if (_gameEntity.collideWith(_box, _gameEntity.x, _gameEntity.y))
{
trace("Collision!");
}
}
The collideWith() function checks collision with the entity passed as an argument, virtually placing the first entity (in this case, _gameEntity) in the position specified by the second and third arguments.
Once a collision is detected, there must be a response to it. We will only change the position of the moving entity:
override public function update():void
{
super.update();
var entityArray:Array = [];
getType("GameEntity", entityArray);
for each (var entity:Entity in entityArray)
{
entity.x = entity.x > 550 ? 550 : entity.x;
entity.y = entity.y > 400 ? 400 : entity.y;
}
if (_gameEntity.collideWith(_box, _gameEntity.x, _gameEntity.y))
{
_gameEntity.x = _gameEntity.y = 0;
}
}
Take a look at the example. Try to move the entity into the box.
Step 11: Creating a Simple Button – Adding an Image
FlashPunk doesn’t have any buttons by default. Almost all games need buttons, so in this step we will create a Button class. First of all, a button has three states (as you may know from common Flash development): “Up”, “Over” and “Down”. This spritesheet illustrates that:

And now let’s start the class:
package
{
import net.flashpunk.Entity;
import net.flashpunk.graphics.Spritemap;
public class Button extends Entity
{
protected var _map:Spritemap;
public function Button(x:Number = 0, y:Number = 0)
{
super(x, y);
}
public function setSpritemap(asset:*, frameW:uint, frameH:uint):void
{
_map = new Spritemap(asset, frameW, frameH);
_map.add("Up", [0]);
_map.add("Over", [1]);
_map.add("Down", [2]);
graphic = _map;
setHitbox(frameW, frameH);
}
override public function render():void
{
super.render();
}
}
}
The setSpritemap() function sets a spritemap for the button and sets “animations” for the button. Always the image must have first the “Up” frame, then the “Over”, followed by the “Down” frame. There’s also a call to setHitbox(). The hitbox will be used to check whether the mouse is or isn’t over the button’s box.
Step 12: Creating a Simple Button: Up/Over/Down Controls, Callback
Now that we have our Button successfully showing an image, it’s time to create up, over and down controls. We will do it by creating two Boolean attributes: “over” and “clicked”. We will also detect whether the mouse is over the button’s hit box or not. Add these functions in Button.as:
protected var _over:Boolean;
protected var _clicked:Boolean;
override public function update():void
{
if (!world)
{
return;
}
_over = false;
_clicked = false;
if (collidePoint(x - world.camera.x, y - world.camera.y, Input.mouseX, Input.mouseY))
{
if (Input.mouseDown)
{
mouseDown();
}
else
{
mouseOver();
}
}
}
protected function mouseOver():void
{
_over = true;
}
protected function mouseDown():void
{
_clicked = true;
}
override public function render():void
{
if (_clicked)
{
_map.play("Down");
}
else if (_over)
{
_map.play("Over");
}
else
{
_map.play("Up");
}
super.render();
}
And don’t forget to import net.flashpunk.utils.Input.
Following the logic in update(): first of all, both attributes (_clicked and _over) are set to false. After that, we check if the mouse is over the button. If it isn’t, the attributes will remain false and the button will be in the “Up” state. If the mouse is over, we check whether the mouse button is currently down. If that’s true, the button is in the “Down” state and _clicked is set to true; if it’s false, then the button is in the “Over” state and the _over attribute is set to true. These attributes will define which frame the spritemap should go to.
This button will be useless if you can’t detect when the user has effectively clicked it. Let’s change the class a bit in order to support callback functions:
protected var _callback:Function;
protected var _argument:*;
public function Button(callback:Function, argument:*, x:Number = 0, y:Number = 0)
{
super(x, y);
_callback = callback;
_argument = argument;
}
override public function update():void
{
if (!world)
{
return;
}
_over = false;
_clicked = false;
if (collidePoint(x - world.camera.x, y - world.camera.y, Input.mouseX, Input.mouseY))
{
if (Input.mouseReleased)
{
clicked();
}
else if (Input.mouseDown)
{
mouseDown();
}
else
{
mouseOver();
}
}
}
protected function clicked():void
{
if (!_argument)
{
_callback();
}
else
{
_callback(_argument);
}
}
Our button is done! This code will allow you to pass a callback function (and optionally an argument) to your button, so whenever the user clicks the button, the function will be called.
Step 13: Creating a Simple Button: Adding It to the Screen
Many steps and nothing on the screen… Time to put a button in there! It’s as simple as adding this code in GameWorld.as:
[Embed(source = "/../img/ButtonSheet.png")]
private const BUTTONSHEET:Class;
private var _button:Button;
public function GameWorld()
{
_gameEntity = new GameEntity();
_box = new Box();
_button = new Button(onButtonClick, null);
_button.setSpritemap(BUTTONSHEET, 50, 40);
add(_gameEntity);
add(_box);
add(_button);
_box.x = 200;
_box.y = 150;
_button.x = 400;
_button.y = 200;
addGraphic(new Image(IMAGE), 0, 50, 50);
}
private function onButtonClick():void
{
FP.screen.color = Math.random() * 0xFFFFFF;
trace("The button has been clicked!");
}
Now all you have to do is compile the project and the button will be there!
Step 14: The Console
And now the final feature from FlashPunk that will be presented in this tutorial! The Console is FlashPunk’s tool for debugging: it features logs, which are pretty much like traces; shows the time taken to run important engine step; and displays how many entities are on screen and the current FPS. It’s a great tool to use when developing your game. To enable it, just add the following line to Main.as:
override public function init():void
{
trace("The game has started!");
FP.console.enable();
FP.world = _gameWorld;
}
And to log anything in it, use the FP.log() function. For example, let’s change that trace() call:
override public function init():void
{
FP.console.enable();
FP.log("The game has started!");
FP.world = _gameWorld;
}
That’s pretty much it! You’ll see that the “Output” part from the debugging console now shows the log. You can go ahead and change all the trace() calls in our code to calls to FP.log().
Conclusion
And that’s our introduction to FlashPunk, covering the most important aspects of this amazing library: entities, worlds, images and animations; collision, buttons, input and movement. I hope you’ll like this library as much as I do – it really makes work easier!
View full post on Activetuts+


Learn the basics of how FlashPunk works – an amazing library to save you time and help you create the perfect game!
Final Result Preview
Let’s take a look at the final result we will be working towards:
Use the arrow keys to move your character (the blue guy). The red/brown guy is an NPC; the shaded red area is a danger zone, and the green box is a button. You’ll learn how to create all this in this tutorial.
Step 1: What Is FlashPunk?
FlashPunk is an ActionScript 3 library created for the development of Flash games. Basically, it does all the hard work for you and lets you focus entirely on developing your game, rather than on the engine behind it. The best part about it is that you don’t need Flash Pro to work with it: you can do everything with a free code editor like FlashDevelop. Not to mention it’s way faster when it comes to drawing things on screen, since it uses blitting!
This tutorial will go through all the basics of FlashPunk. After following it, you’ll be ready to make a simple game with this amazing library!
Step 2: Initializing the Engine
Begin by downloading the latest version of FlashPunk from the official site (this tutorial uses the version from August 30, 2011). Put the “net” folder, with all its contents, in your “src” folder.
FlashPunk has a class called
Engine. This class is what starts everything in the library. Think of it as aMainclass, but with special code to power up all the classes in FlashPunk. In order to use theEngineclass, we will modify theMainclass a little bit.package { import net.flashpunk.Engine; [Frame(factoryClass="Preloader")] public class Main extends Engine { public function Main():void { } } }Now, our class extends
Engine. InMain‘s constructor, we need to make a call to theEngineconstructor: this is what sets the important information about the game: width, height, framerate and whether the engine should run at a fixed framerate or not.public function Main():void { super(550, 400, 30, false); }There is a function that can (and must be) overridden from the
Engineclass: theinit()function. It will run only once, and will initialize everything to get the game working.override public function init():void { trace("The game has started!"); }I’m pretty sure everyone wants to put something on the screen and see this engine working! Because of that, the next few steps will cover the very basics of the elements of FlashPunk, adding depth as the tutorial goes on.
Step 3: Worlds and Entities
In FlashPunk, there are elements called
WorldsandEntities. These are the main elements of the library, and you’ll work with them from the beginning to the very end of your game.Worlds are pretty much like what is commonly known as a “screen”. Everything in your game will happen in a world: the main menu is a world that will give you access to the actual game world, where you will fight some enemies and die, which will lead you to the game over world, with your scores and statistics about how well you did. More about worlds will be explained later.
Entities are exactly what they seem to be; they live in a world and do something in it: a button is an entity; your character is an entity; enemies and bullets are entities. They are the things that give life to the game.
Given that, we will create the game world (there’s time to make the main menu world later, let’s jump to some action!) by extending FlashPunk’s
Worldclass:package { import net.flashpunk.World; public class GameWorld extends World { public function GameWorld() { } } }Now that you have created a world, you need to tell FlashPunk that you want this world to be the active one. Let’s do it in
Main.as:private var _gameWorld:GameWorld; public function Main():void { super(550, 400, 30, false); _gameWorld = new GameWorld(); } override public function init():void { trace("The game has started!"); FP.world = _gameWorld; }And don’t forget to import
net.flashpunk.FP!Step 4: Adding an Entity, and Giving It an Image
Now that we have our world, we can make an entity by extending the
Entityclass and adding it to our game world:package { import net.flashpunk.Entity; public class GameEntity extends Entity { public function GameEntity() { } } }And in
GameWorld.as:private var _gameEntity:GameEntity; public function GameWorld() { _gameEntity = new GameEntity(); add(_gameEntity); }Notice that if you compile and run the game, the entity doesn’t appear in the screen. That’s because it has no image yet! Every entity can have a graphic object. This graphic can be a single image, a spritesheet with animations, tiled images — pretty much anything.
We will add this little image to our entity:
An entity’s graphic can be accessed by the
graphicproperty. That’s how we are going to put the image in it! First, embed it; then, just pass it toImage‘s constructor and FlashPunk will take care of transforming that into something visible for you. Compile and run now. Surprise! Our entity is there!package { import net.flashpunk.Entity; import net.flashpunk.graphics.Image; public class GameEntity extends Entity { [Embed(source = "/../img/EntityImage.png")] private const IMAGE:Class; public function GameEntity() { graphic = new Image(IMAGE); } } }This is what you should get:
Step 5: Making the Entity Move
Now that we have our entity on the screen, what about making it move? Each
Entityhas a function calledupdate(), which you must override to use. This function is called by every world in the beginning of each frame. If you need to make your entity move, that’s the place where you put your code!override public function update():void { x += 10 * FP.elapsed; y += 5 * FP.elapsed; }And don’t forget to import:
See it in action! (Refresh the page if you can’t see anything here.)
You may have noticed the use of
FP.elapsed.FP.elapsedgives the amount of time that elapsed since the last frame (in seconds), making it very easy to create time-based motion. However, for that to work, you must have set the fourth parameter to theEngine‘s constructor tofalse. Remember that (Step 2)? Setting it tofalsemeans that you want FlashPunk to run with a variable timestep, whereas setting it totruemakes FlashPunk run on a fixed timestep. Doing the latter, you don’t need to useFP.elapsed. You will know that every time theupdate()function is called, a frame has passed.Step 6: Move the Entity as You Wish With Keyboard Input
We’ve got the entity moving on just one direction in the last step. Introducing keyboard input: now you will be able to move the entity to where you want!
FlashPunk has a class called
Inputwhich takes care of both keyboard and mouse input. In this tutorial, we will only use keyboard input for movement. It is very easy:override public function update():void { if (Input.check(Key.A) || Input.check(Key.LEFT)) { x -= 50 * FP.elapsed; } else if (Input.check(Key.D) || Input.check(Key.RIGHT)) { x += 50 * FP.elapsed; } if (Input.check(Key.W) || Input.check(Key.UP)) { y -= 50 * FP.elapsed; } else if (Input.check(Key.S) || Input.check(Key.DOWN)) { y += 50 * FP.elapsed; } }And the import statements:
Input.check()returnstrueif theKeypassed as an argument is being pressed at the time the function has been called. There are other very useful functions, likeInput.pressed(), which returnstrueif the key has been pressed at the time the function has been called (i.e. the key was up a frame ago and is now down), orInput.released(), which does exactly the opposite.Another interesting thing that the
Inputclass allows us to do is to define many keys under a single name. For example, we could defineKey.UP,Key.WandKey.Ias"UP", and only check forInput.check("UP"). That way, we can improve our function:public function GameEntity() { graphic = new Image(IMAGE); Input.define("UP", Key.W, Key.UP); Input.define("DOWN", Key.S, Key.DOWN); Input.define("LEFT", Key.A, Key.LEFT); Input.define("RIGHT", Key.D, Key.RIGHT); } override public function update():void { if (Input.check("LEFT")) { x -= 50 * FP.elapsed; } else if (Input.check("RIGHT")) { x += 50 * FP.elapsed; } if (Input.check("UP")) { y -= 50 * FP.elapsed; } else if (Input.check("DOWN")) { y += 50 * FP.elapsed; } }And this is what you should get:
Step 7: More About Entities
Entities can do a lot more than just move around and have images. Let’s take a look at what surprises they can hold!
Entities have a property called
type. You can set this property to any string you want. This allows you to organize your entities into groups, which will prove very useful in the next step (about worlds). We can, for example, set our entity’s type to “GameEntity”:public function GameEntity() { graphic = new Image(IMAGE); Input.define("UP", Key.W, Key.UP); Input.define("DOWN", Key.S, Key.DOWN); Input.define("LEFT", Key.A, Key.LEFT); Input.define("RIGHT", Key.D, Key.RIGHT); type = "GameEntity"; }Following on that, we have the useful
worldproperty and theadded()andremoved()functions. Theworldproperty allows you to access the world from within the entity’s code once the entity has been added to an world. It is like thestageproperty in common Flash development; the functions are like theADDED_TO_STAGEandREMOVED_FROM_STAGEevent listeners. Here’s an example of the functions working inGameEntity.as:override public function added():void { trace("The entity has been added to the world!"); trace("Entities in the world: " + world.count); } override public function removed():void { trace("The entity has been removed from the world!"); }Step 8: Deeper Look at Worlds
It is time to take a deeper look at worlds and how they work. First of all, FlashPunk can only have one world running at once, but your game can have as many worlds as you wish, as long as only one remains active every time.
Worlds have
update()functions just as entities do, but their function is a little different: there is actual code in theWorldclass. That means you’ll have to callsuper.update()every time you override this function.Apart from entities, worlds can also have graphics added to them. Graphics are images that don’t need to be updated by you (FlashPunk still creates an entity to add them to the world, so the engine will still send a call to an
update()function). You can add them by callingaddGraphic().The most important thing about worlds is that they have several functions to retrieve certain entities:
getType(),getClass(),getAll(),getLayer()andgetInstance(). That way, you can have the world return an array of all the bullets currently in the game, so that you can perform a check against all of them for collision. Very handy, I must say!Take a look at the code added to
World.as. We will use a second image as well:[Embed(source = "/../img/EntityImage2.png")] private const IMAGE:Class; public function GameWorld() { _gameEntity = new GameEntity(); add(_gameEntity); addGraphic(new Image(IMAGE), 0, 50, 50); } override public function update():void { super.update(); var entityArray:Array = []; getType("GameEntity", entityArray); for each (var entity:Entity in entityArray) { entity.x = entity.x > 550 ? 550 : entity.x; entity.y = entity.y > 400 ? 400 : entity.y; } }And don’t forget to import
net.flashpunk.graphics.Image!In this code, the
addGraphic()function call adds another graphic similar to_gameEntity‘s graphic – think of it as a NPC! – to the world in the position (50, 50). Lines 23-31 show an example of retrieving only entities of a particular kind: we callgetType()to get only entities of the “GameEntity” type (currently only one entity). After that, we iterate through every entity retrieved and prevent them from getting past the right and bottom borders. (So, the entity can move outside the screen, but not far.) Simple, isn’t it?Step 9: Animations
Time for something more interesting! FlashPunk supports animations of all kinds. All you have to do is, instead of creating an instance of
Image, create an instance ofSpritemap. This class receives a spritesheet and allows you to map frames and link to animations.In our entity’s class, embed this spritemap:
Then, create an instance of
Spritemapand pass the spritesheet as a parameter to the constructor. After that, it’s all about calling theadd()andplay()functions![Embed(source = "/../img/EntitySheet.png")] private const SHEET:Class; private var _timeInterval:Number; public function GameEntity() { graphic = new Spritemap(SHEET, 40, 20, onAnimationEnd); Spritemap(graphic).add("Stopped", [0]); Spritemap(graphic).add("Blinking", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 24); Input.define("UP", Key.W, Key.UP); Input.define("DOWN", Key.S, Key.DOWN); Input.define("LEFT", Key.A, Key.LEFT); Input.define("RIGHT", Key.D, Key.RIGHT); type = "GameEntity"; Spritemap(graphic).play("Blinking"); } private function onAnimationEnd():void { Spritemap(graphic).play("Stopped"); _timeInterval = 0; } override public function update():void { _timeInterval += FP.elapsed; if (_timeInterval >= 3) { Spritemap(graphic).play("Blinking"); } if (Input.check("LEFT")) { x -= 50 * FP.elapsed; } else if (Input.check("RIGHT")) { x += 50 * FP.elapsed; } if (Input.check("UP")) { y -= 50 * FP.elapsed; } else if (Input.check("DOWN")) { y += 50 * FP.elapsed; } }The constructor of
Spritemap(line 19) takes four arguments: a source to get a graphic from, the width and height of each frame of the spritesheet and a callback function to call when the animation ends (optional). InGameEntity‘s constructor, we create theSpritemapand define two animations: “Stopped”, which only contains the first frame and runs at 0 fps (stopped!) and “Blinking”, which contains all frames and runs at 24 frames per second.The rest of the code is there to play the “Blinking” animation every three seconds.
Take a look at our entity blinking:
Step 10: Collision
With everything running well, it’s time to introduce another feature: collision detection. FlashPunk has a great collision detection system: all we need to do is set hitboxes for our entities and ask the world to check for collisions. For that, we will create another entity called
Boxwhich will contain the following graphic:package { import net.flashpunk.Entity; import net.flashpunk.graphics.Image; public class Box extends Entity { [Embed(source = "/../img/BoxImage.png")] private const IMAGE:Class; public function Box() { graphic = new Image(IMAGE); setHitbox(60, 60); } } }And inside
GameWorld.as:private var _box:Box; public function GameWorld() { _gameEntity = new GameEntity(); _box = new Box(); add(_gameEntity); add(_box); _box.x = 200; _box.y = 150; addGraphic(new Image(IMAGE), 0, 50, 50); }The
setHitbox()function sets a rectangle that will act as a hit box for the entity. The first two parameters are the width and height of the box. The next two parameters (optional) are the origin coordinates (x and y) of the rectangle. Doing the same forGameEntity:public function GameEntity() { graphic = new Spritemap(SHEET, 40, 20, onAnimationEnd); Spritemap(graphic).add("Stopped", [0]); Spritemap(graphic).add("Blinking", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 24); Input.define("UP", Key.W, Key.UP); Input.define("DOWN", Key.S, Key.DOWN); Input.define("LEFT", Key.A, Key.LEFT); Input.define("RIGHT", Key.D, Key.RIGHT); type = "GameEntity"; Spritemap(graphic).play("Blinking"); setHitbox(40, 20); }Now that we have both our entity and the box set up with hitboxes, we need to check for collisions in the world class:
override public function update():void { super.update(); var entityArray:Array = []; getType("GameEntity", entityArray); for each (var entity:Entity in entityArray) { entity.x = entity.x > 550 ? 550 : entity.x; entity.y = entity.y > 400 ? 400 : entity.y; } if (_gameEntity.collideWith(_box, _gameEntity.x, _gameEntity.y)) { trace("Collision!"); } }The
collideWith()function checks collision with the entity passed as an argument, virtually placing the first entity (in this case,_gameEntity) in the position specified by the second and third arguments.Once a collision is detected, there must be a response to it. We will only change the position of the moving entity:
override public function update():void { super.update(); var entityArray:Array = []; getType("GameEntity", entityArray); for each (var entity:Entity in entityArray) { entity.x = entity.x > 550 ? 550 : entity.x; entity.y = entity.y > 400 ? 400 : entity.y; } if (_gameEntity.collideWith(_box, _gameEntity.x, _gameEntity.y)) { _gameEntity.x = _gameEntity.y = 0; } }Take a look at the example. Try to move the entity into the box.
Step 11: Creating a Simple Button – Adding an Image
FlashPunk doesn’t have any buttons by default. Almost all games need buttons, so in this step we will create a
Buttonclass. First of all, a button has three states (as you may know from common Flash development): “Up”, “Over” and “Down”. This spritesheet illustrates that:And now let’s start the class:
package { import net.flashpunk.Entity; import net.flashpunk.graphics.Spritemap; public class Button extends Entity { protected var _map:Spritemap; public function Button(x:Number = 0, y:Number = 0) { super(x, y); } public function setSpritemap(asset:*, frameW:uint, frameH:uint):void { _map = new Spritemap(asset, frameW, frameH); _map.add("Up", [0]); _map.add("Over", [1]); _map.add("Down", [2]); graphic = _map; setHitbox(frameW, frameH); } override public function render():void { super.render(); } } }The
setSpritemap()function sets a spritemap for the button and sets “animations” for the button. Always the image must have first the “Up” frame, then the “Over”, followed by the “Down” frame. There’s also a call tosetHitbox(). The hitbox will be used to check whether the mouse is or isn’t over the button’s box.Step 12: Creating a Simple Button: Up/Over/Down Controls, Callback
Now that we have our Button successfully showing an image, it’s time to create up, over and down controls. We will do it by creating two Boolean attributes: “over” and “clicked”. We will also detect whether the mouse is over the button’s hit box or not. Add these functions in
Button.as:protected var _over:Boolean; protected var _clicked:Boolean; override public function update():void { if (!world) { return; } _over = false; _clicked = false; if (collidePoint(x - world.camera.x, y - world.camera.y, Input.mouseX, Input.mouseY)) { if (Input.mouseDown) { mouseDown(); } else { mouseOver(); } } } protected function mouseOver():void { _over = true; } protected function mouseDown():void { _clicked = true; } override public function render():void { if (_clicked) { _map.play("Down"); } else if (_over) { _map.play("Over"); } else { _map.play("Up"); } super.render(); }And don’t forget to import
net.flashpunk.utils.Input.Following the logic in
update(): first of all, both attributes (_clickedand_over) are set to false. After that, we check if the mouse is over the button. If it isn’t, the attributes will remain false and the button will be in the “Up” state. If the mouse is over, we check whether the mouse button is currently down. If that’s true, the button is in the “Down” state and_clickedis set to true; if it’s false, then the button is in the “Over” state and the_overattribute is set to true. These attributes will define which frame the spritemap should go to.This button will be useless if you can’t detect when the user has effectively clicked it. Let’s change the class a bit in order to support callback functions:
protected var _callback:Function; protected var _argument:*; public function Button(callback:Function, argument:*, x:Number = 0, y:Number = 0) { super(x, y); _callback = callback; _argument = argument; } override public function update():void { if (!world) { return; } _over = false; _clicked = false; if (collidePoint(x - world.camera.x, y - world.camera.y, Input.mouseX, Input.mouseY)) { if (Input.mouseReleased) { clicked(); } else if (Input.mouseDown) { mouseDown(); } else { mouseOver(); } } } protected function clicked():void { if (!_argument) { _callback(); } else { _callback(_argument); } }Our button is done! This code will allow you to pass a callback function (and optionally an argument) to your button, so whenever the user clicks the button, the function will be called.
Step 13: Creating a Simple Button: Adding It to the Screen
Many steps and nothing on the screen… Time to put a button in there! It’s as simple as adding this code in
GameWorld.as:[Embed(source = "/../img/ButtonSheet.png")] private const BUTTONSHEET:Class; private var _button:Button; public function GameWorld() { _gameEntity = new GameEntity(); _box = new Box(); _button = new Button(onButtonClick, null); _button.setSpritemap(BUTTONSHEET, 50, 40); add(_gameEntity); add(_box); add(_button); _box.x = 200; _box.y = 150; _button.x = 400; _button.y = 200; addGraphic(new Image(IMAGE), 0, 50, 50); } private function onButtonClick():void { FP.screen.color = Math.random() * 0xFFFFFF; trace("The button has been clicked!"); }Now all you have to do is compile the project and the button will be there!
Step 14: The Console
And now the final feature from FlashPunk that will be presented in this tutorial! The
Consoleis FlashPunk’s tool for debugging: it features logs, which are pretty much like traces; shows the time taken to run important engine step; and displays how many entities are on screen and the current FPS. It’s a great tool to use when developing your game. To enable it, just add the following line toMain.as:override public function init():void { trace("The game has started!"); FP.console.enable(); FP.world = _gameWorld; }And to log anything in it, use the
FP.log()function. For example, let’s change thattrace()call:override public function init():void { FP.console.enable(); FP.log("The game has started!"); FP.world = _gameWorld; }That’s pretty much it! You’ll see that the “Output” part from the debugging console now shows the log. You can go ahead and change all the
trace()calls in our code to calls toFP.log().Conclusion
And that’s our introduction to FlashPunk, covering the most important aspects of this amazing library: entities, worlds, images and animations; collision, buttons, input and movement. I hope you’ll like this library as much as I do – it really makes work easier!
Today we’ll build a fully functional calendar widget using AS3. It’s not rocket science, just an excellent example of using the
Dateclass, which can handle all the complexity of extracting times, dates, months and years. We are also going to use some Flash components, and make sure that this calendar is portable to Flash Builder, FlashDevelop, and so on.Final Result Preview
Let’s take a look at the final result we will be working towards:
Step 1: Brainstorming
Before starting to build the code, let’s take a look at what we’ll need:
Step 2: Preparing the Calendar.as Class File
In this step we shall create a basic structure of our Calendar.as class file.
There are several ways of creating ActionScript class file, like using FDT, Flash Builder, FlashDevelop. And, of course, the “Higgs Boson” of this multimedia world: the one true Flash IDE. I am using Flash Professional. Create an ActionScript 3.0 class file. The following is the basic structure of Calendar.as to start with.
package { import flash.display.Sprite; public class Calendar extends Sprite { //variables public function Calendar () { // constructor code } } }I am sure that you have saved this class file as Calendar.as in the new folder for this calendar app. If not please save it.
Step by step we shall modify this Calendar.as to make it fully functional.
In the next step we will create a few text fields to label the weekdays and dates.
Step 3: Setting Up Text Formats for Dates and Weekdays
In this step we will modify our class by declaring some variables and creating a new function
setTextFormat( fontFace, fontSize ).We will also passing some parameters to the constructor of the class.
Initially it will look like “Syntactic Sugar”, but as the tutorial progresses it will become “Syntactic Salt”. Sorry, but we want to keep this class compact so as to make it easily portable. The idea is to have only one class and that’s all.
So, keep a close watch on the constructor’s number of parameters and their order as we progress.
Back to modifying the document class…
package { import flash.display.Sprite; import flash.text.TextFormat; public class Calendar extends Sprite { //variables private var dateCellFormat:TextFormat; private var dayLabelTxtFmt:TextFormat; public function Calendar( fontFace:String = "Arial", fontSize:int = 15 ) { setTextFormat( fontFace, fontSize ); } private function setTextFormat(whichFont:String, size:int):void { //date text format dateCellFormat = new TextFormat(); dateCellFormat.font = whichFont; dateCellFormat.color = 0xFFFFFF; dateCellFormat.size = size; dateCellFormat.align = "center"; //day label text format dayLabelTxtFmt = new TextFormat(); dayLabelTxtFmt.font = "_sans"; dayLabelTxtFmt.color = 0x000000; dayLabelTxtFmt.size = size - 3; } } }Observe all the parameters passed to the constructor: we have already assigned values to them. This is the way to create default values for parameters. If no parameters are specified when instantiating the class, these default values will be used instead. We will see this in coming steps.
Nothing visible yet. Wait for a few steps.
Step 4: Creating Cell Grid to Arrange Dates
The calendar is made up of rows and columns forming a grid.
But how many rows and columns?
It depends on the calendar type – Vertical or Horizontal.
Vertical view needs 7 (rows) x 6 (columns), while horizontal view needs 6 (rows) x 7 (columns) as shown:
As our calendar is limited to horizontal view for this tutorial, we will need 7 columns and 6 rows. Thus total (7 x 6) = 42 cells.
So, let us modify our class so that it will generate a grid required for calendar view. Along with adding an import statement, new variables and constructor parameters, we will also add a new function
makeDatesCellGrid(..)to the class as shown:package { import flash.display.Sprite; import flash.text.TextFormat; import flash.text.TextField; public class Calendar extends Sprite { //variables private var cellW:Number; //cell width private var cellP:Number; //cell padding private var allDatesCells:Array = new Array(); private var dateCellFormat:TextFormat; private var dayLabelTxtFmt:TextFormat; public function Calendar( fontFace:String = "Arial", fontSize:int = 15, cellWidth:Number = 30, padding:Number = 3, originX:Number = 15, originY:Number = 15 ) { cellW = cellWidth; cellP = padding; setTextFormat( fontFace, fontSize ); makeDatesCellGrid( originX, originY ); } private function setTextFormat(whichFont:String, size:int):void { //date text format dateCellFormat = new TextFormat(); dateCellFormat.font = whichFont; dateCellFormat.color = 0xFFFFFF; dateCellFormat.size = size; dateCellFormat.align = "center"; //day label text format dayLabelTxtFmt = new TextFormat(); dayLabelTxtFmt.font = "_sans"; dayLabelTxtFmt.color = 0x000000; dayLabelTxtFmt.size = size - 3; } private function makeDatesCellGrid(cellXPos:Number, cellYPos:Number):void { //Create grid of date cells for (var i:int = 0; i < 42; i++) { var dateCell:TextField = new TextField(); addChild(dateCell); //position cells to form a grid (7 x 6 = 42) dateCell.x = cellXPos + (cellW * (i-(Math.floor(i/7)*7))); dateCell.y = cellYPos + (cellW * Math.floor(i/7)); //put all date cells into array for further access allDatesCells.push(dateCell); } } } }Oops, lots of code is written and nothing visible yet. I know you are waiting to see some visible results. For that we will make a temporary change in
makeDatesCellGridby adding a new line as shown:private function makeDatesCellGrid(cellXPos:Number, cellYPos:Number):void { //Create grid of date cells for (var i:int = 0; i < 42; i++) { var dateCell:TextField = new TextField(); addChild(dateCell); //position cells to form a grid (7 x 6 = 42) dateCell.x = cellXPos + (cellW * (i-(Math.floor(i/7)*7))); dateCell.y = cellYPos + (cellW * Math.floor(i/7)); //put all date cells into array for further access allDatesCells.push(dateCell); allDatesCells[i].text = i; } }Wait…Wait. Now create a new Flash file for ActionScript 3.0 and save this file with any name (ideally Calendar.fla as I did) in the same folder where you saved Calendar.as. Now open the ActionScript panel by pressing “F9″ and type the following code:
Now test the movie.
There are a few things to fix, like setting up the dates for current month, labels for days, etc.
Also note that though the constructor can take parameters, we saw a result without passing any values for parameters. This is what I was talking about in the previous step.
Observe the following two lines we placed inside the constructor,
We created these vars for global access. We have assigned values to these variables from constructor parameters.
We used “cellW” for placing cells in grid form. We will use “cellP”, i.e. cell padding (the gap between neighbouring cells), in coming steps. So wait and watch.
One more thing. After testing above code, remove the line
allDatesCells[i].text = i;from themakeDatesCellGrid(..)function. We added it for testing purposes only. Actually, we are going to put dates in those text fields in coming steps.Step 5: Adding Names of Weekdays
In this step we will add labels for weekdays to our calendar. For that we are going to add a new function
makeDaysLabels(...)and call it inside the constructor.First of all, add new array
weekDays[]at the start of the class under the//Variablesdeclaration section. This array hold names of all weekdays as shown:private var weekDays:Array = new Array("Sun","Mon","Tue","Wed","Thu","Fri","Sat");Now add the following function to the class.
private function makeDaysLabels(cellXPos:Number, cellYPos:Number):void { //Add week day names for (var i:int = 0; i < 7; i++) { var dayLabel:TextField = new TextField(); addChild(dayLabel); dayLabel.selectable = false; dayLabel.text = weekDays[i]; dayLabel.setTextFormat(dayLabelTxtFmt); dayLabel.x = cellXPos + (cellW * i); dayLabel.y = cellYPos - 15; } }Finally, call this function inside constructor as shown:
public function Calendar( fontFace:String = "Arial", fontSize:int = 15, cellWidth:Number = 30, padding:Number = 3, originX:Number = 15, originY:Number = 15 ) { cellW = cellWidth; cellP = padding; setTextFormat( fontFace, fontSize ); makeDatesCellGrid( originX, originY ); makeDaysLabels( originX, originY ); }Weekdays are ready and waiting for dates to have their right places.
Step 6: Initializing and Decorating Dates’ Cells
In this step we will make textfield background visible so that there’s no need to draw a rectangle shape. But if you want to add more style then after completing this tutorial go ahead and create rectangle instances as dates’ cells background and add a gradient color to them. For now it’s ok to set the textfield’s background to
trueand apply a solid color to it.Add the following new function to our class.
private function monthSetup():void { for (var i:int = 0; i < 42; i++){ allDatesCells[i].text = ""; //decor all cells allDatesCells[i].background = true; allDatesCells[i].backgroundColor = 0x000000; allDatesCells[i].border = true; allDatesCells[i].borderColor = 0xCCCCCC; allDatesCells[i].selectable = false; allDatesCells[i].width = allDatesCells[i].height = cellW - cellP; allDatesCells[i].setTextFormat(dateCellFormat); } }Then, call this function inside constructor as shown:
public function Calendar( fontFace:String = "Arial", fontSize:int = 15, cellWidth:Number = 30, padding:Number = 3, originX:Number = 15, originY:Number = 15 ) { cellW = cellWidth; cellP = padding; setTextFormat( fontFace, fontSize ); makeDatesCellGrid( originX, originY ); makeDaysLabels( originX, originY ); monthSetup(); }Test the movie.
Cool… The cells are ready to hold dates for the selected month.
Step 7: Arrange Dates for Selected Month
We will add a new function
arrangeDates(). This is the most important function for this Calendar app.We will be completing the following must-do tasks through this function:
Dateclass.Pretty challenging tasks, but interesting.
Ok, back to the job. First we will declare some variables at the start of the class under the
//Variablessection as shown:Here
currDateTimegives the current time, today’s date, and the current month and year.firstDaygives all info about the first day (i.e. Date 1) of the current month. Observe that we have passed the value for third parameter as “1″ for date. Try tracing with various dates to get an idea of what this is doing. But remember to make it “1″ again after testing.firstDayColumn, as the name suggests, gives the column number for the first day of current month,daysOfMonthsarray holds maximum number of days from “January…” to “…December”. But for “February” we have “28″ days; in case of leap year we must assign “29″ days as the maximum number of days for February. We will see it later in this step.maxDayswill hold maximum number of days for current month.Fine. Good enough explanation for each var. It’s time for new function
arrangeDates().In order to understand this function neatly, we will modify it gradually.
So first, In this function we will get the column number for first day (i.e. Date 1) of a selected month as shown:
private function arrangeDates():void { //get column number for first day of the month if (firstDay.day == 0) { //when last date of previous month is on saturday then move to second row firstDayColumn = firstDay.day + 7; } else { firstDayColumn = firstDay.day; } }This
"firstDayColumn"is important since once we get column number for first day (i.e Date 1) then placing next dates becomes very easy. We will simply use this"firstDayColumn"number inside aforloop to place the next dates. We will see it soon in coming steps.Step 8: Getting the Maximum Number of Days for Selected Month
We will modify the
"arrangeDates()"function to get the maximum days for selected month as shown:private function arrangeDates():void { //get column number for first day of the month if (firstDay.day == 0) { //when last date of previous month is on saturday then move to second row firstDayColumn = firstDay.day + 7; } else { firstDayColumn = firstDay.day; } //get max days for current month w.r.t leap year if any maxDays = (firstDay.getFullYear()%4 == 0 && firstDay.getMonth() == 1 ? 29 : daysOfMonths[firstDay.getMonth()]); }The above new line is a shorthand method for an
ifstatement. New users may consider it “Syntactic Salt”, but once used to it will consider it “Syntactic Sugar”.Observe the question mark “?” in the above line. On the left side of this question mark – i.e.
"firstDay.getFullYear()%4 == 0 && firstDay.getMonth() == 1"– is a conditional statement. If this left side istruethen the statement after the “?” mark (here29) is executed; if it isfalsethe statment on the right of the “:” mark (heredaysOfMonths[firstDay.getMonth()]) is executed. See the image below for a better understanding.Ok, back to our shorthand if statement. It gives the maximum number of days for selected month. But you have to consider leap years, too. In a leap year, February has 29 maximum days. This leap year comes every fourth year.
So we have two conditions, one is to check if it is leap year and second is to check if it is February.
To check whether current year is leap year we used modulus operator (which gives the remainder after dividing one number by another), as in
"firstDay.getFullYear() % 4". If it gives a remainder of 0 (zero) then we can say that it is a leap year. For example, 2012 is a leap year, as(2012 % 4 == 0). Similarly(2016 % 4 == 0), so 2016 is a leap year, and so on. If the remainder is nonzero then obviously it is not a leap year. Thus we can detect leap years.(Editor’s note: actually, there’s a little more to it than that: years divisible by 100 are usually not leap years – unless they are also divisible by 400. To keep this tutorial simple, we’ll omit that, but you can program it in if you wish!)
Now we will check whether the current month is “February”. Before that let us consider the index number for months generated by Date class. It starts from 0 for Jan, 1 for Feb, 2 for March and so on. So to find out whether the current month is “February”, we use
"firstDay.getMonth() == 1". “1″ for “February”.If both these conditions are true then we set
maxDays = 29; otherwise we refer to the"daysOfMonths"array.Step 9: Placing Dates of Selected Month at Their Positions
Now we have
"maxDays"and"firstDayColumn"with us so we are able to arrange dates for selected month.Modify
"arrangeDates()"as shown below,private function arrangeDates():void { //get column number for first day of the month if (firstDay.day == 0) { //when last date of previous month is on saturday then move to second row firstDayColumn = firstDay.day + 7; } else { firstDayColumn = firstDay.day; } //get max days for current month w.r.t leap year if any maxDays = (firstDay.getFullYear()%4 == 0 && firstDay.getMonth() == 1 ? 29 : daysOfMonths[firstDay.getMonth()]); //put dates for current month for (var i:int = 0; i < maxDays; i++) { allDatesCells[firstDayColumn + i].text = i + 1; allDatesCells[firstDayColumn + i].setTextFormat(dateCellFormat); allDatesCells[firstDayColumn + i].alpha = 1; } }We set
"allDatesCells[firstDayColumn + i].alpha = 1"for date field as we are going to set previous and next month’s date fields’ alpha to0.5in coming steps. So we setalpha = 1in advance to avoid returning to this function, since this function is almost finished.Now
"arrangeDates()"function has something to show. But we need to call it somewhere. The function"monthSetup()"is the right place to call this function.So modify function
"monthSetup()"as shown:private function monthSetup():void { for (var i:int = 0; i < 42; i++){ allDatesCells[i].text = ""; //decor all cells allDatesCells[i].background = true; allDatesCells[i].backgroundColor = 0x000000; allDatesCells[i].border = true; allDatesCells[i].borderColor = 0xCCCCCC; allDatesCells[i].selectable = false; allDatesCells[i].width = allDatesCells[i].height = cellW - cellP; allDatesCells[i].setTextFormat(dateCellFormat); } arrangeDates(); }At this point you can test the movie.
Wow. exciting. See those dates at their positions.
Step 10: Highlighting Today’s Date
In this step we will highllight today’s date. So modify the
"for"loop in"arrangeDates()"as shown:private function arrangeDates():void { //get column number for first day of the month if (firstDay.day == 0) { //when last date of previous month is on saturday then move to second row firstDayColumn = firstDay.day + 7; } else { firstDayColumn = firstDay.day; } //get max days for current month w.r.t leap year if any maxDays = (firstDay.getFullYear()%4 == 0 && firstDay.getMonth() == 1 ? 29 : daysOfMonths[firstDay.getMonth()]); //put dates for current month for (var i:int = 0; i < maxDays; i++) { allDatesCells[firstDayColumn + i].text = i + 1; allDatesCells[firstDayColumn + i].setTextFormat(dateCellFormat); allDatesCells[firstDayColumn + i].alpha = 1; //Highlight today if (firstDay.fullYear == currDateTime.fullYear && firstDay.month == currDateTime.month) { if(allDatesCells[firstDayColumn + i].text == currDateTime.date) { allDatesCells[firstDayColumn + i].backgroundColor = 0xEE5D15; } } } }Test movie to see today’s date highlighted.
We are very close to getting that “Calendar” feel. Once the dates of previous month and next month are placed, then we are done – for the current month, at least.
Step 11: Adding Dates of the Previous Month
We want to add dates from before the “firstDayColumn”, so we will get all previous date fields in reverse order by using a decrementing
"for"loop.Add the following new function to the class:
private function prevMonthDates():void { var prevMonthFirstDay:Date = new Date(firstDay.fullYear,firstDay.month,firstDay.date - 1); for (var i:int = firstDayColumn-1; i >= 0; i--) { allDatesCells[i].text = prevMonthFirstDay.date - ((firstDayColumn - 1) - i); allDatesCells[i].setTextFormat(dateCellFormat); allDatesCells[i].alpha = 0.5; } }Now call this function inside
"monthSetup()"as shown:private function monthSetup():void { for (var i:int = 0; i < 42; i++){ allDatesCells[i].text = ""; //decor all cells allDatesCells[i].background = true; allDatesCells[i].backgroundColor = 0x000000; allDatesCells[i].border = true; allDatesCells[i].borderColor = 0xCCCCCC; allDatesCells[i].selectable = false; allDatesCells[i].width = allDatesCells[i].height = cellW - cellP; allDatesCells[i].setTextFormat(dateCellFormat); } arrangeDates(); prevMonthDates(); }Test the movie to see the previous month’s dates placed at their positions.
Step 12: Adding Dates of the Next Month
In this step we will add dates of next month. Add the following new function to the class as shown:
private function nextMonthDates():void { for (var i:int = 1; i < (42 - maxDays - (firstDayColumn - 1)); i++){ allDatesCells[(firstDayColumn - 1) + i + maxDays].text = i; allDatesCells[(firstDayColumn - 1) + i + maxDays].setTextFormat(dateCellFormat); allDatesCells[(firstDayColumn - 1) + i + maxDays].alpha = 0.5; } }Now call this function inside
"monthSetup()"function as shown:private function monthSetup():void { for (var i:int = 0; i < 42; i++){ allDatesCells[i].text = ""; //decor all cells allDatesCells[i].background = true; allDatesCells[i].backgroundColor = 0x000000; allDatesCells[i].border = true; allDatesCells[i].borderColor = 0xCCCCCC; allDatesCells[i].selectable = false; allDatesCells[i].width = allDatesCells[i].height = cellW - cellP; allDatesCells[i].setTextFormat(dateCellFormat); } arrangeDates(); prevMonthDates(); nextMonthDates(); }Test the movie to see dates of next month placed at their positions.
At this point our calendar is ready, displaying all the dates required in the current month’s display. Now in the coming steps we will allow the user to select any month and any year, to complete the Calendar application.
Step 13: Adding the Capability to Select Any Month
Selecting any month will add more sense to our Calendar. To select any month from the list, the
"ComboBox"component is the best option available.Therefore we need to add a ComboBox which holds a list of months. It should be placed at an appropriate position, somewhere near the calendar. So we will allow placing this ComboBox as per the developer’s choice. How will we allow the developer to change this? We will pass parameter for the X and Y positions of this ComboBox to the constructor of Calendar.as.
To add and place the ComboBox, first consider the following tasks we must perform:
"X"and"Y"positions to the constructor."monthPicker(...)"where we will manipulate the ComboBox."pickMonth()"which will hear the ComboBox’s events and will call a function to show the selected month.First, add the following import statements to the class,
Then add new variables at start of the class under
//Variablessection as shown:private var months:Array = [ {label:"January", data:0}, {label:"February", data:1}, {label:"March", data:2}, {label:"April", data:3}, {label:"May", data:4}, {label:"June", data:5}, {label:"July", data:6}, {label:"August", data:7}, {label:"September", data:8}, {label:"October", data:9}, {label:"November", data:10}, {label:"December", data:11}, ]; private var monthPickerCB:ComboBox; //combobox to pick a monthWe will use the
"months"array to put values inside combobox.Ok, now let us modify the constructor function as shown:
public function Calendar( fontFace:String = "Arial", fontSize:int = 15, cellWidth:Number = 30, padding:Number = 3, originX:Number = 15, originY:Number = 15, cbX:Number = 15, cbY:Number = 15 ) { cellW = cellWidth; cellP = padding; monthPickerCB = new ComboBox(); setTextFormat( fontFace, fontSize ); makeDatesCellGrid( originX, originY ); makeDaysLabels( originX, originY ); monthSetup(); monthPicker( cbX, cbY ); }We added the parameters
"cbX"and"cbY"into the constructor’s signature to pass position values specified by user, or to use default values if not specified.Then we created a ComboBox instance as
"monthPickerCB = new ComboBox()".We called
"monthPicker(...)"in advance, as we are going to create it now.So we will add two new functions
"monthPicker(...)"and"pickMonth(...)"as shown:private function monthPicker(cbX:Number, cbY:Number):void { monthPickerCB.dataProvider = new DataProvider(months); addChild(monthPickerCB); //position combobox monthPickerCB.x = cbX; monthPickerCB.y = (cellW * 6) + cbY; monthPickerCB.selectedIndex = currDateTime.month; monthPickerCB.addEventListener(Event.CHANGE, pickMonth); } private function pickMonth(e:Event):void { firstDay.month = ComboBox(e.target).selectedItem.data; monthSetup(); }One more step and we are done adding the ComboBox.
Drag the ComboBox from the Component panel into your Flash file’s Library panel. (Use the Window menu if you can’t find the panels.) This way, we can access the ComboBox component and its classes at runtime.
Test the movie to see combobox at bottom left of the calendar (as default value).
Now you can display all the months, for the current year.
Exciting… So we will now add year selection, giving one more dimension to our calendar.
Step 14: Adding the Capability to Select Any Year
Selecting any year will add one more dimension to our Calendar. To select any year within a range, the
NumericSteppercomponent is a good choice.Therefore we will add
"NumericStepper"component which will allow the user to increment or decrement the selected year. As with the ComboBox, we will pass parameters to the constructor to specify its X and Y positions.To add and place a
NumericStepper, we’ll have to perform the following tasks:"X"and"Y"positions to the constructor."yearPicker(...)"where we will manipulate NumericStepper."pickYear()"which will hear NumericStepper events and will tell to show the selected year.NumericSteppercomponent by dragging it from the Component panel to the Library panel of our FLA.First, add the following import statement to the class,
Then add a new variable at the start of the class under the
//Variablessection as shown:Ok, now let us modify constructor function as shown:
public function Calendar( fontFace:String = "Arial", fontSize:int = 15, cellWidth:Number = 30, padding:Number = 3, originX:Number = 15, originY:Number = 15, cbX:Number = 15, cbY:Number = 15, nsX:Number = 26, nsY:Number = 15, monthsRange:int = 39 ) { cellW = cellWidth; cellP = padding; monthPickerCB = new ComboBox(); yearPickerNS = new NumericStepper(); setTextFormat( fontFace, fontSize ); makeDatesCellGrid( originX, originY ); makeDaysLabels( originX, originY ); monthSetup(); monthPicker( cbX, cbY ); yearPicker( nsX, nsY, monthsRange ); }We have added three parameters:
"nsX","nsY"and"monthsRange". The first two are to specify the position, and the third one is to specify the range (previous and next months). 39 will allow to pick any year within the next and previous 40.OK, now we will add two new functions
"yearPicker(...)"and"pickYear(...)", as shown:private function yearPicker(nsX:Number, nsY:Number, maxYrsRange:int):void { yearPickerNS.maximum = currDateTime.fullYear + maxYrsRange; yearPickerNS.minimum = currDateTime.fullYear - maxYrsRange; yearPickerNS.value = currDateTime.fullYear; addChild(yearPickerNS); //position numeric stepper yearPickerNS.x = monthPickerCB.width + nsX; yearPickerNS.y = (cellW * 6) + nsY; yearPickerNS.addEventListener(Event.CHANGE, pickYear); } private function pickYear(e:Event):void { firstDay.fullYear = e.target.value; monthSetup(); }One more step and we are done adding the numeric stepper.
Drag the Numeric Stepper component from the Component panel to your Flash file’s Library panel. This way, we can access the Numeric Stepper component and its classes at runtime.
That’s it. Test your movie to see a basic Calendar App running.
Step 15: Customizing the Calendar
We were testing the Calendar app with default values. Now we will customize it by passing different parameter values to its constructor.
To start with, we will try using the different fonts available on our system.
So, in the Flash file, go to the first statement in the ActionScript panel as shown:
Click inside parenthesis next to
new Calendar, and type"Courier New". Now your statement will look like this:var myCalendar:Sprite = new Calendar("Courier New");Test the movie to see the new font applied to dates.
Now try changing next parameter (font size) like so:
var myCalendar:Sprite = new Calendar("Courier New", 17);While typing the next parameter you will see a code hint pop up, showing all necessary parameters with their default values, like so:
Make sure all parameters are there in the code hint popup. There is a bug(?) I have found when I was working on one project, which I’d like to make you aware of.
When I was writing a document class, at a point when there were only four parameters in the constructor signature, I closed this class file. When I got back to the work, I continued working on this class. At one point there were six parameters. Then I wanted to check this parameter passing stuff. So I opened the Flash file I created for this project and started typing parameters, as we did in our Calendar Flash file. I was expecting my newly added parameters to be displayed in code hints… but surprisingly there were still only four parameters.
I saved and restarted all related files but the result was the same. After lots of testing I found the reason. The reason was, I created a new folder in the same folder where the document class was placed. I removed this folder outside the main folder, then I opened the class file to make some changes and I saved it. Then I went back to Flash file and started typing parameters and this time all parameters showed up.
Ok, not a severe bug, but the lesson is: be careful while setting up a folder structure for your project.
So back to the point where we left off. Try passing proper values for different parameters. You can also set up variables in Flash file (or in a class), so if one value is changed then other related parameters will also get adjusted accordingly – for example, if you want to increase the Font Size then Cell Width must increase accordingly, and so on.
Step 16: Checking the Portability of This Calendar App
The real power of this class is that you can use it in any of your project like so:
We have already seen how to instantiate Calendar.as in Flash timeline code through this tutorial.
Now we will see how to call it in another class.
There is nothing much that we have to do differently when calling our class from another one. We simply put the same code we typed in Flash timeline into some other class. See the
"Main.as"class below for better understanding:package { import flash.display.Sprite; public class Main extends Sprite { public function Main():void { init(); } private function init():void { var c:Sprite = new Calendar(); addChild(c); } } }You can use the above “Main.as” in Flash, FlashDevelop, FlashBuilder, FDT. Only you will have to carry Calendar.as, “Main.as” in your project folders.
In Flash IDE set this “Main.as” as Document class using Property Inspector panel. Make sure you have both Calendar.as, “Main.as” in your project folder. Also make sure you have “Combobox” and “NumericStepper” in your library panel. Now you can test the movie to see the working calendar.
FlashDevelop
Now we will test it with FlashDevelop 4.
Create a new folder “Calendar_FD”.
Open FlashDevelop, create a new project, and browse for the above
"Calendar_FD"folder. Press OK to create new project.Inside “Calendar_FD” folder, you will see three new folders: “bin”, “lib” and “src”.
Now in the “src” folder copy Calendar.as. Also copy above “Main.as” we created and replace it with the auto generated “Main.as” in the same folder.
Now every thing is set up. But what about the
"ComboBox"and"NumericStepper"components? Here no Library panel is present, unlike in the Flash IDE.The solution to this issue is making SWC files. We will create a
"CalendarComp.swc"file.Create a new FLA (in Flash Pro), name it “CalendarComp.fla”, and save it anywhere (but remember its path).
Drag
"ComboBox"and"NumericStepper"components to the Library panel and save the file.Open Publish Settings and in the “Flash” tab check “Export SWC” under “SWF settings” group. Hit Publish.
"CalendarComp.swc"is created in the folder where you saved"CalendarComp.fla", so browse to this folder and copy"CalendarComp.swc"to the"lib"folder of the"Calendar_FD"main folder.Back in FlashDevelop, look in the Project Panel. Right click
"CalendarComp.swc"and select"Add to Library". Now we can access all class files required for our components, just like in the Flash IDE.Finally click “Test Project” to see your Calendar app running.
Flash Builder
How about Flash Builder 4?
Create a new folder, “Calendar_FB”.
Open Flash Builder and create a new project.
Browse for the above “Calendar_FB” folder. Also enter a project name (I used “MyProject” as a project name). Press Finish to create a new project.
Inside the “Calendar_FB” folder, you will see a “src” directoy, which already contains “MyProject.as” as the main class.
Copy our Calendar.as class file to this folder. It is now visible in the “Package Explorer”.
As in, FlashDevelop there is no library panel like the Flash IDE has. So how to get
"ComboBox"and"NumericStepper"components?The solution is to use a SWC, like we did with FlashDevelop. But you need to specify the class path in Flash Builder.
So first copy the
"CalendarComp.swc"we created in the above section for FlashDevelop, and paste this file inside the “src” folder of the “Calendar_FB” main folder.In Flash Builder we need to add a class path. So click on Project > Properties to open the “Properties” window. Select “ActionScript Build Path” and click the “Add SWC…” button. Then browse for “CalendarComp.swc” which you pasted in the “src” folder.
Modify “MyProject.as” as shown:
package { import flash.display.Sprite; public class MyProject extends Sprite { public function MyProject():void { var myCalendar:Sprite = new Calendar(); addChild(myCalendar); } } }Finally click “Run My Project” to see your Calendar app running.
Conclusion
So, friends, I would like to stop at this stage. I hope you have got something useful from this tutorial that will help in your coming projects. Best of luck… See you soon.
In this tutorial we saw the logical use of the
"Date()"class in AS3. We addressed the leap year issue. We displayed the next and previous month’s dates. We saw how to deal with Flash components. We also checked the portability of this Calendar app using FlashDevelop, Flash Builder and Flash Pro. During the process we also saw how to create and use SWC files to access Flash components outside Flash Pro.All good. Friends, keep reading Activetuts+. Enjoy!
In this Premium Tutorial, you’ll learn to create an entertaining retro game using Flash and ActionScript 3.0. Check out the demo – the result is based on the classic Boxing for the Atari.
Become a Premium member to follow this awesome tutorial, as well as hundreds of other advanced tutorials and screencasts.
Preview
Here’s what you’ll create by the end of this tutorial:
In the demo above, use the Arrow Keys to move and Z/X to punch.
Active Premium Membership
We run a Premium membership system which costs $9 a month (or $22 for 3 months!) which periodically gives members access to extra tutorials, like this one! You’ll also get access to Psd Premium, Vector Premium, Audio Premium, Net Premium, Ae Premium, Cg Premium and Photo Premium too. If you’re a Premium member, you can log in and download the tutorial. If you’re not a member, you can of course join today!
Also, don’t forget to follow @envatoactive on twitter and grab the Activetuts+ RSS Feed to stay up to date with the latest tutorials and articles.
We’ll soon be publishing our first HTML5 tutorials here at Activetuts+, but before we start, here’s a fast and easy tutorial to get up to speed on the basics of HTML – even if you’ve never done any before.
The Difference Between HTML and .html
Create a new file, anywhere on your computer, and call it page.html. In Windows, the easiest way to do that is right-click an empty spot within a folder, click New > Text File, then make sure you delete the “.txt” at the end of a file. Windows will probably tell you that it’s dangerous to change the file extension like this; just click OK. Alternatively, you can open a text editor (like Notepad or TextEdit), click File > New, and enter page.html as the filename.
Once you’ve created the file, open it in a text editor; there are plenty of fancy editors that offer all sorts of HTML-friendly features, but for this tutorial, the aforementioned Notepad or TextEdit will do just fine. (If you just double-click the file, it’ll probably open in your browser, so you’ll either need to open the text editor first and use File > Open to select the file, or (in Windows) right-click the file and choose Open With > Notepad.) Make sure you use a simple text editor rather than a word processor.
Enter this in your file:
Save it, and open it in your browser – or just click here to open mine. It’ll look something like this:
So there you go! You’ve created a web page – a .html page – and it displays just fine, with no error messages. That’s it, end of the tutorial, thanks for reading.
Just kidding, of course.
Browsers Aren’t Psychic
Here’s an experiment: take an image file, copy it (so that you don’t damage the original image), and rename the copy to something.html. Then, try to open this with your browser. Click here to open the screenshot above, after it’s been renamed from .png to .html, in your browser.
Yikes. That’s a lot of gobbledegook. But if you open the original .png file in your browser, it’ll load just fine. Your browser can obviously cope with these files, so what gives?
When you open a .html file with your browser, it says, “Hey, I know how to deal with .html files!” – it assumes that the contents of the file are written using HyperText Markup Language (HTML for short, as you’ll have guessed), and tries to use everything it knows about this markup language to display the page. Image files aren’t written using HTML, so when it tries to display something that makes any sense using its HTML rules, it fails miserably.
There are other file extensions that browsers automatically associate with HTML – like .htm – and browsers can also be told to assume that other types of files will contain HTML – like ones ending in .php or .aspx.
So, not every file that’s written in HTML will end in .html. But does this mean that
Is this HTML?is perfect HTML in itself? Well… not exactly.People Make Mistakes
While the actual contents of an image file are usually generated by a program like Paint or Photoshop based on a user’s input, the contents of a .html file may be typed directly into the file by the user. And users, being human, make mistakes.
Browsers, in general, err on the side of forgiveness; rather than pettily refusing to display a page if the user has made even a simple mistake – like certain overzealous English teachers – it’ll try to guess what the user meant and display the page to the best of its abilities.
Is this HTML?, you will be shocked to hear, is not ideal HTML, in that it does not contain all of the information a browser would like to see – but it’ll be displayed anyway.This means, then, that files containing HTML don’t always end in .html, and files ending in .html don’t always contain perfect HTML. To confuse the issue even further, sometimes you won’t even save a file at all, but rather type HTML straight into a big text box, the contents of which a computer will later insert in the middle of a bigger HTML file, like I’m doing now:
What Makes a “Proper” HTML Page?
All right, so a single line of English doesn’t count as by-the-book HTML, that’s no surprise. What should we add to our simple file, then?
Tags
Tags are the most important element of HTML. A bit like how blog post tags identify the topics of articles, or hashtags identify which hilarious meme a Tweet is joining in on, HTML tags identify something about whatever it is they’re tagging. This is vague, I know, but you’ll see why.
For example, in our current page:
…what is “Is this HTML?”. It’s text, sure. Specifically, we could say it’s a paragraph of text. And in order to show this, we can tag the paragraph by wrapping it in
<p>tags (p for paragraph – and no, before you ask, there’s no “sentence” tag):Each tag starts with an opening chevron
<and ends with a closing chevron>, with the name of the tag between the chevrons. Note that the second tag has a slash/directly before the tag name; this indicates that it’s a closing tag, and therefore marks the end of the paragraph started by the opening<p>tag. The whole paragraph (including tags),<p>Is this HTML?</p>can be called a p-block.None of this makes the page display any differently, though; the browser was displaying the contents of the original file as if they were in a paragraph anyway. Some tags that will change the appearance of the text are
b– for bold – andifor italic. Try this:We’ve got tags within tags now: the italic-tagged text and the bold-tagged text are both inside the paragraph block; this is called a hierarchical structure. It’s like a tree, with the
pbeing the trunk, andbandibeing branches. I hope you can see that this wouldn’t make sense:…although, your browser would actually display that, because it is so lenient.
Anyway, save this and open it in your browser:
Click here to see mine. You’ll see that it displays with bold and italic text, like this:
Is this HTML?
Any experienced HTML developers reading this over your shoulder will be furiously scratching themselves and calling me a fool for using these exact tags – you’ll see why soon – but just ignore them for the moment.
Okay, now compare these three snippets:
How do you think they’ll each display?
The obvious answer is that the first will display the two paragraphs on separate lines, one after the other; the second will display the two paragraphs on one line; and the third will display them on separate lines with a huge gap between them.
This isn’t true.
All three cases will display in the same way, like so.
Why? Because HTML doesn’t care about carriage returns (new lines). It has a rule that says, by default, “paragraph blocks start on new lines”. Even this will display in the same way:
While we’re at it, so will this:
HTML does care about spaces, but only one at a time. If there are two or more in a row, they get condensed down to one.
Why? Well, it’s really part of a bigger theme that explains your developer friend’s itchiness:
Content, Not Presentation
Whenever a post on any Tuts+ site is tagged “Basix”, our WordPress software automatically adds a tiny speech bubble with a “b” inside over the top of the image at the top of the post . Similarly, my Twitter client is configured so that any Tweets that have been hashtagged “#bieberfever” are displayed in giant bold red text so that I don’t miss them.
But neither Basix nor #bieberfever imply anything about the presentation of the thing they are attached to; Basix says “this tutorial is written for beginners”, and #bieberfever says “this Tweet is about Justin Bieber”. They each imply something about the content. Their presentations change because of external rules that decide how certain types of content should be displayed.
HTML follows the same ideals, and that’s why the
banditags are looked down upon. They only exist because when HTML was invented (about 18 years ago), the creators hadn’t decided on this “separate content from presentation” mindset, so browsers have (again, due to their lenience) continued to display bold and italic tags ever since.Still, this doesn’t mean that you can’t use italic or bold text in your HTML files! No, you just have to use different tags, which identify something about the content rather than the presentation. See, when you add italics to a section of text, you’re trying to emphasize the content – so instead of using an
itag, you should use anemtag. And when you make some text bold, you’re trying tostrengthenit on the page – so you should use astrongtag.Your HTML should therefore look like this:
By default, your browser will display that in exactly the same way as it did when you used old
banditags. You may well ask why we’d bother changing it, then – the answer lies in the phrase “by default”.Individual web pages – even individual paragraphs – can tell the browser to display
emandstrongtags differently: suppose you decide that a dotted underline is a better way of strengthening the text, and small caps are a better way of showing emphasis. You can reconfigureemto do one andstrongto do the other on your site, and the appearance of all the text will change, without you having to alter the textual content at all. But it wouldn’t make any sense to have theb-for-bold tag display a dotted underline, would it?There are a few other tags like
bandithat are no longer in use (said to be “deprecated”); you can find out more about any particular tag on W3Schools.I’ll explain a bit more about changing the appearance of web pages later on. I can’t explain everything, as it’s a huge subject, with a Tuts+ site all to itself.
Required Tags
Okay, so we’ve got a basic HTML page that displays correctly, and contains no deprecated tags. Great! But there are some tags that every HTML page requires, officially. Let’s add them one by one.
The
htmltag says, “hey, browser, this is an HTML document!” To which the browser would presumably respond, “yeah, thanks, I’d guessed that!” if it were a sarcastic human being rather than a piece of software. All of the document should go inside thehtmltag; to go back to the tree metaphor,htmlshould always be the trunk.The
bodycontains the actual content of the page. “As opposed to what?” As opposed to……the
headtag, which contains information about the page, like……the
title. If you look at the title bar of your browser with your old HTML page loaded, it’ll just say something like “page.html” (or maybe “Untitled”, or the name of the browser). If you use the code above, though, it’ll say “This is an HTML page”. Note that “This is an HTML page” does not appear anywhere in the actual content of the page, though: this illustrates the difference between theheadand thebody.That HTML is getting a little hard to read now, so you might prefer to use the Tab key to indent the lines:
Indenting lines like this also helps illustrate that tree-like structure. If you accidentally copied and pasted a tag at the wrong level, it’d be really obvious, like this:
That second paragraph is clearly out of place.
Doctype
I’ve mentioned a few times that browsers are very forgiving when trying to display broken (or non-standard) HTML. This is a blessing and a curse.
It means that sloppy HTML will still be displayed, which is great for anybody that ever makes a mistake i.e. the whole of humankind – but different browsers will display non-standard HTML in different ways.
It means that the people that make the browsers can come up with all sorts of new tags and features that aren’t part of standard HTML but that do display in their browser, which is great for driving the power of what can be done with HTML forward, beyond the official standards – but not all browsers will support the same set. (For instance, years and years ago, Netscape Navigator allowed you to create blinking text using the
<blink>tag, while Internet Explorer allowed you to create text that scrolled across the screen using the<marquee>tag; neither tag was supported by the other browser, and both effects were very annoying.)It means that browser developers broke the rules of (or flat out ignored) some areas of the official HTML standards, so that there was a difference between how a page should display, and how it actually looked.
In a nutshell, it means that, even today, different browsers display the same pages differently.
To attempt to get around this we have doctypes. For example, stick this at the top of your page (even before the
htmltag):…and the browser will display the page in “quirks mode”, meaning, “using the non-standard practices from the late 90s”. Replace it with this:
…and it’ll display it in “standards mode”, meaning, “using the official HTML standards”. Except, some older browsers that are still in use, like Internet Explorers 6 and 7, still don’t fully follow all the standards. And different browsers still each have their own additional features.
Doctypes are a mess – there are lots and lots of them – but fortunately we don’t really have to worry about them, here at Activetuts+, because we’re focusing exclusively on HTML5. And HTML5 has only one doctype:
Ahhh. Okay, sure, older browsers don’t really know what to do with that, but they can’t handle HTML5 anyway, so it doesn’t matter to us. We’ve just got to hope that today’s browsers stick to the HTML5 standards and don’t get into that old mess again.
So now you should edit your HTML page to add a doctype:
It’s a weird tag, because it doesn’t actually enclose anything (there’s no
</!DOCTYPE html>), it’s got a space in it, and it’s partly in capitals. That’s because it’s not a tag at all, it just looks a bit like one because of the chevrons. Don’t worry about it.Attributes
Okay, we’ve used this Language to Markup some Text, but what the heck makes that Text so “Hyper”, the H of HTML?
Hypertext is defined as text, displayed on some sort of electronic device, with hyperlinks to other pieces of text. (The term was coined in the 60s, which explains why it sounds so corny.) And you know what hyperlinks are: bits of text that link to other pages when you click them, like this.
We create hyperlinks using the
atag – for anchor, because you use it to anchor a URL to some text. No, I don’t know why they didn’t usehorl.But this alone isn’t enough:
I mean, how could it? There’s no URL there.
We have to use an attribute of the
atag, like so:We’ve added an
href(hypertext reference) attribute to theatag, which will allow it to hold a URL (though we haven’t specified the URL yet). Note that it’s still anatag, not ana hreftag, and that the closing tag is still</a>, not</a href>; this is why tag names are always a single word, with no spaces.To set the href’s value to a specific URL, we use an equals sign, and enclose the URL in quotes:
Try it out. Clicking the link will take you to the Activetuts+ homepage.
Attributes make HTML a lot more powerful. Putting a
ptag around some text just says, “this is a paragraph” – it attaches one piece of information to the text – but with attributes we can say so much more; here we’ve said, “the word ‘this’ is an anchor, and it refers to http://active.tutsplus.com/”.Self-Closing Tags
This is all very plain, so let’s add an image.
Except… hmm, how exactly do we mark up a piece of text to make it an image? You might guess at something like this:
…which is a good guess, but not correct.
In this case, it makes no sense to have the image’s tag – which is
img, by the way – tag any text. We write it (without any attributes, for now) like so:The slash at the end indicates that the tag is closing itself. It’s a bit of a weird concept, and is where the whole idea of tags tagging text falls down a bit. If you prefer, you can call HTML tags elements instead.
Anyway, let’s insert this into our page:
Like the
atag, theimgwon’t display anything without any attributes. Let’s add some.We’ll need an image file to use; this file has to be accessible by anyone reading your page, so while you could use the file path of a picture on your computer, you’d be the only one that could see it.
I’ll use my Twitter avatar, since that’s online, and hope it doesn’t change before you read this tutorial. It’s:
http://a3.twimg.com/profile_images/1403171562/VectorMJWSquareTransparent.png, so, not exactly brief.Rather than using
hreffor the attribute that points to this URL, we have to usesrc(short for “source”) – after all, it’s not actually a hyperlink. So:If you load this page, you’ll see my avatar at the bottom – unless I’ve changed it or Twitter has gone down, in which case you’ll see a “broken image” symbol.
In case the image does break, we can add an alternative piece of text to be shown instead, using the
altattribute:(Remember, HTML doesn’t care about tabs, new lines, and spaces, so it doesn’t matter that I’ve shoved the new attribute on a separate line.)
Try changing the
srcURL to one that won’t work (like,http://blahblahblah/), and you’ll see the alternative text (click here for an example). Well, you might, depending on your browser; the HTML 5 standards suggest doing this, but don’t insist on it. Chrome doesn’t do it, much to my surprise.Anyway, it’s a good habit to get in to, because it also allows blind people – who are reading your page via text-to-speech software – to hear the contents of the images.
Nested Tags
We’ve seen that tags can be nested, as in our case of some italic emphasized text within a paragraph, or paragraphs within a body, but I want to make it clear that you can make things more complex if you wish.
For example, you can mark up some emphasized text with an anchor:
…as long as you don’t do something silly like:
You can even tag an image with an anchor, as if it were a piece of text:
See the results here.
There are a ton of other tags I could cover, like
h1,h2,h3(etc.) for headings;prefor inserting big blocks of sourcecode or ASCII art (inside which, unusually, HTML does pay attention to extra spaces and new lines);tablefor inserting tabulated information;inputfor adding buttons and textboxes;iframe, which lets you insert a whole extra webpage inside the current webpage; and more – but this is enough about the content, because we need to talk about another aspect of HTML.Presentation: CSS
HTML – the language – isn’t concerned with presentation, it’s true. But HTML pages need to be able to modify their presentation, or everything would be Times New Roman with purple links, like in the examples we’ve seen so far!
For this purpose we have another language, which we embed inside HTML pages, which is all about presentation: Cascading Style Sheets (CSS).
A Simple Example
Remember earlier I mentioned that we could make
strongtags display with a dotted underline, andemtags display in small caps? We can do that using the CSS language. It looks like this, on its own:strong { border-bottom: 1px dotted; } em { font-variant: small-caps; }Doesn’t look at all like HTML, does it? You can probably read it well enough, though. We have the name of a tag, followed by a pair of curly brackets (“braces”), inside which are all the properties of the text that we wish to set. Each property then has a value, with a colon separating the property from its value, and a semi-colon at the end of each property-value pair. Together, this is called a style sheet. Simple enough.
We apply a style sheet to the page like so:
<!DOCTYPE html> <html> <head> <title>This is an HTML page</title> <style type="text/css"> strong { border-bottom: 1px dotted; } em { font-variant: small-caps; } </style> </head> <body> <p>Is <em>this</em> <strong>HTML</strong>?</p> <p>This is certainly a paragraph.</p> <p>And <a href="http://active.tutsplus.com/"><em>this</em></a> is a hyperlink.</p> <a href="http://twitter.com/MichaelJW/"> <img src="http://a3.twimg.com/profile_images/1403171562/VectorMJWSquareTransparent.png" alt="MichaelJW's Twitter Avatar" /> </a> </body> </html>Test the page. You’ll see that the word “this” now uses small capitals (in both cases, because they’re both emphasized), and “HTML” has a dotted underline.
However, the
emtag still has an italics effect, andstrongis still displaying words in bold. What gives?This is what the “Cascading” in “Cascading Style Sheets” refers to: the browser has its own, default CSS styles (like “em” means “italics”), and these cascade down, so that the CSS styles that you apply are simply added on top, rather than replacing the existing ones. It’s as if the browser has a style sheet that looks like this:
strong { font-weight: bold; } em { font-style: italic; }(Yes, I know, it’s counter-intuitive not to use the same property to set italics and bold styles.)
This then cascades down, so that it’s as if the overall style sheet – including your variations – looks like this:
strong { font-weight: bold; border-bottom: 1px dotted; } em { font-style: italic; font-variant: small-caps; }Note that your properties are added after the browser’s own. Since the browser computes these properties one at a time, in order, we can cancel out the browser’s styles by changing our set of styles like so:
strong { border-bottom: 1px dotted; font-weight: normal; } em { font-variant: small-caps; font-style: normal; }Then, the overall style sheet will look like this:
strong { font-weight: bold; border-bottom: 1px dotted; font-weight: normal; } em { font-style: italic; font-variant: small-caps; font-style: normal; }When the browser comes across text that’s been tagged
strong, it’ll say, okay, first, I need to make this bold. Then, I need to give it a 1 pixel thick, dotted border, at the bottom of the text. Then, I need to make the font weight normal. Wait, didn’t I make this bold a second ago? Oh well, never mind.Result: normal-weighted text, with a dotted border across the bottom. Let’s incorporate this new set of styles into our page:
<!DOCTYPE html> <html> <head> <title>This is an HTML page</title> <style type="text/css"> strong { border-bottom: 1px dotted; font-weight: normal; } em { font-variant: small-caps; font-style: normal; } </style> </head> <body> <p>Is <em>this</em> <strong>HTML</strong>?</p> <p>This is certainly a paragraph.</p> <p>And <a href="http://active.tutsplus.com/"><em>this</em></a> is a hyperlink.</p> <a href="http://twitter.com/MichaelJW/"> <img src="http://a3.twimg.com/profile_images/1403171562/VectorMJWSquareTransparent.png" alt="MichaelJW's Twitter Avatar" /> </a> </body> </html>Load the page. Perfect!
Other Sources of CSS
Sticking the style sheet in a
styletag within the head of a page is called embedding it, but there are other ways we can affect the overall style of the page.For instance, we can put style sheet into its own .css file (called an external style sheet), and tell the page to use it. To do that, we use a place a self-closing
linktag within the head, whoserelattribute isstylesheetand whosehreftag is the URL of a .css file.You should be able to figure out how to write that from the description; try it with the style sheet we use at Activetuts+: http://active.tutsplus.com/wp-content/themes/tuts/style.css. If you need help, expand the code box below.
Click here to see the result. It’s a mess, to be honest, but you can see that it’s loaded the styles.
External style sheets are applied after the browser’s defaults, but before any embedded ones. This means you can create a single external style sheet to use across your entire web site, and then tweak it by embedded specific styles for each page, if you wish.
In fact, you can get even more specific, and apply a style to just one single element. This is called an inline style, and it’s done by entering the CSS into the
styleproperty of a tag:Check out the result of the above change. Inline styles are applied after all the other styles.
Note that you don’t have to enter the name of the element and use curly braces – you’ve already defined which tag you want to style. You can apply multiple styles within the same
styleattribute; just type them one after the other (no new line needed), and make sure there’s a semi-colon between each of them.What if you want to style just a few words within a paragraph? You could wrap them in an
emtag, and style that tag like so:…but then they’ll be in italics too, right? So you cancel that out like so:
…but that’s no good either, because you can’t guarantee that the
emtag doesn’t have other styles attached to it as well – and since you don’t know, you can’t cancel them all out. Fortunately, there’s a tag to help.The
<span>TagThe
spantag provides no visual changes by itself, which means it’s perfect for our example above:It’s unlikely – not impossible, but unlikely – that someone will have applied certain styles to all
spantags in an embedded or an external style sheet, so you should be safe using this.Ah, but now look what we’re doing. We’re applying presentation rules right there in the content – exactly the sort of situation that the
emandstrongtags were invented to avoid! It’s not technically invalid HTML, but it’s still dodgy, and should be used sparingly or not at all.However, sometimes you will want to say, “this section of text is different to the others, but none of the existing HTML tags describe that difference very well.” Maybe you’re writing a page about animals, and you decide that it would be useful to tag all the words that represent species – like “cat” and “dog” and so on – so that you can apply an style to all of them at once later.
HTML doesn’t have a
speciestag, though. It’s tempting to say, “well, on my website, I’m going to say that thestrongtag is always used to represent species” – but this is a bad idea; it’s not whatstrongis for.Instead, you can use classes. Any tag can be given a class like so:
Setting a class doesn’t do anything on its own, but you can then define a style for each class in your style sheet:
Note that to define a style for a class, rather than a tag, you have to place a dot before its name in the CSS.
Take a look at the page now. It looks like something you might have found on Geocities about ten years ago, but at least it’s showing some important HTML principles. Classes’ styles are added after embedded styles and before inline ones, so the whole cascading order looks like this:
So, we’ve looked at content and presentation within HTML files. There’s one important aspect left.
Code: JavaScript
HTML and CSS are not programming languages. They don’t actually do anything. They just tell text how it is structured and how it should look.
JavaScript is a programming language. It does stuff.
One of the simplest examples of JavaScript code is
alert('Hello'). When the browser runs this code, it’ll make a little dialog box appear, with the word “Hello” inside.Much like with CSS, we can embed JavaScript into a page, add it via an external file, or attach it to a single tag. To embed it, we put it inside a
scripttag in the head, rather than astyletag:Check out the result.
Linking to external JavaScript is easy, too; you put your code in a .js file, and add it to your page like this:
So, it’s the same tag as we used to embed some code, except it’s empty, and it has a
srcattribute. I’m not going to give an example of this, but feel free to try your own.Inline JavaScript – i.e., attaching code to specific elements – works a little differently than inline CSS. A first guess at how we could do this might be:
<span script="alert('Hello')">Some code is attached to this text.</span>…but, if you think about it, this doesn’t make sense. When would the alert appear? When the page loaded? When that specific section of text loaded? When it was on the page? When it was clicked? You can’t guess, and neither can the browser.
Instead, there are a whole bunch of attributes that you can stick JavaScript into; they’re called event attributes, and they trigger the JavaScript inside them for different reasons.
For instance, one such attribute is
onclick, which triggers the JavaScript inside it whenever the user clicks the contents of the tag with their mouse. It looks like this:Load the page and click the last paragraph – yes, you can click it, even though it’s not a hyperlink. Tada! You’re half-way to programming a duet.
That’s Not All
I’m not going to go into a lot of detail on JavaScript here, because that’s what the bulk of our future HTML5 tutorials are going to focus on. However, you should know that JavaScript can do a lot more than make annoying dialog boxes appear.
Besides making the browser do stuff on its own, JavaScript can alter and create HTML (as well as CSS and JavaScript code itself). It’s hard to overstate how powerful that makes it.
And as if that weren’t enough: if you’re using Chrome, you can play Angry Birds in your browser, written in JavaScript, HTML, CSS… and, okay, a touch of Flash for the sounds.
What Next?
If you’re interested in learning more about the design and appearance of webpages, check out our sister site Webdesigntuts+. One of our other sister sites, Nettuts+, has already published a lot of articles on HTML, CSS, and JavaScript that you should dig in to.
We’ll be focusing on the latest version of HTML – HTML5 – and using it to create apps and games for modern browser. If you want to keep up to date with what we’re doing, then you can follow us on Twitter, like us on Facebook, subscribe to us through RSS, or sign up to our free email newsletter.
Here’s one last tip for the curious: you can view the HTML of any web page on the Internet by right-clicking a blank area and selecting “View Source”. I should warn you: some sites make the markup all messy so that you can’t read (and copy) it; some sites’ markup is just naturally messy. But try it out!
Artificial intelligence is a popular programming topic, with obvious applications in game development, and machine learning is a branch of AI focused on creating code that can learn based on past experiences. Databases are perhaps not as stereotypically cool to study, but still very important for any programmer. Stanford University is offering free, online, undergraduate-level courses in each subject. Read on to find out how you can enroll…
Are These Proper University Courses?
Yes and no. The courses are online versions of those which actual Stanford students will take (other than being delayed by a couple of weeks), and so will be taught and graded at university level – but studying online won’t get you a Stanford certificate or university credit, so you won’t be able to put “attended Stanford university” on your CV.
Still, you’ll get the lectures (in video format) and the homework and exams (which you can submit and have graded electronically), so you’ll be learning the same material as the Stanford students!
What’s Being Taught?
Three courses are on offer:
Introduction to Artificial Intelligence
You’ll need to brush up on your probability and linear algebra skills (perhaps you could try Khan Academy).
For more information, and to enroll, visit ai-class.com.
Machine Learning
Again, you’ll need to be familiar with basic probability theory and linear algebra.
For more information, and to enroll, visit ml-class.com.
Introduction to Databases
This isn’t just about relational databases and SQL; it also covers XML (including XPath, XQuery and XSLT), UML, and “NoSQL” systems.
For more information, and to enroll, visit db-class.com.
Is There a Deadline?
Yes. This isn’t like Tuts+ Premium, where you sign up and have access to courses and tutorials to read at your leisure; everybody taking the online course starts at the same time, with new lectures released each week and homework assignments due in on certain dates.
The courses all start on the 10th of October, and end in mid-December, so you should enroll before they start.
If you don’t have time, or simply don’t want, to do the homework assignments, you can opt to take the “basic track” – in the AI course at least – which has the same material, but without the homework or exams. You can downgrade to this at any time, so there’s no harm in signing up to the “advanced track” for now.
If you’re interested in programming, I hope you’ll sign up to at least one of the courses – not just because of the content, but because this should be a really interesting experiment in online learning.
We’ve got another Activetuts+ Exclusive Freebie for you! This time, Matt Stuttard (aka MSFX) is offering you his brand new debugging utility, Tr.ace(), which adds some very useful extra features to Flash’s
trace()function. Read on to find out more, and to download your copy.All developers use some form of tracing as a loose method of debugging at some point whilst developing their applications. The problem with the trace statement within AS3 though is that it’s rather limited. If you’re working within a team it’s possible you’ve no idea who’s trace is whose or where any of the traces are coming from, and there’s no automated formatting to help distinguish traces from different users and classes!
Wouldn’t it be awesome if you could restrict your applications’ trace statements to specific classes or particular users, or even completely ignore traces from specific users or classes altogether?
Wouldn’t it be great to have a little more control over the formatting of your traces so that you’d know who traced it, the class it’s traced from, and the time the trace was executed?
How about the ability to add automatic linebreaks between each trace, and neatly trace out complex, nested Arrays and Objects, all while automatically copying the output to the clipboard?
Welcome to Tr.ace()!
Downloading Tr.ace()
Tr.ace() is an open source library that’s available to download here at Activetuts+ in ZIP format, and is also available as a public repository on my GitHub for those who are little more nerdy or who wish to fork and/or contribute towards the Tr.ace() library.
The library is AS3-only and has two separate repositories: one for Flash Player 9 and one for Flash Player 10+.
To use the Tr.ace() library you must download and extract one of the above source packages, and then copy the ‘uk’ directory, located within the ‘src’ directory, into your global classpath directory. You’re then all set to go!
(Any problems? Check out this extensive guide to using an external library in your Flash projects.)
Configuring Tr.ace()
Firstly, a little theory and explanation.
Tr.ace() is a library focused on tracing. ‘Tr’ is the main class and ‘ace()’ is a function of the Tr class, hence the library name Tr.ace().
The name of the library also illustrates the library’s usage. As ‘Tr’ is a static class you don’t need to create an instance of it to use its functions or configure any of its settings; you simply use
Tr.whateverTheValueorTr.whateverTheFunction().Lastly, because the internal workings of the library use the Singleton Design Pattern, any of the settings you apply to Tr.ace() only need applying once within your application – I’d suggest within your Document / Main Class.
There are two versions of the library, one for Flash Player 9 and one for Flash Player 10 and up. The only difference (currently) is that the Flash Player 10 version supports automatic copying to the Clipboard.
Lets now take a look at some of these settings (the following are excerpts from ExampleMain.as, which can be found in the src directory) :
As you should be able to see from the above examples it’s easy to very quickly tailor your output to either restrict the output to only be from specific users or classes, or to ignore the output from specific users or classes.
As the below examples also demonstrate, it’s very easy to toggle linebreaks, timestamps and copying to the clipboard (FP10 only). You can also very easily switch tracing off all together.
Lets now have a quick look over the functions available within the Tr.ace() library.
Tr.ace(…)
The Tr.ace(…) function requires three parameters: the output, the username and the class being traced from.
All three of these parameters are required, to allow the library to restrict to or ignore traces from particular users or classes; if you’re worried about this taking longer to type than a usual trace you can wrap it up as a Code Snippet in your favourite IDE – more later.
// A String as the output from user MSFX within the Class 'ClassName'. Tr.ace("here is the output", TrUsers.MSFX, ClassName); // Sum of several numbers as the output from user DEFAULT within the Class 'AnotherClassName'. Tr.ace( ((1 + 2.5 + 6.999) * 12), TrUsers.DEFAULT, AnotherClassName); // tracing a variable as the output from user MSFX within the Class 'YetAnotherClassName'. Tr.ace("variable equals: " + variableName, TrUsers.MSFX, YetAnotherClassName);Tr.aceArray(…)
The Tr.aceArray(…) function requires three parameters: the Array, the username and the class being traced from.
All three of these parameters are required, to allow the library to restrict to or ignore traces from particular users or classes; if you’re worried about this taking longer to type than a usual trace you can wrap it up as a Code Snippet in your favourite IDE – more later.
// A String as the output from user MSFX within the Class 'ClassName'. Tr.aceArray("here is the output", TrUsers.MSFX, ClassName); // Sum of several numbers as the output from user DEFAULT within the Class 'AnotherClassName'. Tr.aceArray( ((1 + 2.5 + 6.999) * 12), TrUsers.DEFAULT, AnotherClassName);Tr.aceMulti(…)
The Tr.aceMulti(…) function requires a mimimum of three parameters: the username, the class being traced from and an unlimited list of arguments (seperated via commas) to trace out. Note that these parameters are in a different order than in the other functions.
All three of these parameters are required, to allow the library to restrict to or ignore traces from particular users or classes; if you’re worried about this taking longer to type than a usual trace you can wrap it up as a Code Snippet in your favourite IDE – more later.
// A String as the output from user MSFX within the Class 'ClassName'. Tr.aceMulti("here is the output", TrUsers.MSFX, ClassName); // Sum of several numbers as the output from user DEFAULT within the Class 'AnotherClassName'. Tr.aceMulti( ((1 + 2.5 + 6.999) * 12), TrUsers.DEFAULT, AnotherClassName);Tr.aceObject(…)
The Tr.aceObject(…) function requires three parameters: the Object, the username and the class being traced from.
All three of these parameters are required, to allow the library to restrict to or ignore traces from particular users or classes; if you’re worried about this taking longer to type than a usual trace you can wrap it up as a Code Snippet in your favourite IDE – more later.
// A String as the output from user MSFX within the Class 'ClassName'. Tr.aceObject("here is the output", TrUsers.MSFX, ClassName); // Sum of several numbers as the output from user DEFAULT within the Class 'AnotherClassName'. Tr.aceObject( ((1 + 2.5 + 6.999) * 12), TrUsers.DEFAULT, AnotherClassName);Adding Users to the Tr.ace() Library
You may have noticed several pre-existing usernames within the Tr.ace() library such as TrUsers.DEFAULT and TrUsers.MSFX in the above examples. To remove the chance of typos it’s recommended that you add your name as a Static Constant to the Tr.ace() Library as with the pre-existing usernames.
To add yourself to the library, open the ‘TrUsers.as’ file located at ‘uk/msfx/utils/tracing/users/TrUsers.as’. Add your own user name as a public static constant to the bottom of the list and you’re ready to go!
Using Tr.ace() With Code Snippets
Although Tr.ace() provides lots of advantages over using the usual
trace()statement within Flash there is quite a lot more to type, which could potentially slow down your development. Enter Code Snippets.Code Snippets are exactly what you’d think: automatically-inserted snippets of code. Most IDEs, if not all, support them and they speed up your development like you’re Superman. I use FlashDevelop (and you should too!) so let’s quickly have a look at adding a new code snippet for Tr.ace().
The code snippet must be usable wherever you decide to add it to your application with as little adjustments as possible. With Tr.ace() you must provide both the user tracing and the Class traced from along with the output. The user is easy enough to wrap up in the code snippet since it’ll likely not change very often; however, it’s likely we’ll trace from different Classes quite often.
Therefore, instead of entering the class manually each time, we can use a simple piece of code to return the class we’re tracing from automatically. I performed some tests using Grant Skinners Performance Harness on different approaches of achieving this and found the method below to be the most efficient (other than using the actual class name itself). You can find the results within the src directory.
If you’re using FlashDevelop, open the Code Snippets Panel and click ‘Add’ to create a new snippet. (There’s bound to be a similar menu option in whatever IDE you use.)
Add a new snippet and call it whatever you want (I used ‘tr’) and add the main body to the snippet – with your own username of course.
Object(this).constructorreturns the Class from which the function is called, no matter where you enter this line in your project.Add the entry point where the output for the trace should go.
Finally, click ‘Save’.
Code snippet complete! To use the snippet within FlashDevelop, simply press Ctrl+B, type ‘tr’ and hit Enter. Then, type in whatever you want to trace.
Documentation for Tr.ace()
Within the download package you’ll find a ‘docs’ directory, open the ‘index.html’ file to view the ASDocs-generated Documentation for Tr.ace(). You can also find the documentation online: http://docs.msfx.co.uk/as3/trace/.
Happy Tracing!
So, that’s it really. If you have any questions or suggestions for the library feel free to get in touch in the comments.
Happy Tracing!
Twice a month, we revisit some of our readers’ favorite posts from throughout the history of Activetuts+. This week’s retro-Active Quick Tip, first published in June 2010, is an introduction to a popular (but commonly mis-used) design pattern.
In this Quick Tip we are going to talk about the Singleton design pattern and how it can help you to optimize your code when you need exactly one instance of a class.
Step 1: Introduction
As a programmer you must be aware that there are some cases where you want to use an instance of a class, but you want to create just one and keep it throughout the entire program. Well, that’s what Singletons are for.
Step 2: What is a Singleton?
A Singleton is a Object-Oriented Design Pattern used by many programmers; it lets you create a sort of “global” instance of a class. It is created in such a way that only one unique instance can exist, so that all instances of that class are in exactly the same state.
Step 3: Why Would We Use it?
The most common example would be a score – for example a football score. You would have a Score class, with properties homeTeamScore and awayTeamScore and a method like increaseScore(team:Team).
Both teams must be able to increase their score when they make a goal, but you can’t give each team their own Score instance; you want both to access and modify the same one.
This is a case where a Singleton is a perfect solution, since it could work as a global instance that anybody can access; you will have just one instance for everyone, so you don’t have to worry that each team will be modifying a different score.
Step 4: Singleton Class
Now let’s start creating a singleton in AS3, but first remember the key elements of a singleton:
Create a new AS3 class and call it Singleton.as.
(Not familiar with class-based coding? Check out this quick intro.)
Here’s the basic Singleton code:
package { public class Singleton { private static var instance:Singleton; //This will be the unique instance created by the class private static var isOkayToCreate:Boolean=false; //This variable will help us to determine whether the instance can be created public function Singleton() { //If we can't create an instance, throw an error so no instance is created if(!isOkayToCreate) throw new Error(this + " is a Singleton. Access using getInstance()"); } //With this method we will create and access the instance of the method public static function getInstance():Singleton { //If there's no instance, create it if (!instance) { //Allow the creation of the instance, and after it is created, stop any more from being created isOkayToCreate = true; instance = new Singleton(); isOkayToCreate = false; trace("Singleton instance created!"); } return instance; } } }Step 5: Create a Flash Project
Now let’s go and test the Singleton, first create a new Flash file named Main.fla. On the properties panel set the class also to Main.
Step 6: Create a Singleton
Create a new class named “Main” and create an instance of Singleton using the constructor:
package { import flash.display.MovieClip; public class Main extends MovieClip { public function Main() { var testSingleton:Singleton = new Singleton(); } } }Save and run it, you will see that it throws an error telling us to use the getInstance() function instead, so go ahead and do that:
package { import flash.display.MovieClip; public class Main extends MovieClip { public function Main() { var testSingleton:Singleton = Singleton.getInstance(); } } }Save and run it, there’s no error now, and you can see in the console the text “Singleton instance created!”, meaning that it was created successfully.
So when you use a Singleton class, you cannot use new Singleton(), you have to use Singleton.getInstance() instead.
Step 7: Add Properties to the Class
The Singleton isn’t very useful at the minute. Let’s add a property:
package { public class Singleton { private static var instance:Singleton; //This will be the unique instance created by the class private static var isOkayToCreate:Boolean=false; //This variable will help us to determine whether the instance can be created //new example property: public var exampleProperty:String = "This is an example"; public function Singleton() { //If we can't create an instance, throw an error so no instance is created if(!isOkayToCreate) throw new Error(this + " is a Singleton. Access using getInstance()"); } //With this method we will create and access the instance of the method public static function getInstance():Singleton { //If there's no instance, create it if (!instance) { //Allow the creation of the instance, and after it is created, stop any more from being created isOkayToCreate = true; instance = new Singleton(); isOkayToCreate = false; trace("Singleton instance created!"); } return instance; } } }Now, in Main.as, you can access testSingleton.exampleProperty just as if it were a normal class. Try tracing it out.
Step 8: Try Creating Another Singleton
To prove that the Singleton does what it’s supposed to do, create another singleton and change the example property of one of them:
package { import flash.display.MovieClip; public class Main extends MovieClip { public function Main() { var testSingleton:Singleton = Singleton.getInstance(); var anotherSingleton:Singleton = Singleton.getInstance(); anotherSingleton.exampleProperty = "This is set in anotherSingleton"; trace(testSingleton.exampleProperty, anotherSingleton.exampleProperty); } } }What do you think will happen?
This even works if you create the Singleton variables in different classes.
Conclusion
The singleton pattern can be used on any code, and I highly recommend it if you are going to use just one instance of a class since it gives you better control of it. I hope you liked this Quick Tip, thanks for reading!
Saludos -Eduardo
This two-part tutorial will cover creating a multi-state car using a finite state machine. We will start with Procedural FSM and progress into the State Pattern design pattern. Concept and creation will be our main focus during this first part; we will then proceed into application and extension during the second part.
Final Result Preview
Let’s take a look at the final result we will be working towards:
What Is a Finite State Machine?
Wikipedia defines an FSM (Finite State Machine) as a mathematical abstraction sometimes used to design digital logic or computer programs. It is a behavior model composed of a finite number of states, transitions between those states, and actions, similar to a flow graph in which one can inspect the way logic runs when certain conditions are met.
It has finite internal memory; an input feature that reads symbols in a sequence, one at a time, without going backward; and an output feature, which may be in the form of a user interface, once the model is implemented. The operation of an FSM begins from one of the states (called a start state), goes through transitions (depending on the inputs) to different states, and can end in any of those available – however, only a certain set of states mark a successful flow of operation.
In my own words, an FSM is a device used by developers to create objects that have different behaviors determined by the current state they are in. Depending on the input, the object can react and/or transition to a different state.
One good example would be a 1970 HK VP70Z machine pistol, which has three firing modes: safety, single shot, and semi-automatic three-round burst. Depending on the current mode you have it set to (state), the result (output) is different when you pull the trigger (input).
Tools: When conceptualizing an idea (the multi-state object you’re trying to create), it’s best to use a State Transition Table to know what states and actions for those states you will need to add.
Step 1: Setup
It’s time to start a new project. With FlashDevelop, create a new AS3 project. For the name, put CarFSM. Click “Browse…” and save it to your desired location. Go into the Package slot and enter “com.activeTuts.fsm”. Make sure the “Create directory for project” checkbox is selected then click “OK” to finish.
Once the project is loaded in FlashDevelop, click “View” and select “Project Manager”. See the “src” folder? Right-click that and choose “Explore”.
When you have that folder open, you should see the “com” folder you created earlier. Open the sourcecode I’ve included with this tutorial and drag the “assets” folder into “src”; make sure you don’t drop it into the “com” folder.
Next, go inside the sourcecode “com” folder and drag the “bit101″ folder into the “com” folder inside “src”. You can also download minimalComps here if you prefer getting it straight from the source.
Finally, drill down inside the “com” folder (within “src”) all the way into “fsm” and double-click Main.as. This should now be open inside FlashDevelop (assuming you have FD as your default .as extension application).
Step 2: Warm Up
We’ll start by considering the two states of an even more simple example: MinimalComps’s Checkbox.
Let’s say we want a Checkbox that will reflect its current state by changing its label. Below is the Transition Table for the Checkbox.
Now for the code. Inside Main.as, one line below the class imports, add the metadata shown below.
Next, go inside the
init()method and set your cursor below where it says “entry point”. Then add a method callsimpleExample(). Next, make sure you have the cursor active somewhere inside the method call, and press the “CTRL + SHIFT + 1″ keys. A prompt will show; pick “Generate private function” and hit the “Enter” key.Now just copy and paste the code below inside the newly created method. Next, put your cursor inside the word “CheckBox” and press “CTRL + SHIFT + 1″ to automatically import the required class. Once done, hit “CTRL + ENTER” to run the application.
From here on, if you run into any errors, please compare your classes with the ones I’ve included with the source download.
var checkBox:CheckBox = new CheckBox (this, 240, 160, 'false', showValue); checkBox.scaleX = checkBox.scaleY = 2; function showValue (e:Event):void { CheckBox (e.target).label = Boolean (e.target.selected).toString (); }You should have something similar to what you see above this line. There are your two states,
ONandOFF. Every time you click , the checkbox toggles states and also changes its label as a form of output.On to the real “Car” FSM project. Make sure you have the project set to run in “debug” mode to enable
trace()statements.Step 3: The Procedural Car FSM
Okay, let’s forget the preview at the top of the page and start the Car FSM from scratch. In Main.as, highlight the
init()method along with thesimpleExample()method and hit the “BACK” key to remove them.Go one line above the constructor and add the variables below.
The variables _past, _present, _tick, and _counter will all be used for timed execution. I’ll explain more about that soon. The _car variable will hold reference for the Car class that will encapsulate the procedural Car FSM actions. The rest are Boolean properties used for triggering timed actions.
Let’s work on the timed execution. Add the code below inside the constructor.
_present = getTimer (); trace ('Start of constructor = ' + _present * 0.001 + ' seconds\n'); _past = _present; addEventListener (Event.ENTER_FRAME, update);Get your cursor inside the word “update” and press “CTRL + SHIFT + 1″ and choose “Generate Event handler”. When you test the application, you’ll see a print out similar to “Start of constructor = 2.119 seconds” (it could be less if you have a faster PC). It’s the same as dividing the value of
getTimer()with 1000 but they say multiplication is faster.Let’s proceed to the
update()method. Add the code below into it._present = getTimer (); _tick = (_present - _past) * .001; ///converted to 1/1000 milliseconds _past = _present; _counter += _tick; ///used for tests with timed execution. if (_counter >= 2)//2 seconds { _counter -= 2; trace (_counter + ' 2 seconds'); }Now, when you test it again, you’ll notice a
trace()statement pop-out every two seconds. The _counter is then reset to whatever overlap it had to maintain timing accuracy.Try using a different value other than two seconds and run it a couple more times.
On to the Car class. Before proceeding, go ahead and remove that
if()statement inside theupdate()method.Step 4: The Car Class
As I mentioned earlier, we’re starting with a fresh idea of creating a multi-state car. Let’s say we decided to have a car that can be turned on and off, also driven forward and can run out of gas. That would give us four different states –
ON,OFF,DRIVE_FORWARD, andOUT_OF_FUEL.The very first thing to do is work out the different states and actions for those states in a State Transition Table. You can use a pencil and a blank piece of paper to quickly jot down all the states and actions the Car object would need. See the image below.
Always have an " update()" method to have real time control over your states. Like consume a higher amount of fuel when driving than when the engine’s idling at park.
It’s easy to tell how each state should respond for each of the actions. It seems simple because we (being human) all think of objects to be in one state or another.
Now we’re ready to code the class.
Inside the constructor method in Main.as, go one line before the
ENTER_FRAMEevent listener and add the code below.Now since there is no Car class, get your cursor inside the “Car” word and press “CTRL + SHIFT + 1″, select “Create new class” and hit the “ENTER” key.
Use the same information as shown below. Click “OK” to finish.
You should now have the Car class open in FlashDevelop.
Step 5: Car Variables
Add the code below one line above the class constructor.
The Car is setup to only consume fuel 6 times per second. This is represented by the class constant
ONE_SIXTH_SECONDS. In addition, the consumption amount depends if the car is on idle or driving forward. We’ll useIDLE_FUEL_CONSUMPTIONandDRIVE_FUEL_CONSUMPTIONfor those purposes.The four states are represented by String constants with
ENGINE_OFFset as default.The
_engineTimerproperty will be used to triggerconsumeFuel()every 1/6 seconds but only if the state is eitherENGINE_ONorENGINE_DRIVE_FORWARD.Finally,
_fuelSupply(which is whatconsumeFuel()will slowly take away) takes in the value of_fuelCapacityfor a full tank.Step 6: Methods From the State Transition Table
Leave the Car constructor empty for now. Go below it and add the
update()method shown below.public function update ($tick:Number):void { switch (_currentState) { case ENGINE_OFF: //nothing break; case ENGINE_ON: _engineTimer += $tick; //gas consumption and trace statement if (_engineTimer >= ONE_SIXTH_SECONDS) //6 times per second interval { trace ('vm');//you may comment this out if you like _engineTimer -= ONE_SIXTH_SECONDS; // 0 + overlap consumeFuel (IDLE_FUEL_CONSUMPTION);///30 seconds gas supply } break; case ENGINE_DRIVE_FORWARD: _engineTimer += $tick; if (_engineTimer >= ONE_SIXTH_SECONDS) { trace ('vroomm');//you may comment this out if you like _engineTimer -= ONE_SIXTH_SECONDS; consumeFuel (DRIVE_FUEL_CONSUMPTION);///15 seconds gas supply } break; case ENGINE_OUT_OF_FUEL: //nothing break; } }We’ll have Main.as call this method at every
ENTER_FRAMEevent passing in the elapsed time between frames. Once called, it checks the Car’s current state and runs the appropriate action.If left alone, state transition can only occur through
consumeFuel()which sets it toOUT_OF_FUELwhen_fuelSupplyruns out.Note: Actions that are on your State Transition Table will always be public access used as input controls. This holds true whether you’re using Procedural FSM or the State Pattern.
Step 7: Turning the Car On
Add the code below after the
update()method.public function turnKeyOn ():void { trace ("attempting to turn the car on..."); switch (_currentState) { case ENGINE_OFF: trace ("Turning the car on...the engine is now running!"); _currentState = ENGINE_ON; break; case ENGINE_ON: trace ("the engine's already running you didn't have to crank the ignition!"); break; case ENGINE_DRIVE_FORWARD: trace ("you're driving so don't crank the ignition!"); break; case ENGINE_OUT_OF_FUEL: trace ("no fuel - the car will not start, get some fuel before anything. Returning the key to the off position."); break; } }Just like the
update()method,_currentStateis checked and the appropriate action is run. It pretty much explains itself.Step 8: Turning the Car Off.
The same goes for turning the Car off. Add it next.
public function turnKeyOff ():void { trace ("attempting to turn the car off..."); switch (_currentState) { case ENGINE_OFF: trace ("The car's already off, you can't turn the key counter-clockwise any further..."); break; case ENGINE_ON: trace ("click... the engine has been turned off from park."); _currentState = ENGINE_OFF; break; case ENGINE_DRIVE_FORWARD: trace ("nvrm...click... rolling to a stop...the engine has been turned off."); _currentState = ENGINE_OFF; break; case ENGINE_OUT_OF_FUEL: trace ("you already did this when the fuel ran out earlier..."); break; } }It becomes very easy to create the methods. Just copy and paste the previous one, then make a few changes here and there.
Step 9: Driving Forward
Always go back to your State Transition Table to see what needs to happen for each state when you call the input method you’re currently working on.
Add the code below the
turnKeyOff()method.public function driveForward ():void { trace ("attempting to drive forward..."); switch (_currentState) { case ENGINE_OFF: trace ("click, changing the gear to drive doesn't do anything...the car is not running, returning the gear to park..."); break; case ENGINE_ON: trace ("click, changing gears to drive ...now were going somewhere..."); _currentState = ENGINE_DRIVE_FORWARD; break; case ENGINE_DRIVE_FORWARD: trace ("already driving - no need to change anything..."); break; case ENGINE_OUT_OF_FUEL: trace ("click, changing the gear to drive won't do anything...the car has no fuel, returning the gear to park..."); break; } }Step 10: Consuming Fuel
This method is private since only the Car needs access to it. It’s called six times per second from
update().Put the code next after the
driveForward()method.private function consumeFuel ($consumption:Number):void { if ((_fuelSupply -= $consumption) <= 0) { _fuelSupply = 0; trace ("phit...phit - the engine has stopped, no more fuel to run, returning the gear to park..."); _currentState = ENGINE_OUT_OF_FUEL; } }Now you see how the code goes as explained earlier at the Class variables section.
Step 11: Refueling the Car
This method is private since only the Car needs access to it.
Put the code next.
public function reFuel ():void { trace ("attempting to refuel..."); if (_fuelSupply == 1) { trace ("no need to refuel right now, the tank is full."); return; } switch (_currentState) { case ENGINE_OFF: trace ("getting out of the car and..."); break; case ENGINE_ON: trace ("turning the key to off position, getting out of the car and..."); _currentState = ENGINE_OFF; break; case ENGINE_DRIVE_FORWARD: trace ("changing gear from drive to park and turning the key to off position, getting out of the car and..."); _currentState = ENGINE_OFF; break; case ENGINE_OUT_OF_FUEL: trace ("turning the key to the off position, getting out of the car and..."); _currentState = ENGINE_OFF; break; } var neededSupply:Number = _fullCapacity - _fuelSupply; _fuelSupply += neededSupply; trace ("filling up to " + _fuelSupply + " gallon(s) of fuel."); }The method first checks to see if the Car has a full tank. If so, it tells you it has a full tank and exits the method.
If the Car doesn’t have a full tank on the other hand, it goes through the familiar case/switch statement and runs the proper
trace()statement.The last bit of code calculates the amount of consumed fuel and replenishes only that to keep a full tank. It then prints the value of a full tank.
Step 12: Using
toString()to Helptrace()StatementsThis method had to be overridden since the Car inherits from Sprite which in turn inherits from EventDispatcher.
All it does is return a String statement shown below. Add it as the last method for the Car class.
override public function toString ():String { return "The car is currently " + _currentState + " with a fuel amount of " + _fuelSupply.toFixed (2) + " gallon(s)."; }So now, whenever you call
trace(_car)from Main.as, instead of getting “[object Car]“, you get a statement like “The car is currently off with a fuel amount of 1.00 gallon(s).”Let’s go back to Main.as for testing. Be sure to save your work before moving forward.
Step 13: Stress Testing
Inside Main’s constructor, right after where you added the
ENTER_FRAMEevent listener. Enter the code below.At this point the Car will perform all six actions without any time lapse. The
ENTER_FRAMEevent has not started yet.Next, get inside the
update()method just below where_tickis added to_counterand paste the code shown next._car.update (_tick); //test 1 after 5 seconds of running the car and //only if _initiatedTest1 has a value of false. if (_counter >= 5 && ! _initiatedTest1) { _initiatedTest1 = true; _car.reFuel (); _car.reFuel (); _car.driveForward (); _car.turnKeyOff (); _car.turnKeyOff (); _car.driveForward (); _car.turnKeyOn (); _car.driveForward (); trace (_car); } //test 2 after 11 seconds if (_counter >= 11 && ! _initiatedTest2) { _initiatedTest2 = true; _car.turnKeyOff (); _car.turnKeyOn (); _car.driveForward (); trace (_car); } //test 3 after 30 seconds if (_counter >= 30 && ! _initiatedTest3) { _initiatedTest3 = true; _car.turnKeyOn (); _car.turnKeyOff (); _car.turnKeyOn (); _car.driveForward (); _car.turnKeyOn (); _car.turnKeyOff (); _car.turnKeyOn (); trace (_car); } //test 4 after 35 seconds if (_counter >= 35 && ! _initiatedTest4) { _initiatedTest4 = true; _car.reFuel (); _car.reFuel (); trace (_car); _car.turnKeyOff (); _car.driveForward (); _car.turnKeyOff (); _car.turnKeyOff (); _car.turnKeyOn (); trace (_car); } //test 5 after 42 seconds if (_counter >= 42 && ! _initiatedTest5) { _initiatedTest5 = true; _car.driveForward (); trace (_car); } //test 6 after 45 seconds if (_counter >= 45 && ! _initiatedTest6) { _initiatedTest6 = true; _car.turnKeyOn (); _car.turnKeyOn (); _car.driveForward (); trace (_car); } ///stop the car after 60 seconds if (_counter >= 60 && ! _finalActions) { _finalActions = true; trace ('elapsed ' + getTimer () / 1000); _car.turnKeyOff (); trace (_car); }I know it’s a lot of code but again, it’s self-explanatory. Run the application and check your output.
If you get errors, make sure to compare your code with the classes I’ve included with this tutorial.
Try changing
_fuelCapacityin Car and mix-up the methods on some or all of the test sections and run it again. You’ll see that the code is solid and this Procedural FSM is effective. And That’s it! We’re done.Wait a minute. Since all is well, why don’t we add the ability to drive backward and turbo? While at it, we might as well add animation and sound. Now, imagine just how bloated the Car class would get if you make it do all the things the car does at the top of page. We’re looking at maybe 2000 lines of code – at least. LOL! I’d probably say, Ya, sure, I can hack that. But the code becomes very fragile and easy to break. So it might be a good idea to use a different technique.
If the FSM object has simple behavior, by all means, use this technique. But if you have a complex object that might need to add new features in the future. Maybe even add a few more states – well, that’s where the State Pattern comes in.
Step 14: Introducing the State Pattern
Say hello to the “big brother” of Procedural FSM. Using this Design Pattern will make your states easy to maintain and make changes to, but the best part – other states can now be added without the risk of ruining the code.
To apply this pattern, we will again refer to our trusted State Transition Table. See Step 4. The State Pattern consists of three parts. First is the State Interface, this will contain all the actions you see in the State Transition Table. In addition, this State Interface may also contain methods that are shared by all State classes. Second, the State classes that correspond for each state shown in your State Transition Table. And third, the State Machine – which is normally your converted Procedural FSM object (the Car class). When converted, the Car will provide public accessors and modifiers to allow external control from all State classes. The Car will delegate actions to the currently active State.
Step 15: Beginning the Conversion
Click “View” and select “Project Manager”. Within “src”, drill down until you see the “fsm” folder. Right-click that and choose “Add > New Interface…” then hit “ENTER”.
Name it “IState”. Interfaces start with “I” for naming convention.
Once FlashDevelop opens the class, add the code below in it.
This IState interface will be implemented by all the State classes. The last function
toString()has nothing to do with controlling the Car but all the State classes use it.For more info about Interfaces, see AS3 101: Introduction to Interfaces. Let’s start adding the state classes.
Step 16: The EngineOff Class
Follow the same procedure when you created the IState Interface but instead, choose “Add new class”.
Name it “EngineOff”. For the interface slot, click add and type “IState”, this should find the IState class from within the same folder. Also, the checkbox for “Generate interface methods implementations” should be selected. Click “OK” to complete.
The new class comes out half-way finished. It should look very similar to what shows below.
package com.activeTuts.fsm { public class EngineOff implements IState { public function EngineOff() { } /* INTERFACE com.activeTuts.fsm.IState */ public function turnKeyOff():void { } public function turnKeyOn():void { } public function driveForward():void { } public function reFuel():void { } public function update($tick:Number):void { } public function toString():String { } } }These state classes won’t need to extend Sprite since all Media assets (second part) will be added and controlled via the car. The states will be instantiated through the Car class passing itself as reference. A two-way composition structure will be used to allow communication between the Car and the state classes.
Step 17: Finishing Our EngineOff Class
Change the constructor method to match the code below.
private var _car:Car; public function EngineOff ($car:Car) { _car = $car; }I’ve included the _car variable above the modified constructor. Now we can control the Car class from within this state.
Let’s move on to the interface method implementations.
Get inside the
turnKeyOff()method. Check your State Transition Table to see what happens here. Next, compare that with the proceduralturnKeyOff()method inside the Car class. Remember, we still have the Car class in Procedural FSM. Once you see the match. Copy the action for theENGINE_OFFstate into the empty method. TheturnKeyOff()method should reflect what you see below.public function turnKeyOff ():void { _car.print ("The car's already off, you can't turn the key counter-clockwise any further..."); }The
trace()statement has been replaced byprint()which we’ll add to the Car class later.Now go inside the
turnKeyOn()method and add the code listed next._car.print ("Turning the car on...the engine is now running!"); _car.changeState (_car.getEngineOnState ());Check it against you State Transition Table and procedural
turnKeyOn()method for theENGINE_OFFstate to see if it’s the same. ThechangeState()method is delegated back to the car passing in the retrieved state it needs to change to.The rest of the methods are processed the same way. Copy the code below and replace the empty methods with it.
public function driveForward ():void { _car.print ("click, changing the gear to drive doesn't do anything...the car is not running, returning the gear to park..."); } public function reFuel ():void { if (_car.hasFullTank () == false) { _car.print ("getting out of the car and adding " + Number (_car.refillWithFuel ()).toFixed (2) + " gallon(s) of fuel."); } } public function update ($tick:Number):void { } public function toString ():String { return 'off'; }The
driveForward()method does the same as the proceduraldriveForward()methods with_currentStateset asENGINE_OFFreFuel()asks the car to see if the tank is not full. If not, the car will then refill with fuel. You’ll see how those two methods work when we change the Car class later.The
update()method remains empty since the car won’t be running.toString()works the same as the car’stoString()method.This completes the EngineOff class.
Before creating the rest of the other state classes, let’s modify the Car class and convert it into its own State Machine.
Step 18: The Car State Machine
Important: Create a duplicate Car class before following the procedure below. A text copy would suffice but make sure to save it as reference for later.
Instead of going through the changes item by item, just copy the code below then paste and replace the content of your Car class with it.
package com.activeTuts.fsm { import flash.display.Sprite; import flash.events.Event; public class Car extends Sprite { ///CAR STATES //engine off state //engine on state //drive state //no gas state public static const ONE_SIXTH_SECONDS:Number = 1 / 6; //6 times per second public static const IDLE_FUEL_CONSUMPTION:Number = .0055; public static const DRIVE_FUEL_CONSUMPTION:Number = .011; private var _engineOffState:IState; private var _engineOnState:IState; private var _engineDriveForwardState:IState; private var _engineOutOfFuelState:IState; private var _fuelCapacity:Number = 1; private var _fuelSupply:Number = _fuelCapacity; //starting on a full tank (in gallons) private var _engineTimer:Number = 0; private var _currentState:IState; public function Car () { init (); } private function init ():void { initializeStates (); } private function initializeStates ():void { _engineOffState = new EngineOff (this); _engineOnState = new EngineOn (this); _engineDriveForwardState = new EngineDriveForward (this); _engineOutOfFuelState = new EngineOutOfFuel (this); _currentState = _engineOffState;//default state } public function update ($tick:Number):void { _currentState.update ($tick); } ///car functions public function turnKeyOn (e:Event = null):void { _currentState.turnKeyOn (); } public function turnKeyOff (e:Event = null):void { _currentState.turnKeyOff (); } public function driveForward (e:Event = null):void { _currentState.driveForward (); } public function reFuel (e:Event = null):void { _currentState.reFuel (); } public function consumeFuel ($consumption:Number):void { if ((_fuelSupply -= $consumption) <= 0) { _fuelSupply = 0; print ("the engine has stopped, no more fuel to run..."); changeState (_engineOutOfFuelState); } } public function refillWithFuel ():Number { var neededSupply:Number = _fuelCapacity - _fuelSupply; _fuelSupply += neededSupply; return neededSupply; } public function hasFullTank ():Boolean { var fullTank:Boolean = _fuelCapacity == _fuelSupply ? true : false; if (fullTank) print ("no need to refuel right now, the tank is full..."); return fullTank; } public function getEngineOffState ():IState { return _engineOffState; } //explicit, you know you're calling a method public function getEngineOnState ():IState { return _engineOnState; } public function getEngineOutOfFuelState ():IState { return _engineOutOfFuelState; } public function getEngineDriveForwardState ():IState { return _engineDriveForwardState; } public function changeState ($state:IState):void { _currentState = $state; } public function get engineTimer ():Number { return _engineTimer; } //implicit, as if you're accessing a public variable public function set engineTimer ($value:Number):void { _engineTimer = $value; } public function print ($text:String):void { trace ($text); } override public function toString ():String { return 'The car is currently ' + _currentState + ' with a fuel amount of ' + _fuelSupply + ' gallon(s).'; } } }Let’s go over the changes.
Starting at variable definition. You’ll notice that the four states have changed type to IState and are no longer static constants.
Next, the constructor now calls
init()which in turn callsinitializeState(). All the state classes are instantiated through that method.Then comes the easy part, no more switch statements. The car just delegates the actions to the current state. See
turnKeyOff()down toreFuel()The
consumeFuel()method had to become public access for EngineOn and EngineDriveForward.And then the two methods we used in EngineOff’s
reFuel()method –hasFullTank()andrefillWithFuel().Below them are the explicit getters that provide access for all four states. It may seem like it’s weird protocol but it’s just encapsulation at work.
The
changeState()does exactly what it says, it changes the _currentState.Again, following the strict rule of OOP, the
_engineTimerproperty can be accessed and modified through these two methods:get engineTimer()andset engineTimer().print()for now will just pass the String parameter to atrace()statement. And then thetoString()method.Step 19: Creating the Other Three States
To simplify creation of the three other classes, get inside the Car class from within the
initialize()method. Place your cursor inside the “EngineOn” word and press “CTRL + SHIFT + 1″ to generate a prompt. Select “Create new class” and hit “ENTER”.Match the information as shown in the image below then click “OK”.
This is similar to Step 16 when you created the EngineOff class. Only this time, we used FD’s shortcut keys. Also, you’ll notice a refence to the car object in the constructor which was passed in at instantiation. Don’t forget to add that “$” sign to the car parameter for your constructor and the only class variable
_caron top.Compare it with the code below.
package com.activeTuts.fsm { public class EngineOn implements IState { private var _car:Car; public function EngineOn($car:Car) { _car = $car; } /* INTERFACE com.activeTuts.fsm.IState */ public function turnKeyOff ():void { } public function turnKeyOn ():void { } public function driveForward ():void { } public function reFuel ():void { } public function update ($tick:Number):void { } public function toString ():String { } } }Now go back inside the
initialize()method in Car and repeat the process for the last two remaining state classes.Step 20: Completing the EngineOn Class
Do you think you can piece it together using the Car.txt (duplicate) and State Transition Table?
Give it a shot, just follow Step 17 and you’ll do great. Just remember that you’re working on action results for
ENGINE_ONnow.Excellent! When you’re done, compare your code with the classes inside the folder “StatePatternPartial1″ included with the source download.
Step 21: Final Testing
Once you finish work with all your state classes, go back to Main.as and run your application.
Hopefully everything went well and you didn’t get any errors. Information should start printing out of your output panel.
We’ll end the first part of the tutorial here. For the second part, we’ll start by adding the two other states “EngineDriveReallyFast” and “EngineDriveBackward”. Then we’ll add controls for animation and sound proving how easy it is to modify and scale.
Conclusion
Of all the design patterns I’ve played with, this one is by far the most important (for me), especially in the realm of game design. You’ll see why when you start using it to build your next game object. Why don’t you try creating the pistol I mentioned at the beginning of this tutorial. You’ll enjoy creating it! Just remember to start small. Always build it from Procedural FSM then convert it to the State Pattern.
Here are the steps:
See you in the second part!
As always, for any comments, suggestions, or concerns, please post a note in the comments section.
Thanks for reading!
In the first part of this members-only tutorial, you built the basic structure of a Flash card-matching game, using Flickr to get its images. Now you’ll complete the game, adding multiple levels and a full interface.
Become a Premium member to follow this awesome tutorial, as well as hundreds of other advanced tutorials and screencasts.
Preview
Here’s what you’ll create by the end of this tutorial:
Active Premium Membership
We run a Premium membership system which costs $9 a month (or $22 for 3 months!) which periodically gives members access to extra tutorials, like this one! You’ll also get access to Psd Premium, Vector Premium, Audio Premium, Net Premium, Ae Premium, Cg Premium and Photo Premium too. If you’re a Premium member, you can log in and download the tutorial. If you’re not a member, you can of course join today!
Also, don’t forget to follow @envatoactive on twitter and grab the Activetuts+ RSS Feed to stay up to date with the latest tutorials and articles.
It’s time for another debugging Quick Tip. We’ll continue our focus on specific (and common) errors that tend to stymie less-experienced ActionScripters. In this case, we’ll cover Error #1063, the argument count mismatch error.
Step 1: Being Bad
First, let’s create a situation where the error occurs. Open up a new Flash document (assuming you’re following along with Flash Pro CS3+, otherwise this sample is easily adapted to a Flex project). Open the Script Editor and enter the following code:
import flash.events.MouseEvent; function onClick():void { trace("click."); } stage.addEventListener(MouseEvent.CLICK, onClick);You can probably spot the problem already; if you’ve spent any amount of time writing ActionScript 3 you’ll have found yourself writing event handlers. If not, don’t feel bad — we’ll go over it all in due course.
If you run the Flash file as is, and then click on the stage, you’ll produce the following run-time error message:
And we never get to the trace statement that should have run after clicking.
Step 2: Breaking It Down
So what’s going on? In this case, Adobe’s verbiage for the error is actually not so bad, and if you’ve gotten used to parsing a run-time error message, its meaning might be pretty clear. But not everyone is as smart as you, so here’s the breakdown for everyone else.
ArgumentError — This is someone inconsequential information, but it shows the specific
Errorclass that was thrown. We’ve got something that’s not as general as a simpleError, and we’ve entered a specific categorization of error associated with arguments (to functions and methods).Error #1063 — Here we’re just giving the formal error number, like all good run-time errors. You can use this code to more easily locate it in Adobe’s run-time error documentation.
Argument count mismatch… — In more proletarian terms, there were the wrong number of arguments sent to a function. Which function? It’s…
…on Untitled_fla::MainTimeline/onClick(). — This simply identifies the function that received the wrong number of arguments.
Expected 0, got 1. — We get a bit of extra information in this error description. This details the count mismatch. This phrase will change according to the nature of the specific error, but in our case it’s saying that the function was written without any arguments in the signature, but a single argument got sent to it anyway.
Flash likes its ducks in a row. So, it notices this discrepancy and decides to throw a
tantrumerror, because it would rather you (the developer) figured out what went wrong than that you simply ignored the problem. This is good, because if the count mismatch went the other way (expected 1, got 0), then we’d be stuck without an argument for a required parameter, and the function would do Dog knows what.Step 3: The Root of the Problem
The nature of the error should be clear at this point, but you may still be wondering why it occurred at all. Where did that superfluous argument come from?
The argument isn’t exactly superfluous. It’s expected, actually, since we’ve hooked up our function to be an event listener. The event system in Flash has the notion of an event object that encapsulates aspects of the event that occurred. This object gets passed to the listener function as the sole argument. So, we expected 0 because we wrote our function without any parameters, but we got 1 because the event dispatcher sent along an event object.
Now you may be wondering why the compiler didn’t catch this error. It’s true: if you wrote this:
function sayClick():void { trace("click."); } sayClick(42);Then the SWF won’t even compile, because you’ll get this error:
The difference is that in the latter example, we have actual code that calls the function with the wrong number of arguments. That is, we wrote the line down that calls the function incorrectly. The compiler can look at the line that defines the function, and the line that calls the function, and compare them for discrepancies, and sound the alarm when they occur.
Image by Alan / Falcon
However, in the original example, there is no line of code written down that literally calls the function by name. Instead, the function is called by reference. When we add the event listener, we pass in the function, and at that point it’s a variable, not a function call. This reference gets stored by the event dispatcher, and then executed dynamically when the event occurs (that’s a real high-level overview of how the event system works, but we don’t have time to go deeper). So, the line of code that ultimately calls the error-causing function is a rather generic line of code that uses indirection to get the job done, and therefore something much harder for the compiler to catch.
(In my opinion, Adobe could at least register the
addEventListenerline at compile time, and go looking for the function reference by name. If it finds a match, it could check the function signature for a proper event argument, and produce errors accordingly. It still couldn’t be done in a foolproof way, but it might go a long way to catching these errors before actually running the SWF.The main point, though, is that this run-time error has a compile-time counterpart, but that the run-time error occurs when the function is called by reference and not directly by name.
Step 4: Fixing the Hole
The rain is getting in when we call the function by reference, and have a discrepancy in the number of arguments. We generally have two options: we can modify the call, or modify the function’s arguments. In this particular example, we can’t modify the call, as that happens inside
EventDispatcher, code to which we don’t have access. That leaves us with modifying the arguments.This, again, has two options. First, we can simply add the argument. This lines up the number of arguments and from here on out, everything will be copacetic. We don’t need to use the argument, we just need to have the function “catch” it when it’s called.
function onClick(e:MouseEvent):void {The second option is to, again, add the argument (no way around that, I’m afraid). However, if you originally wrote the function as a regular function and not an event listener, and are calling it from elsewhere in your code without arguments, you may appreciate this variation. Make the argument optional, and default it to
null:function onClick(e:MouseEvent=null):void {This will work well with the event system: it gets sent an event object and can catch it. It also works well with your existing code; if no argument is sent, the parameter default is used and the function proceeds.
Step 5: Callbacks
Note that this error is not limited to event listeners, although that’s probably the most common context where you’ll experience it. Ultimately it’s the using of functions stored in variables, as opposed to called by name, that leads to the error. This is how the event system works. We can rework the original example to produce more or less the same error, only without the click:
function sayMyName(name:String):void { trace("Hello, " + name); } var funcRef:Function = sayMyName; funcRef();Again, we get past the compiler error because we have a layer of indirection between the function definition and the function call. Thus, we get the run-time error (expected 1, got 0).
It’s not always this cut and dry, though. If you utilize callbacks in your code, you might fall prey to error 1063. Callbacks are sort of like event listeners, only there is no formal, built-in mechanism for implementing them. They’re basically just functions you pass around by reference, which are stored (either temporarily or long-term) by some other process, which then calls the callback function at the appropriate time.
Tweening engines typically implement these. Some go for a more formal event-driven system, but TweenLite, for example, utilizes callbacks for receiving notifications about the tween progress. This line:
TweenLite.to(someClip, 1, {onComplete:tweenFinished, onCompleteParams:[42, "answer"]});…would call a function named
tweenFinishedat the end of the tween, passing in two parameters to the function. This technique is ultimately more flexible then events, as you are not limited to just the single event object as a parameter. But it does yield itself to similar vulnerabilities to error 1063 due to the nature of passing functions around by reference.That Is All
That wraps up another Debugging Quick Tip. Thanks for reading, and I hope you learned something along the way!