8bitrocket.com
18May/102

HTML 5 Canvas: Using Processing.js To Develop Canvas Apps

This weekend, a friend of mine named Mike Foti noticed my pained expressions when I was talking about the HTML 5 Canvas, and he decided to help salve some of my injuries from my first few tests by pointing me towards a project named processing,js. processing.js is an MIT Media Labs project that began as a way to make the creation of Java Applets easier. It has been ported to work with the HTML 5 Canvas, and so far it looks promising.

The idea behind processing.js is to create a "programming language" that is used to create HTML 5 Canvas apps. If you think of the Canvas as a low-level display API, then processing.js is sort of like what ActionScript is to the SWF specification: it's an easier to use abstraction layer that assists in making applications.

If you look at the processing.js reference, you will see a lot of very useful looking functions that look like they will help take some of the pain put of creating Canvas applications. These include a lot of functions for mouse and keyboard support drawing graphics, etc., but also for application flow like  app loops and frame rates. However, one glaring omission is sound support. My friend had told me that he had seen a lot of Canvas apps that used processing.js on the web, and that makes a lot of sense to me. Since processing.js (or simply, Processing) has been around for a some time for Java, my inkling is that many Java developers have simply ported their Java Processing apps to the Canvas using processing.js. They make for stunning demos, but if they don't explore the Canvas at the API level are they missing something? The lack of sound in many of those demo apps may stem from the lack of support in processing.js, and not the lack of support in the Canvas (although support *is* lacking in the Canavs API). This is just a theory mind-you, but it seems like a fairly obvious situation to me.

Still curious, I sat down today and decided to port my "Gaudy Text Animation" HTML 5 demo from a couple days ago into processing.js. The results were mixed. While processing.js does appear to make some things simpler, it did not solve all my problems.

Here is a run-down of the conversion:

Support Processing.js In An HTML Page

The first thing you need to do when creating an app with processing.js is to include the library in the HTML page. After you have downloaded the current version, and place it somewhere that the HTML code can find. I simply put it in the same directory as my demo file. The following two pieces of code should be put in the section of your page.

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

[/cc] Next, you need to add support in JavaScript to get the processing interpreter running. This acts much like the Window load event we create with the native canvas API. [cc lang="javascript" width="550"]
[/cc]

(note: the above code was borrowed and modified from here: http://lethain.com/entry/2008/may/10/getting-started-with-processing-js/ )

In my previous HTML 5 Canvas examples I put all my code in the of the page. However, proper way to create an app with processing.js is to put the majority of the code in the . You do this by creating a
[cc lang="javascript" width="550"]


[/cc]

Application Loop

Now it is time to look at some code that is specific to processing.js. There are two main functions that you need to create for processing.js, both are called automatically.

  • setup(): is used to create the initial application settings
  • draw(): is used to redraw the screen on every frame

In setUp() you set -up things like the dimensions of Canvas, default colors, and the frame rate

[cc lang="javascript" width="550"]
void setup()
{
size(600, 150);
stroke(255);
frameRate(30);
}
[/cc];

In draw() you start making things happen. The most basic this you do, is set the background color (0=black). Also note that variables are declared Java-style, and can be declared outside of the functions. We will be using all of these variables to make the text appear. Notice that many of Phenobarbitals are the same as the ones we used in the "Gaudy Text Animation" HTML 5 demo.


[cc lang="javascript" width="550"]
String text1SizeDir = "up";
String text1Dir = "right";
int text1FontSize = 20;
int text1Xpos = 5;
String text1AlphaDir = "down";
float text1Alpha = 255;
void draw()
{
background(0);
}
[/cc]

Drawing Text Changes

While the setup and execution of a processing.js app differs from a standard Canvas app, the actual code to create the moving text is only slightly modified. This is because processing.js remains and "immediate mode" environment, and thus requires the entire screen to be repainted on every frame. However, in some ways it does feel like it is slightly easier to work with processing.js than the straight Canvas API. This text example might not be a good example of where one might be better than the other.

Drawing the text with processing.js looks like this:

[cc lang="javascript" width="550"]
font = loadFont("_sans");
textFont(font,text1FontSize);
fill(256, 0, 0) ;
text("Hello Canvas",text1Xpos,80);
[/cc]

Drawing the text with the Canvas API looked like this:

[cc lang="javascript" width="550"]
context.font = text1FontSize + 'px _sans';
context.fillStyle = '#FF0000';
context.textBaseline = 'top';
context.fillText ( text1 , text1Xpos, 80);
[/cc]

Note, the fill() function in processing .js is used to change the color of the text and apply an alpha transparency which we will do below.

Moving Text Changes

Moving the text across the screen is very similar in both the Canvas API and processing.js. The major difference comes when you apply the text to the screen (the last line of each example)

Moving the text with processing.js

[cc lang="javascript" width="550"]if (text1Dir == "right") {
text1Xpos += 10;

if (text1Xpos > 460) {
text1Dir = "left";
}
} else {
text1Xpos -= 10;

if (text1Xpos < 10) {
text1Dir = "right";
}

}
text("Hello Canvas",text1Xpos,80);
[/cc]

Moving the text with the Cavas API

[cc lang="javascript" width="550"]metrics = context.measureText(text1)
text1Width = metrics.width;
if (text1Dir == "right") {
text1Xpos += 10;

if (text1Xpos > 600-text1Width) {
text1Dir = "left";
}
} else {
text1Xpos -= 10;

if (text1Xpos < 10) {
text1Dir = "right";
}

}

context.fillText ( text1 , text1Xpos, 180);
[/cc]

Sizing Text Changes

Resizing the text to make it grow and shrink is also very similar in both processing.js and the Canvas API. The major difference is that you set the font and text size with two different calls with processing.js, while you do it with one function call with the Canvas API.

Sizing the text with processing.js

[cc lang="javascript" width="550"]if (text1SizeDir == "up") {
text1FontSize += 1;
if (text1FontSize > 40) {
text1SizeDir = "down"
}

} else {
text1FontSize -= 1;
if (text1FontSize < 20) {
text1SizeDir = "up"
}

}
font = loadFont("_sans");
textFont(font,text1FontSize);
[/cc]

Sizing the text with the Canvas API

[cc lang="javascript" width="550"]if (text1SizeDir == "up") {

text1FontSize += 2;

if (text1FontSize > 40) {
text1SizeDir = "down";
}
} else {

text1FontSize -= 2;

if (text1FontSize < 20) {
text1SizeDir = "up";
}

}
context.font = text1FontSize + 'px _sans';
[/cc]

Text Alpha Changes

Apply the alpha transparency to text is a bit easier with processing.js. Instead of setting a global context as you would with the Canvas API, you can apply the alpha transparency when you set the color of the text for drawing.

Text alpha fade with processing.js

[cc lang="javascript" width="550"]if (text1AlphaDir == 'up') {
text1Alpha +=16;
if (text1Alpha > 255) {
text1Alpha = 255;
text1AlphaDir = "down";
}
} else {
text1Alpha -=16;
if (text1Alpha < 0) {
text1Alpha = 0;
text1AlphaDir = "up";
}

}
fill(256, 0, 0,text1Alpha) ;
[/cc]

Text alpha fade with the Canvas API

[cc lang="javascript" width="550"]if (text1AlphaDir == 'up') {
text1Alpha +=.1;
if (text1Alpha > .9) {
text1AlphaDir = "down";
}
} else {
text1Alpha -=.1;
if (text1Alpha < .1) {
text1AlphaDir = "up";
}

}
context.globalAlpha = text1Alpha;
[/cc]

Text Shadow Changes

I could not find any built-in transformation for making a "shadow" in processing.js (I'm not saying it is not there, I just did not find it...yet), so I simply made two version of the text, one off center by 2 pixels for both x and y, and colored it a lighter color. We simply update both of these every frame instead of setting the global context for shadow as we do with the Canvas API.

Text Shadow with processing.js

[cc lang="javascript" width="550"]fill(200, 200, 200,text1Alpha);
text("Hello Canvas",text1Xpos+2, 82);
[/cc]

Text Shadow with the Canvas API

[cc lang="javascript" width="550"]context.shadowColor = '#DDDDDD';
context.shadowOffsetX = '2';
context.shadowOffsetY = '2';
context.shadowBlur = '2';
[/cc]

Full Code And Examples

See The Example of Gaudy Processing.js Text Demo

See the earlier Example of Gaudy Text Animation" HTML 5 demo using the Canvas API

Full code listing for processing.js Gaudy Text Demo:

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



[/cc]

15May/103

HTML 5 Canvas: Creating Gaudy Text Animations…Just Like Flash! (sort of)

Since I have not posted anything about the Canvas in a few weeks I wanted to show some of the experiments I've been working on. Everyone knows that animating text in Flash is very easy and has been around since the technology began. It seems to me, that if the Canvas is going be a "Flash killer", then it must have as robust, if not more-so features than Flash. You can read the Canvas text API here: http://dev.w3.org/html5/2dcontext/#text and see for yourself, but from my view it does not appear to have as many features as Flash at all. Still, I wanted to try it out and see what was possible. Here then is my demo of a HTML5 Canvas text animation.

http://www.8bitrocket.com/wp-content/uploads/2010/07/textblog_movetext.html

Yes, it is true. The HTML 5 Canvas can be used to created some of the same gaudy crap that Flash has been doing for years. Not all of what Flash can do, but some of it. Enough to be dangerous anyway. It's funny, the HTML guys have complained for years about "stupid Flash animations", yet now they have their own tool to do exactly the same thing. Will they practice restraint?

Code Breakdown

Here are the interesting bits of the code. I'm not going to talk about the basics of the canvas, you can get that from here: HTML 5 Canvas For Flash Developers #1: What The #@*! Is The HTML 5 Canvas? or here Exploring The HTML 5 Canvas For Flash Developers #2: Tracking The Mouse To Draw On The Screen.

Animation Loop

It's simple to put text on a Canvas, put animating it is another matter. The Canvas uses an immediate mode drawing paradigm which means he entire screen must be redrawn every time you want to move an object or objects. Unless you are a fan of blitting (we are!) most Flash developers are used to having the player keep track of their objects and redraw them on the screen on each frame. Because of this, they don't need to custom code an animation loop. With the HTML 5 Canvas, you need to create your own loop to have things move on the screen. The code to do this is in the box below. Here are the details of this code:

  • After the page loads (eventWindowLoaded() callback function) we use a setInterval function that should be very familiar to Flash developers (especially those versed in AS2). We set the interval to call appLoop() every 50 milliseconds.
  • The appLoop() only does one thing: calls drawScreen(). This is where you will code all of your "immediate mode" screen manipulations. Remember, there is no display list, so you will be redrawing EVERYTHING on EVERY call to drawScreen().
  • The clearScreen() function is called on every time we are going to redraw. It will perform two functions. It will draw the background, and it will reset and global transformations (i.e. alpha) so that they don't effect the drawing of the background.

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

...

setInterval("appLoop()", 50);

...

}

function appLoop() {
drawScreen();
}

function drawScreen() {

clearScreen();
}

function clearScreen() {
context.clearRect(0, 0, 600,400);
context.fillStyle = '#000000';
context.fillRect(0, 0, 600, 400);

}

[/cc]

Moving Text

Now we are going to move some text back and forth across the screen. The steps below describe the code we will create to do this:

  • First, we create a variable named text1Dir that we will use to tell us which direction the text is moving. We want to make sure that we can reverse direction of the text when it reaches the side of the screen.
  • Next we create a variable to hold the calculated x position of the text: text1Xpos;
  • We use a variable named text1 to describe the text that will be displayed: text1 = "Hello Canvas!";
  • In drawScreen() we will start adding code that will redraw the text in text1 every time the function is called.
  • We use the measureText() function of the 2D context to find the width of the text in text1. The function returns an instance of metrics that contain only one property(at this time), width.
  • We use this width to determine if the text has reached the right side of the canvas( (if (text1Xpos > 600-text1Width))) or the left side of the canvas( if (text1Xpos < 10) ).
  • If it has reached either side, we will switch directions using the text1Dir variable ("right" or "left")
  • If we have not reached either side, we will update the position of the text (text1Xpos) based on the direction in text1Dir: "right": (text1Xpos += 10;) or "left": (text1Xpos -= 10;)
  • Finally, we apply the new text to the screen:
    context.fillText ( text1 , text1Xpos, 180);

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

...

text1Dir ="right";
text1 = "Hello Canvas!";

...

}
function drawScreen() {

...

metrics = context.measureText(text1)
text1Width = metrics.width;

if (text1Dir == "right") {
text1Xpos += 10;

if (text1Xpos > 600-text1Width) {
text1Dir = "left";
}
} else {
text1Xpos -= 10;

if (text1Xpos < 10) {
text1Dir = "right";
}

}
context.font = '20px _sans';
context.fillStyle = '#FF0000';
context.textBaseline = 'top';
context.fillText ( text1 , text1Xpos, 180);

}

[/cc]

Text Size

We are going to use a similar set of code to that of the movement, for both sizing the text, and for creating the alpha fade on the text. The idea is that we keep track of the direction (for size it will be "up" or "down" instead of "right" and "left" for movement) and reverse accordingly when we reach a limit set in the code.

  • We create a variable to hold the "direction" of the resize: text1SizeDir = "up";
  • We create a variable to hold the font size of the text: text1FontSize=20;
  • In drawScreen() we use text1SizeDir to determine what the next font size for the text will be when we redraw it.
  • We apply the font size to the text like this: context.font = text1FontSize + 'px _sans';, simply appending the font size to the font declaration for context.

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

...

text1SizeDir = "up";
text1FontSize=20;

...

}

function drawScreen() {

...

if (text1SizeDir == "up") {

text1FontSize += 2;

if (text1FontSize > 40) {
text1SizeDir = "down";
}
} else {

text1FontSize -= 2;

if (text1FontSize < 20) {
text1SizeDir = "up";
}

}

...

context.font = text1FontSize + 'px _sans';

...

}
[/cc]

Text Alpha Transparency For Fading

Uses an Alpha value for fading the text is very similar to text size and movement except for one major fact: alpha is "global transition" and thus must be used with special care as it will apply to everything on the screen when it is redrawn. For this reason, we will need to add a line of code to the clearScreen() function that resets the alpha before we draw the background. like AS3, alpha is set between the numbers 1 and 0 (AS2 uses a percentage).

  • The text1AlphaDir variable is similar to text1SizeDir as it uses the value "up" and "down", however we will start it with the value "down" which signifies that we will start by fading the text.
  • In drawScreen() we will set context.globalAlpha just before we display the text on the screen. Everything draw after context.globalAlpha will be drawn with the value set for the alpha. It must be reset for anything that does not need the alpha value.
  • We will increment and decrement the alpha value by .1, or 10% each time we redraw the screen.
  • We reset the context.globalAlpha in clearScreen() to 1 so that the background will be drawn with no alpha fade.

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

...
text1AlphaDir = "down";
...

}

function drawScreen() {

...

if (text1AlphaDir == 'up') {
text1Alpha +=.1;
if (text1Alpha > .9) {
text1AlphaDir = "down";
}
} else {
text1Alpha -=.1;
if (text1Alpha < .1) {
text1AlphaDir = "up";
}

}
context.globalAlpha = text1Alpha;

...

}

function clearScreen() {

...
context.globalAlpha = 1;
....

}

[/cc]

Text Shadow

Finally, we get to the text shadow. Shadows are fairly easy to create. However, since they are set as part of the global context, you need to be careful how you use them. There are 4 attributes that need to be set to create a shadow:

  • context.shadowColor : The color of the shadow. #XXXXXX hex format is valid.
  • context.shadowOffsetX : The number of pixels to set the shadow on the x axis of the image.
  • context.shadowOffsetY : The number of pixels to set the shadow on the y axis of the image.
  • context.shadowBlur : The number of pixels to spread a blurring effect across both axis

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

...

context.shadowColor = '#DDDDDD';
context.shadowOffsetX = '2';
context.shadowOffsetY = '2';
context.shadowBlur = '2';

...
}
[/cc]

Full Code Listing

Here is the full code listing for this little gem:

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

Your browser does not support the HTML 5 Canvas.

[/cc]

There are more things that you can do with text, but most of them are manipulations of the global context. I'll report back when I have mastered that aspect of the canvas to make something worth showing. However, the point of this tutorial is to show that while some of the FX you can create with the HTML 5 Canvas are similar to those in Flash, creating them takes a lot more low level code than seems reasonable for a Flash designer. Obviously, tools will be created to build some of these things automatically (you can bet Adobe is on it right now), but those tools will still end-up writing out code much like this...all of it clearly visible in the HTML page.

1May/101

Tutorial: Exploring the AS3 Bitmap Class Lesson 3 – Scale from the center with a Matrix.

Tutorial: Exploring the AS3 Bitmap Class Lesson 3 - Scale from the center with a Matrix.

In this third lesson we will explore a method of scaling the object from Lesson 1 and Lesson 2 using a Matrix operation. Like rotation, we could simple apply the scaleX and scaleY properties of the Bitmap object, but that would result in the scale operation starting from the top-left corner rather than the center. We could also place the Bitmap inside a Sprite object, and move it to -.5* width for x and -.5*height for y and the scale the Sprite. The focus of these lessons though is working with the raw Bitmap object to eliminate the over-head of using the Sprite class as the Bitmap container. So, we will scale using a Matrix operation. After creating a new Matrix for this scale operation we will add in the dynamic tile sheet animation and rotation Matrix back into the code to demonstrate all of them working in conjunction.

We will be using the dynamic tile sheet we created in Lesson 1, so if you have not familiarized yourself with that lesson and find yourself getting lost it would be a good idea to go back and take a look. You might also want to examine the Matrix rotation operations we looked at in Lesson 2 as we will not cover that subject in detail here.

Lesson 3 will comprise parts 7,8, and 9 of this series.

Part 7: Scaling a Bitmap with a Matrix

Scaling a Bitmap object with a Matrix is very similar to rotating it with a Matrix. To ensure that we are scaling around the center point of the object we will need to "translate" it to -.5*width for x and -.5*height for y, then scale it, then move it back to is original position on the screen.

We will be using two new class variables for part 7.

[cc lang="javascript" width="550"]
private var bitmapScaleMatrix:Matrix;
private var bitmapScale:Number = 1;
[/cc]

The bitmapScaleMatrix is a Matrix class instance that will be used to apply our Matrix scale operations to the BitmapToDisplay object. The bitmapScale variable will hold the current scale of the BitmapToDisplay. This will be updated in each frame tick. It is important to note that we need this variable because we cannot use the scaleX and scaleY attributes of the BitmapToDisplay. This single variable replaces them both in our example. If you want to have separate x and y scales then you simply need a second variable.

Inside our runGame() function we will scale the object on each frame tick. We will start with a scale of 1 and increase it by .1 until it reaches 2 and then drop it back down to .1 to start over again. Similar to the rotation in Lesson 2, we need to call the identity() function on the bitmapScaleMatrix before on each frame tick to reset it before we apply the new scale.

[cc lang="javascript" width="550"]bitmapScaleMatrix.identity(); //resets the matrix

bitmapScaleMatrix.translate(-16,-16);
bitmapScaleMatrix.scale(bitmapScale, bitmapScale);
bitmapScaleMatrix.translate(startLocation.x+16, startLocation.y+16);
bitmapToDisplay.transform.matrix = bitmapScaleMatrix;
bitmapScale += .1;
if (bitmapScale > 2) {
bitmapScale = .1;
}

[/cc]

Let's step through this code:

1. We reset the Matrix with the identity() call.
2. We translate the so that the center of our 32x32 Bitmap is at the 0,0 origin. We do this with by translating to -16,-16
3. We call the scale() function of the Matrix and pass in the bitmapScale class variable for both the x scale and y scale.
4. We translate back to the original screen position for the Bitmap object plus the 16 by 16 pixels we used in the translation.
5. We apply the Matrix by setting the Bitmap.transform.matrix to = the bitmapScaleMatrix
6. We bump up of bitmapScale by .1 and check to make sure it is not larger than the maximum (2). If it is, we set it back to .1.

Here is the complete code for the "Part7" class. The new lines of code in part 7 are indicated and to remove the rotation and tile sheet animation we have commented out some lines. You don't need to add those in now, but we will be adding them in in parts 8 and 9.

[cc lang="javascript" width="550"]
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.geom.Transform

/**
* ...
* @author Jeff Fulton
*/

public class Part7 extends Sprite {
private var bitmapToDisplay:Bitmap;

private var bitmapDataToDisplay:BitmapData;

private var bitmapScrollRect:Rectangle;
//private var animationDelay:int = 5;
//private var animationCount:int = 0;

//private var bitmapRotation:int = 0;
//private var bitmapRotationMatrix:Matrix;
private var startLocation:Point = new Point(84, 84);

//*** new in part 7
private var bitmapScaleMatrix:Matrix;
private var bitmapScale:Number = 1;

public function Part7():void {
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}

private function init(e:Event = null):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
bitmapDataToDisplay = new BitmapData(64, 32, true, 0x00000000);
bitmapToDisplay = new Bitmap(bitmapDataToDisplay);
bitmapToDisplay.x = startLocation.x;
bitmapToDisplay.y = startLocation.y;
addChild(bitmapToDisplay);
createBitmapData();
//*** new in part 7
bitmapScaleMatrix= new Matrix();
//bitmapRotationMatrix= new Matrix();
bitmapScrollRect=new Rectangle(0, 0, 32, 32);
bitmapToDisplay.scrollRect = bitmapScrollRect;
bitmapToDisplay.smoothing = true; //makes it look better when rotates
addEventListener(Event.ENTER_FRAME, runGame,false,0,true);
}

private function createBitmapData():void {
//draw vertical line with setPixel32
for (var ctr:int = 4; ctr <= 27; ctr++) {
bitmapDataToDisplay.setPixel32(15, ctr, 0xff0066ff);
bitmapDataToDisplay.setPixel32(16, ctr, 0xff0066ff);
bitmapDataToDisplay.setPixel32(ctr, 26, 0xff0066ff);
bitmapDataToDisplay.setPixel32(ctr, 27, 0xff0066ff);
}
//copy ship to next tile
bitmapDataToDisplay.copyPixels(bitmapDataToDisplay,_
new Rectangle(0, 0, 32, 32), new Point(32, 0));
//add red thruster under the ship for this frame
for (ctr= 29; ctr <= 31; ctr++) {

bitmapDataToDisplay.setPixel32(47, ctr, 0xffff0000);
bitmapDataToDisplay.setPixel32(48, ctr, 0xffff0000);
}
}

private function runGame(e:Event):void {
bitmapScaleMatrix.identity(); //resets the matrix
bitmapScaleMatrix.translate(-16,-16);
bitmapScaleMatrix.scale(bitmapScale, bitmapScale);
bitmapScaleMatrix.translate(startLocation.x+16, startLocation.y+16);
bitmapToDisplay.transform.matrix = bitmapScaleMatrix;
bitmapScale += .1;
if (bitmapScale > 2) {
bitmapScale = .1;
}
//not needed in part 7

/*
animationCount++;
if (animationCount == animationDelay) {
animationCount = 0;
if (bitmapScrollRect.x == 0) {
bitmapScrollRect.x = 32;
}else {
bitmapScrollRect.x = 0;
}
bitmapToDisplay.scrollRect = bitmapScrollRect;
}
//rotate with a matrix
bitmapRotation += 1;
if (bitmapRotation > 359){
bitmapRotation = 0;
}
var angleInRadians:Number = Math.PI * 2 * (bitmapRotation / 360);

bitmapRotationMatrix.identity(); //resets the matrix

bitmapRotationMatrix.translate(-16,-16);
bitmapRotationMatrix.rotate(angleInRadians);
bitmapRotationMatrix.translate(startLocation.x+16, startLocation.y+16);

bitmapToDisplay.transform.matrix = bitmapRotationMatrix;

trace(bitmapToDisplay.x + "," + bitmapToDisplay.y + "," + bitmapRotation );
*/
//end not needed in part 7
}
}
}

[/cc]

Notice the long line under the //copy ship to next tile comment. This line has been broken with an "_" and should be reattached if you are going to cut and paste the code.

Here is the working swf file:

As you can see, the ship stays in its position and the scale starts from the center of the object. It loops though scale sizes .1 to 2 in increments of .1.

Part 8: Adding Dynamic Tilesheet Animation back into the mix

For this section we will simply be adding the animation with the scrollRect of the bitmapToDisplay back into the code. It is currently commented our in the runGame() function from part 7.

[cc lang="javascript" width="550"]
animationCount++;
if (animationCount == animationDelay) {
animationCount = 0;
if (bitmapScrollRect.x == 0) {
bitmapScrollRect.x = 32;
}else {
bitmapScrollRect.x = 0;
}
bitmapToDisplay.scrollRect = bitmapScrollRect;
}
}

[/cc]

For a refresher, here is the explanation from Lesson 1 part 3.

When then animationCount == animationDelay we will change the x value of the bitmapScrollRect. The value is either 0 (the start x of the first tile) or 32 (the start x of the second tile). It just jumps back and forth between these two values every 5 frames. This isn't the most sophisticated animation technique, but it works fine for this example.

Notice that if there is a change to the bitmapScrollRect, we have to re-apply it to the bitmapToDisplay.scrollRect. The scrollRect property of a display object cannot be changed by reference, it must be re-applied as needed.

All of the above code will be inside a simple runGame function that we will create. This function is called on each frame by a simple EnterFrame event.

Simpily uncomment those lines from the Part7.as class file and you will have the animation back in to the code. Here is the what the runGame function should now look like.
You can save this new changed version as Part8.as. Make sure to change the class name to Part8 and constructor name to Part8 in the code it you choose to do this.

[cc lang="javascript" width="550"]
private function runGame(e:Event):void {
bitmapScaleMatrix.identity(); //resets the matrix
bitmapScaleMatrix.translate(-16,-16);
bitmapScaleMatrix.scale(bitmapScale, bitmapScale);
bitmapScaleMatrix.translate(startLocation.x+16, startLocation.y+16);
bitmapToDisplay.transform.matrix = bitmapScaleMatrix;
bitmapScale += .1;

if (bitmapScale > 2) {
bitmapScale = .1;
}

animationCount++;
if (animationCount == animationDelay) {
animationCount = 0;
if (bitmapScrollRect.x == 0) {
bitmapScrollRect.x = 32;
}else {
bitmapScrollRect.x = 0;
}
bitmapToDisplay.scrollRect = bitmapScrollRect;
}

//not needed in part 8
/*
//rotate with a matrix
bitmapRotation += 1;
if (bitmapRotation > 359){
bitmapRotation = 0;
}

var angleInRadians:Number = Math.PI * 2 * (bitmapRotation / 360);

bitmapRotationMatrix.identity(); //resets the matrix

bitmapRotationMatrix.translate(-16,-16);
bitmapRotationMatrix.rotate(angleInRadians);
bitmapRotationMatrix.translate(startLocation.x+16, startLocation.y+16);

bitmapToDisplay.transform.matrix = bitmapRotationMatrix;

trace(bitmapToDisplay.x + "," + bitmapToDisplay.y + "," + bitmapRotation );
*/
//end not needed in part8
}

[/cc]

Here is the working swf file:

As you can see in the above example, we can simulate animation by changing the x value of the scrollRect property of a display object and at the same time we can scale the Bitmap object using the Matrix operations.

Part 9: Combining Scale and Rotation into a single Matrix

We covered the rotation portion in detail in the Lesson 2. To combine both rotation and scale into a single operation we will be creating a new single Matrix for both and will be removing the original two matrices we created: bitmapRotationMatrix and bitmapScaleMatrix. We will call the new Matrix the bitmapScaleRotationMatrix. In the variable definition section of the Part9.as file we will create this class level variable:

[cc lang="javascript" width="550"]
//*** new in part 9
private var bitmapScaleRotationMatrix:Matrix;
[/cc]

Since we only need a single Matrix for both we will have a single initial assignment in our init() function.

[cc lang="javascript" width="550"]
//*** new in part 9
bitmapScaleRotationMatrix= new Matrix();

[/cc]

Our runGame function will be completely new. It will combine the rotation and scale matrix operations with the existing dynamic tile sheet animation we uncommented in part 8.

[cc lang="javascript" width="550"]
private function runGame(e:Event):void {
animationCount++;
if (animationCount == animationDelay) {
animationCount = 0;
if (bitmapScrollRect.x == 0) {
bitmapScrollRect.x = 32;
}else {
bitmapScrollRect.x = 0;
}
bitmapToDisplay.scrollRect = bitmapScrollRect;
}

//rotate with a matrix
bitmapRotation += 1;
if (bitmapRotation > 359){
bitmapRotation = 0;
}

var angleInRadians:Number = Math.PI * 2 * (bitmapRotation / 360);

bitmapScaleRotationMatrix.identity(); //resets the matrix

bitmapScaleRotationMatrix.translate(-16,-16);
bitmapScaleRotationMatrix.rotate(angleInRadians);
bitmapScaleRotationMatrix.scale(bitmapScale, bitmapScale);
bitmapScaleRotationMatrix.translate(startLocation.x+16, startLocation.y+16);

bitmapToDisplay.transform.matrix = bitmapScaleRotationMatrix;

trace(bitmapToDisplay.x + "," + bitmapToDisplay.y + "," + bitmapRotation );

bitmapScale += .1;
if (bitmapScale > 2) {
bitmapScale = .1;
}
}

[/cc]

Here are the highlights of this combined rotation and scale code:

1. First, ignore the animation section as it was covered in Lesson 1
2. Take a look at Lesson 2 for detailed information on the rotation Matrix operation..
3. We start by calling he identity() method of the bitmapScaleRotationMatrix to reset it on each frame tick.
4. We do the now familiar translate operation once for both scale and rotate.
5. We rotate.
6. We scale. (these can be done in any order)
7. We translate back to the original position.
8. We the apply bitmapScaleRotationMatrix to our bitmapToDisplay

Here is the complete code for part 9.

[cc lang="javascript" width="550"]
package {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.geom.Transform

/**
* ...
* @author Jeff Fulton
*/
public class Part9 extends Sprite {

private var bitmapToDisplay:Bitmap;
private var bitmapDataToDisplay:BitmapData;

private var bitmapScrollRect:Rectangle;
private var animationDelay:int = 5;
private var animationCount:int = 0;

private var bitmapRotation:int = 0;
private var startLocation:Point = new Point(84, 84);

//*** new in part 9
private var bitmapScaleRotationMatrix:Matrix;
private var bitmapScale:Number = 1;

public function Part9():void {
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}

private function init(e:Event = null):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
bitmapDataToDisplay = new BitmapData(64, 32, true, 0x00000000);

bitmapToDisplay = new Bitmap(bitmapDataToDisplay);
bitmapToDisplay.x = startLocation.x;
bitmapToDisplay.y = startLocation.y;
addChild(bitmapToDisplay);

createBitmapData();

//*** new in part 9
bitmapScaleRotationMatrix= new Matrix();

bitmapScrollRect=new Rectangle(0, 0, 32, 32);
bitmapToDisplay.scrollRect = bitmapScrollRect;

bitmapToDisplay.smoothing = true; //makes it look better when rotates

addEventListener(Event.ENTER_FRAME, runGame,false,0,true);
}

private function createBitmapData():void {
//draw vertical line with setPixel32
for (var ctr:int = 4; ctr <= 27; ctr++) {
bitmapDataToDisplay.setPixel32(15, ctr, 0xff0066ff);
bitmapDataToDisplay.setPixel32(16, ctr, 0xff0066ff);
bitmapDataToDisplay.setPixel32(ctr, 26, 0xff0066ff);
bitmapDataToDisplay.setPixel32(ctr, 27, 0xff0066ff);
}

//copy ship to next tile
bitmapDataToDisplay.copyPixels(bitmapDataToDisplay, _
new Rectangle(0, 0, 32, 32), new Point(32, 0));

//add red thruster under the ship for this frame
for (ctr= 29; ctr <= 31; ctr++) {
bitmapDataToDisplay.setPixel32(47, ctr, 0xffff0000);
bitmapDataToDisplay.setPixel32(48, ctr, 0xffff0000);

}
}

private function runGame(e:Event):void {

animationCount++;
if (animationCount == animationDelay) {
animationCount = 0;
if (bitmapScrollRect.x == 0) {
bitmapScrollRect.x = 32;
}else {
bitmapScrollRect.x = 0;
}
bitmapToDisplay.scrollRect = bitmapScrollRect;
}

//rotate with a matrix
bitmapRotation += 1;
if (bitmapRotation > 359){
bitmapRotation = 0;
}

var angleInRadians:Number = Math.PI * 2 * (bitmapRotation / 360);

bitmapScaleRotationMatrix.identity(); //resets the matrix

bitmapScaleRotationMatrix.translate(-16,-16);
bitmapScaleRotationMatrix.rotate(angleInRadians);
bitmapScaleRotationMatrix.scale(bitmapScale, bitmapScale);
bitmapScaleRotationMatrix.translate(startLocation.x+16, _
startLocation.y+16);

bitmapToDisplay.transform.matrix = bitmapScaleRotationMatrix;

trace(bitmapToDisplay.x + "," + bitmapToDisplay.y + "," + bitmapRotation );

bitmapScale += .1;
if (bitmapScale > 2) {
bitmapScale = .1;
}
}
}
}

[/cc]

Notice the long lines like the one under the //copy ship to next tile comment. This line has been broken with an "_" and should be reattached if you are going to cut and paste the code.

Here is the working swf file:

In Lesson 4 we will start to create a BlitBitmap class that we can use standalone, or with in the Essential Flash Games Book framework.

14Apr/103

Tutorial: Exploring the AS3 Bitmap Class Lesson 2 – Rotation with and without a Matrix

Tutorial: Exploring the AS3 Bitmap Class Lesson 2 - Rotation and with and without a Matrix.

In this second lesson we will explore a couple different methods for rotating the Bitmap object from Lesson 1. We will first try simply using Bitmap.rotation property. Next we will do the same operation using a Matrix and finally we will add the dynamic tile-sheet animation to the Matrix rotation. Lesson 2 will be comprised on 3 separate parts (Parts 4,5,and 6 in this series).

We will be using the dynamic tile sheet we created in Lesson 1, so is you have not familiarized yourself with that lesson and find yourself getting lost it would be a good idea to go back and take a look.

Part 4: Rotating a Bitmap object directly

The Bitmap class (just like all display objects) has it's own rotation property. You would think that this would make rotating the contents of the object a trivial matter. In a sense, it is. You just simply set the rotation property to a new value

The problem you will see is that we cannot rotate the BitmapData object inside the Bitmap from any point other than the top-left corner. We will be adding a single line of code to the final code from Part 3 (inside the runGame) function:

[cc lang="javascript" width="550"]
//normal rotation will not work as it rotates on the top left corner
bitmapToDisplay.rotation += 10;

[/cc]

This code will simply rotate the Bitmap by 10 degrees on each frame tick. We will also be moving the bitmapToDisplay to a new position (84x,84y) so the object won't clip off the viewable screen during the rotation. We turn "smoothing" on to help the bitmap rotation not look jagged as the pixel locations are recalculated. This will be added to the init() method.

[cc lang="javascript" width="550"]bitmapToDisplay.smoothing = true;[/cc]

Here is the complete code for the "Part4" class. The new line of code is added to the end of the code in the Part3 runGame function.

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

package {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Point;
import flash.geom.Rectangle;

/**
* ...
* @author Jeff Fulton
*/
public class Part4 extends Sprite {

private var bitmapToDisplay:Bitmap;
private var bitmapDataToDisplay:BitmapData;

private var bitmapScrollRect:Rectangle;
private var animationDelay:int = 5;
private var animationCount:int = 0;

public function Part4():void {
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}

private function init(e:Event = null):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
bitmapDataToDisplay = new BitmapData(64, 32, true, 0x00000000);

bitmapToDisplay = new Bitmap(bitmapDataToDisplay);
bitmapToDisplay.x = 84;
bitmapToDisplay.y = 84;
addChild(bitmapToDisplay);

createBitmapData();
//makes it look better when rotates
bitmapToDisplay.smoothing = true;

bitmapScrollRect=new Rectangle(0, 0, 32, 32);
bitmapToDisplay.scrollRect = bitmapScrollRect;
addEventListener(Event.ENTER_FRAME, runGame,false,0,true);
}

private function createBitmapData():void {
//draw vertical line with setPixel32
for (var ctr:int = 4; ctr <= 27; ctr++) {
bitmapDataToDisplay.setPixel32(15, ctr, 0xff0066ff);
bitmapDataToDisplay.setPixel32(16, ctr, 0xff0066ff);
bitmapDataToDisplay.setPixel32(ctr, 26, 0xff0066ff);
bitmapDataToDisplay.setPixel32(ctr, 27, 0xff0066ff);
}

//copy ship to next tile
bitmapDataToDisplay.copyPixels(bitmapDataToDisplay, _
new Rectangle(0, 0, 32, 32), new Point(32, 0));

//add red thruster under the ship for this frame
for (ctr= 29; ctr <= 31; ctr++) {
bitmapDataToDisplay.setPixel32(47, ctr, 0xffff0000);
bitmapDataToDisplay.setPixel32(48, ctr, 0xffff0000);
}

}

private function runGame(e:Event):void {

animationCount++;
if (animationCount == animationDelay) {
animationCount = 0;
if (bitmapScrollRect.x == 0) {
bitmapScrollRect.x = 32;
}else {
bitmapScrollRect.x = 0;
}
bitmapToDisplay.scrollRect = bitmapScrollRect;
}

//normal rotation will not work as it rotates on the top left corner
bitmapToDisplay.rotation += 10;
}

}

}

[/cc]

Notice the long line under the //copy ship to next tile comment. This line has been broken with an "_" and should be reattached if you are going to cut and paste the code.

Here is the working swf file:

As you can see, if you want to simulate a space ship wildly spinning out of control this might be a good start but it isn't good for a rotation around the internal center point of the Bitmap. I am sure there are a few good applications where simulating the rotation from the top left point might work, but not for an Asteroids style ship rotation.

Part 5: Rotating the Bitmap on the Fly with a Matrix

In part 4 we rotated our Bitmap by updating it's standard rotation property. What we saw was a Bitmap that rotated around the top-left corner. Now, my normal method to get around this is to place the Bitmap inside a Sprite object and set its local coordinates to be -.5*width for x and -.5*height for y. When the Sprite is rotated, the Bitmap will rotate around it's center point. The one problem with this technique is that the actual top-left edge of the viewable Bitmaps now not at 0,0. This means you must offset some math calculations when using these objects (especially in tile-based games). Luckily, this is not the only way to accomplish this task. We can also use a combination of translation and rotation with Matrix object to simulate the same thing.

We will use the standard Matrix class, not the fl.motion.MatrixTransform class as the fl libraries are not part of the Flex SDK.

First let's look at the three new class level variables we will need for this operation:

[cc lang="javascript" width="550"]
private var bitmapRotation:int = 0;
private var bitmapRotationMatrix:Matrix;
private var startLocation:Point = new Point(84, 84);

[/cc]

The bitmapRotation variable will hold the current angle of rotation for the ship. It will range from 0-359 and will be used in the Matrix calculation to position the ship correctly on the screen. The bitmapRotationMatrix is a class level Matrix object instance that will be used to calculate and apply the center point rotation for the ship. We use a class level variable to avoid the overhead of creating a new Matrix object on each frame tick. The startLocation variable will be used as the upper left-hand corner location for the ship. We need to keep this in a separate variable from the Bitmap x and y coordinates as those will change based on the Matrix translation and rotation.

We will be instantiating this our Matrix in the init() function the Part5 class file.

[cc lang="javascript" width="550"]bitmapRotationMatrix= new Matrix();[/cc]

In our runGame function we will be commenting out the code that that does the animation with the scrollRect between the two dynamically created tile sheet sprite frames. We will add in the code that does the Matrix rotation of our ship. We will add the animation back in part 6, but for now we want to just demonstrate the rotation with out any other animation.

[cc lang="javascript" width="550"]
bitmapRotation += 1;
if (bitmapRotation > 359){
bitmapRotation = 0;
}

var angleInRadians:Number = Math.PI * 2 * (bitmapRotation / 360);

bitmapRotationMatrix.identity(); //resets the matrix

bitmapRotationMatrix.translate(-16,-16);
bitmapRotationMatrix.rotate(angleInRadians);
bitmapRotationMatrix.translate(startLocation.x+16, startLocation.y+16);

bitmapToDisplay.transform.matrix = bitmapRotationMatrix;

[/cc]

Let's step through this code:

1. First we update our bitmapRotation variable by 1 and check to see if it is greater than 359. If it is we set it back to 0. Now, this is not explicitly needed for this example as rotation will work without it. I use this because calculations with angles and especially look-up tables with vector calculations for angled movement are much easier to use with 0-359 as the angle of rotation.

2. We create a local Number variable called angleInRadians from the bitmapRotation value because the Matrix operation expects the angle to be represented in his manner.

3. We call the identity() method of the Matrix class. This resets the Matrix and allows us to feed new values into it on each frame tick. For some fun and weird effects, remove it and what what happens.

4. Now we get to the actual Matrix calculations. These function a little like a programming language all to itself.

First we call the translate() function and pass it -.5*with, -.5* height for the object. I just stuck 16 in here because it is a faster calculation. but you can calculate this on the fly with the width and height properties of the bitmapToDisplay object. This forces the rotation operation to occur at the center of the object rather than the upper left-hand corner.

Next we pass the newly calculated angleInRadians value into the rotate() method of the Matrix class. This will rotate the bitmapToDisplay object on the current x,y point, which is the center because of the translate() call above.

Finally, we must move the bitmapToDisplayObject back to its original position. We do this by using the startLocation Point instance's x and y coordinates (84,84) and adding the .5*width and .5*height to the respective startLocation Point values. Again, I applied the actual value (16) for speed of operation, but these very well could be calculated with the properties of the bitmapToDisplay object.

Here is the complete code for part 5:

[cc lang="javascript" width="550"]
package {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.geom.Transform

/**
* ...
* @author Jeff Fulton
*/
public class Part5 extends Sprite {

private var bitmapToDisplay:Bitmap;
private var bitmapDataToDisplay:BitmapData;

private var bitmapScrollRect:Rectangle;
private var animationDelay:int = 5;
private var animationCount:int = 0;

private var bitmapRotation:int = 0;
private var bitmapRotationMatrix:Matrix;
private var startLocation:Point = new Point(84, 84);

public function Part5():void {
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}

private function init(e:Event = null):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
bitmapDataToDisplay = new BitmapData(64, 32, true, 0x00000000);

bitmapToDisplay = new Bitmap(bitmapDataToDisplay);
bitmapToDisplay.x = startLocation.x;
bitmapToDisplay.y = startLocation.y;
addChild(bitmapToDisplay);

createBitmapData();

bitmapRotationMatrix= new Matrix();
bitmapScrollRect=new Rectangle(0, 0, 32, 32);
bitmapToDisplay.scrollRect = bitmapScrollRect;

bitmapToDisplay.smoothing = true; //makes it look better when rotates

addEventListener(Event.ENTER_FRAME, runGame,false,0,true);
}

private function createBitmapData():void {
//draw vertical line with setPixel32
for (var ctr:int = 4; ctr <= 27; ctr++) {
bitmapDataToDisplay.setPixel32(15, ctr, 0xff0066ff);
bitmapDataToDisplay.setPixel32(16, ctr, 0xff0066ff);
bitmapDataToDisplay.setPixel32(ctr, 26, 0xff0066ff);
bitmapDataToDisplay.setPixel32(ctr, 27, 0xff0066ff);
}

//copy ship to next tile
bitmapDataToDisplay.copyPixels(bitmapDataToDisplay, _
new Rectangle(0, 0, 32, 32), new Point(32, 0));

//add red thruster under the ship for this frame
for (ctr= 29; ctr <= 31; ctr++) {
bitmapDataToDisplay.setPixel32(47, ctr, 0xffff0000);
bitmapDataToDisplay.setPixel32(48, ctr, 0xffff0000);
}

}

private function runGame(e:Event):void {

/*
animationCount++;
if (animationCount == animationDelay) {
animationCount = 0;
if (bitmapScrollRect.x == 0) {
bitmapScrollRect.x = 32;
}else {
bitmapScrollRect.x = 0;
}
bitmapToDisplay.scrollRect = bitmapScrollRect;
}
*/

//rotate with a matrix
bitmapRotation += 1;
if (bitmapRotation > 359){
bitmapRotation = 0;
}

var angleInRadians:Number = Math.PI * 2 * (bitmapRotation / 360);

bitmapRotationMatrix.identity(); //resets the matrix

bitmapRotationMatrix.translate(-16,-16);
bitmapRotationMatrix.rotate(angleInRadians);
bitmapRotationMatrix.translate(startLocation.x+16, startLocation.y+16);

bitmapToDisplay.transform.matrix = bitmapRotationMatrix;

}

}

}

[/cc]

Notice the long line under the //copy ship to next tile comment. This line has been broken with an "_" and should be reattached if you are going to cut and paste the code.

Here is the working swf file:

Part 6: Adding the dynamic tile sheet sprite animation back into the mix

For this section we will simply be adding the animation with the scrollRect of the bitmapToDisplay back into the code. It is currently commented out in the runGame() function from part 5:

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

animationCount++;
if (animationCount == animationDelay) {
animationCount = 0;
if (bitmapScrollRect.x == 0) {
bitmapScrollRect.x = 32;
}else {
bitmapScrollRect.x = 0;
}
bitmapToDisplay.scrollRect = bitmapScrollRect;
}
}

[/cc]

For a refresher, here is the explanation from lesson 1 part 3.

When then animationCount == animationDelay we will change the x value of the bitmapScrollRect. The value is either 0 (the start x of the first tile) or 32 (the start x of the second tile). It just jumps back and forth between these two values every 5 frames. This isn't the most sophisticated animation technique, but it works fine for this example.

Notice that if there is a change to the bitmapScrollRect, we have to re-apply it to the bitmapToDisplay.scrollRect. The scrollRect property of a display object cannot be changed by reference, it must be re-applied as needed.

All of the above code will be inside a simple runGame function that we will create. This function is called on each frame by a simple EnterFrame event.

Simpily uncomment those lines from the Part5.as class file and you will have the animation back in to the code.

Here is the working swf file:

As you can see in the above example, we can simulate animation by changing the x value of the scrollRect property of a display object and at the same time we can rotate the Bitmap object using the Matrix operations.

In the next lesson we will cover scale transformations on our bitmapToDisplay object.

12Apr/101

Tutorial: Exploring the AS3 Bitmap Class Lesson 1 – Creating a basic dynamic tile sheet.

Tutorial: Exploring the AS3 Bitmap Class Lesson 1 - Creating a basic dynamic tile sheet.

We talk a lot about Blitting and BitmapData on this site, but we rarely take a step back and look at the container class for BitmapData, the Bitmap. In this series we will explore using the Bitmap class for many different applications and manipulate the BitmapData attribute to create animation and transformations. The end result of all the lessons will be a new class that we will add to the Essential Flash Games framework. With the AS3 Bitmap class we have a display object that we can manipulate to display BitmapData. This allows us to make use of the built-in features of a Flash Display object, but also the power of manipulating individual pixels of data for speedy rendering.

Lesson 1 will consist of parts 1-3 of the Bitmap class exploration. First we will take a look at dynamically drawing a simple shape on a Bitmap canvas. Next we will create a dynamic tile sheet using the BitmapData attribute of the Bitmap, and lastly we will create a simple animation using the dynamic tile sheet. Let's get started.

Part 1: Drawing pixels on a Bitmap canvas

The Bitmap class contains a BitmapData attribute. This provides the look of the Bitmap and allows for us to take control of the content of the Bitmap programmatically. The first thing we are going to do is draw a very simple "space ship" as a 32x32 image. The ship will look like this:

ship1

The "ship" we will create is a very simple upside down "T". This is a 32x32 png file with a transparent background. The ship does not touch the edges of the 32x32 size, but rather there is a 4 pixel buffer around the entire image. I created this one for reference only as it is a simple shape directly onto the bitmapData attribute inside our bitmap.

We will create a bitmap for our part1 code called: private var bitmapToDisplay:Bitmap;

We will also create a BitmapData object to hold the look of the ship: private var bitmapDataToDisplay:BitmapData;

We will draw the pixels of our ship by setting the color of the individual pixels of the bitmapDataToDisplay. Since we have some straight lines (2 pixels thick) that are symmetrical, we can create a very simple loop to draw our ship. We will use the setPixel32 function of the BitmapData:

[cc lang="javascript" width="550"]
for (var ctr:int = 4; ctr <= 27; ctr++) {
bitmapDataToDisplay.setPixel32(15, ctr, 0xff0066ff)
bitmapDataToDisplay.setPixel32(16, ctr, 0xff0066ff)
bitmapDataToDisplay.setPixel32(ctr, 26, 0xff0066ff)
bitmapDataToDisplay.setPixel32(ctr,27,0xff0066ff)
}

[/cc]

In this loop we create a vertical line from 4y to 27y at both pixel 15x and 16x (for a double thickness) and we create a horizontal line from 4x to 27x at pixels 26y and 27y. This mimics the png of the ship created above. Obviously this is probably the most simple space ship ever drawn, but it demonstrates how to manipulate pixels "on-the-fly" and will be the basis for much of the further discussion. Feel free to change up the look to meet your specific aesthetic desires (if it helps you make it through these tutorials, more power to ya!).

Here is the complete code for the "Part1" class.

[cc lang="javascript" width="550"]
package {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;

/**
* ...
* @author Jeff Fulton
*/
public class Part1 extends Sprite {
private var bitmapToDisplay:Bitmap;
private var bitmapDataToDisplay:BitmapData;

public function Part1():void {
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}

private function init(e:Event = null):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
bitmapDataToDisplay = new BitmapData(32, 32, true, 0x00000000);
bitmapToDisplay = new Bitmap(bitmapDataToDisplay);
addChild(bitmapToDisplay);
createBitmapData();
}

private function createBitmapData():void {
//draw vertical line with setPixel32
for (var ctr:int = 4; ctr <= 27; ctr++) {
bitmapDataToDisplay.setPixel32(15, ctr, 0xff0066ff)
bitmapDataToDisplay.setPixel32(16, ctr, 0xff0066ff)
bitmapDataToDisplay.setPixel32(ctr, 26, 0xff0066ff)
bitmapDataToDisplay.setPixel32(ctr,27,0xff0066ff)
}
}
}

}

[/cc]

Here is the working swf file:

Part 2: Drawing a second frame on the same BitmapData to create a dynamic tile sheet

In part 1 we we created a single BitmapData instance called bitmapDataToDisplay and drew our simple space ship on it. In part 2 we will extend the size of this BitmapData from the original width of 32 pixels to a new width of 64 pixels. We will then draw the second frame of our simple space ship in what we will consider tile 2 of our tile sheet. Here is the code we will change in the init() function to make the width of the BitmapData 64 rather than 32:

bitmapDataToDisplay = new BitmapData(64, 32, true, 0x00000000);

Here is the ship image we will draw starting in pixel 32x and finishing in pixel 63x.

ship2

Again, the .png file is just for reference as we are going to be drawing into the BitmapData directly for this lesson. We can imbed the png in the IDE's library or at compile-time if we want to, but for this lesson we'll just use the dynamically drawn ship. The only difference between the ship1 and ship2 images is the small red "exhaust" at bottom of the "ship". This is the second frame of animation that would conceivably be used to simulate the ship thrusting through virtual space.

To draw this second frame into the BitmapData we will offset the original ship drawing by 32. The easy way to accomplish this is to copy the pixels from the first frame and drop them onto the second frame. We will use the BitmapData.copyPixels function to do this:

[cc lang="javascript" width="550"]
//copy ship to next tile
bitmapDataToDisplay.copyPixels(bitmapDataToDisplay, new Rectangle(0, 0, 32, 32), new Point(33, 0));
[/cc]

We pass a reference to the source for the copy (itself), a Rectangle representing the area to copy and a Point representing the upper left-hand corner to start copying the pixels. This draws a copy of the exact original ship pixels to what we will consider "frame 2" of our dynamically created tile sheet.

Next we will draw the red exhaust:

[cc lang="javascript" width="550"]
//add red thruster under the ship for this frame
for (ctr= 29; ctr <= 31; ctr++) {
bitmapDataToDisplay.setPixel32(48, ctr, 0xffff0000);
bitmapDataToDisplay.setPixel32(49, ctr, 0xffff0000);
}

[/cc]

Similar to how we created the

double blue lines for the original ship image, we create a 2 pixel width red line under the ship for the exhaust.

Here is the complete code for Part 2. It is very similar to Part 1 with some minor changes to accommodate the size of the BitmapData (extended to 64 pixel width) and the new drawing code.

[cc lang="javascript" width="550"]
package {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Point;
import flash.geom.Rectangle;

/**
* ...
* @author Jeff Fulton
*/

public class Part2 extends Sprite {

private var bitmapToDisplay:Bitmap;
private var bitmapDataToDisplay:BitmapData;

public function Part2():void {
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}

private function init(e:Event = null):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point

bitmapDataToDisplay = new BitmapData(64, 32, true, 0x00000000);
bitmapToDisplay = new Bitmap(bitmapDataToDisplay);
addChild(bitmapToDisplay);

createBitmapData();
}

private function createBitmapData():void {
//draw vertical line with setPixel32

for (var ctr:int = 4; ctr <= 27; ctr++) {
bitmapDataToDisplay.setPixel32(15, ctr, 0xff0066ff);
bitmapDataToDisplay.setPixel32(16, ctr, 0xff0066ff);
bitmapDataToDisplay.setPixel32(ctr, 26, 0xff0066ff);
bitmapDataToDisplay.setPixel32(ctr, 27, 0xff0066ff);
}

//copy ship to next tile
bitmapDataToDisplay.copyPixels(bitmapDataToDisplay, _
new Rectangle(0, 0, 32, 32), new Point(33, 0));

//add red thruster under the ship for this frame
for (ctr= 29; ctr <= 31; ctr++) {
bitmapDataToDisplay.setPixel32(48, ctr, 0xffff0000);
bitmapDataToDisplay.setPixel32(49, ctr, 0xffff0000);
}
}
}

}

[/cc]

Notice the long line under the //copy ship to next tile comment. This line has been broken with an "_" and should be reattached if you are going to cut and paste the code.

Here is the working swf file:

As you can see, the entire BitmapData is displayed. While this is a good way to display an entire bitmap for an example, it doesn't help us animate the the ship. Let's take a look at how to do a classic "flip-book" animation of the ship next. We will do this by manipulating the Bitmap.scrollRect property.

Part 3: Animating the dynamic tile sheet with a scrollRect

The Bitmap.scrollRect property is used to define the viewable portion of the Bitmap object. It acts a little like a "mask" in that the rectangle will show through and everything else will be blocked out.

We will need to create a class level variable to hold the current Rectangle bounds that we want to display on our Bitmap. We will create a new variable to hold this called:

private var bitmapScrollRect:Rectangle;

In our init() function we will set the size of this to be 32x32:

bitmapScrollRect=new Rectangle(0, 0, 32, 32);

We will also apply it to the scrollRect property of the bitmapToDisplay.

bitmapToDisplay.scrollRect = bitmapScrollRect;

This is only good for the first frame as this will need to be re-applied each time we need to show a different part of the dynamic tile sheet. It does not work like most references where if we change the bitmapScrollRect's x value the bitmapToDisplay.scrollRect is also updated. We need to re-apply the assignment each time we change the x location of the bitmapScrollRect Rectangle.

We will update the bitmapScrollRect's x and y properties to change the location on the dynamic tile sheet we created. This will simulate animating the "thruster" at the bottom of our little space ship. We will create and EnterFrame event and use a counter for this animation. We will create two class level variables for the animation frame counting:

private var animationDelay:int = 5;

private var animationCount:int = 0;

The animationCount will be increased by one on each frame tick. When the count reaches the animationDelay the bitmapScrollRect will be updated. Here is the code that will accomplish this:

[cc lang="javascript" width="550"]
animationCount++;
if (animationCount == animationDelay) {
animationCount = 0;
if (bitmapScrollRect.x == 0) {
bitmapScrollRect.x = 32;
}else {
bitmapScrollRect.x = 0;
}
bitmapToDisplay.scrollRect = bitmapScrollRect;
}

[/cc]

When then animationCount == animationDelay we will change the x value of the bitmapScrollRect. The value is either 0 (the start x of the first tile) or 32 (the start x of the second tile). It just jumps back and forth between these two values every 5 frames. This isn't the most sophisticated animation technique, but it works fine for this example.

Notice that if there is a change to the bitmapScrollRect, we have to re-apply it to the bitmapToDisplay.scrollRect. The scrollRect property of a display object cannot be changed by reference, it must be re-applied as needed.

All of the above code will be inside a simple runGame function that we will create. This function is called on each frame by a simple EnterFrame event.

Here is the complete code for Part 3. It is very similar to Part 2 with addition of the new animation variables and the runGame function for controlling the animation.

[cc lang="javascript" width="550"]
package {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Point;
import flash.geom.Rectangle;

/**
* ...
* @author Jeff Fulton
*/
public class Part3 extends Sprite {

private var bitmapToDisplay:Bitmap;
private var bitmapDataToDisplay:BitmapData;

private var bitmapScrollRect:Rectangle;
private var animationDelay:int = 5;
private var animationCount:int = 0;

public function Part3():void {
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}

private function init(e:Event = null):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
bitmapDataToDisplay = new BitmapData(64, 32, true, 0x00000000);

bitmapToDisplay = new Bitmap(bitmapDataToDisplay);
addChild(bitmapToDisplay);

createBitmapData();

bitmapScrollRect=new Rectangle(0, 0, 32, 32);
bitmapToDisplay.scrollRect = bitmapScrollRect;
addEventListener(Event.ENTER_FRAME, runGame,false,0,true);
}

private function createBitmapData():void {
//draw vertical line with setPixel32
for (var ctr:int = 4; ctr <= 27; ctr++) {
bitmapDataToDisplay.setPixel32(15, ctr, 0xff0066ff);
bitmapDataToDisplay.setPixel32(16, ctr, 0xff0066ff);
bitmapDataToDisplay.setPixel32(ctr, 26, 0xff0066ff);
bitmapDataToDisplay.setPixel32(ctr, 27, 0xff0066ff);
}

//copy ship to next tile
bitmapDataToDisplay.copyPixels(bitmapDataToDisplay, _
new Rectangle(0, 0, 32, 32), new Point(32, 0));

//add red thruster under the ship for this frame
for (ctr= 29; ctr <= 31; ctr++) {
bitmapDataToDisplay.setPixel32(47, ctr, 0xffff0000);
bitmapDataToDisplay.setPixel32(48, ctr, 0xffff0000);
}
}

private function runGame(e:Event):void {

animationCount++;
if (animationCount == animationDelay) {
animationCount = 0;
if (bitmapScrollRect.x == 0) {
bitmapScrollRect.x = 32;
}else {
bitmapScrollRect.x = 0;
}
bitmapToDisplay.scrollRect = bitmapScrollRect;
}
}
}

}

[/cc]

Again, notice the long line under the //copy ship to next tile comment. This line has been broken with an "_" and should be reattached if you are going to cut and paste the code.

Here is the working swf file:

As you can see in the above example, we can simulate animation by changing the x value of the scrollRect property of a display object. This acts like a "mask" and leaves the rest of the dynamically created tile sheet hidden.

In the next lesson we will cover some rotation transformations of the Bitmap object using a Matrix.

11Apr/100

Exploring The HTML 5 Canvas For Flash Developers #2: Tracking The Mouse To Draw On The Screen

After a week where confusion about the future of the Flash platform has been piled on to developers by moves outside their control, we present the second in our series of HTML 5 Canvas for Flash Developers.  We call these tutorials, but really they are more like explorations.  We are learning this thing at the same time you are learning it.  hopefully these will be beneficial in the long run.

Drawing On The HTML 5 Canvas

One of the most basic things that can be accomplished with an HTML Canvas is drawing with the mouse. In this tutorial we will quickly show you the code for achieving this in HTML 5 with a Canvas element. We are not saying this is the best or most efficient way to do it, just one way to tackle the problem.

Creating A Simple Flash-like"Trace" Window In HTML

The first thing that will drive Flash developers crazy when working in browser JavaScript is the lack of good, out of the box, debugging tools for the web browser. You can install things like Firebug, or use an "alert" box, but we have a found a much simpler, easier way to create a "Flash-like" trace() in JavaScript, on the same page as the Canvas. It is very basic and involves a


[/cc]

 

Test the code.

Flash Developer Digestion

Did you notice something about this code? We never created any objects or MovieClips or Sprites or any other high level object to define the screen, the background, or anything we drew. Drawing the way we did in the is example is akin to using an old "plotter" printer. We kept adjusting the "print head" the moveTo() functions, and then comitted them to "paper" with the lineTo(); and stroke(); This is the method we will use as we continue these tutorials. We will think of the Canvas one a piece of paper that we either continuously draw to (like this example) or needs to be completed erased and redrawn every time time we need to change something (like when you move a sprite in a game).

So you can now see, the HTML 5 Canvas is really just a low-level display area for you to draw on to. In the next lesson we will start adding and tracking bitmap images.

 

 

 

3Mar/1012

Tutorial: AS3: How to Blit an animation from a tile sheet embedded at compile time

Tutorial: AS3: How to Blit an animation from a tile sheet embedded at compile time

Today we are going to look at how to use a tile sheet embedded into an AS3 project at compile time.We will cover the embedding, instantiation, and actual usage of the tile sheet to do a simple blit canvas animation. I am currently working on a scrolling re-make of the classic Atari 2600 game, Sky Diver. I have started to create my own simple 8-bit styled tile sheet for the game. I only have the first three preliminary tiles created for the player sprite's free-fall animation. Here is the tile sheet so far:

skyKingGraphics.png

 The sheet is actually 10 tiles wide (320 pixels) by 2 tiles high (64 pixels). The tiles are 32x32 in size. We are only using three tiles so far, but there is room for many more if we need them. We will start to build a simple class that will demonstrate using this sheet for animating the character using a classic blit technique. This technique will use a background fill to wipe clean the entire screen between animation frames. Much of the theory behind this was covered in this basic blitting tutorial, so I will not repeat it here. What we will do is go through this new example line by line and give you a good understanding of this current implementation.

1. Let's start with the class import section of our class.

[cc lang="javascript" width="550"]
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Point;
import flash.geom.Rectangle;

/**
* ...
* By Jeff Fulton 2010
* ...
*/

[/cc]

 

In the above code we are importing the Bitmap class as a display object for our BitmapData blit canvas. The demo will be contained in a Sprite instance that will have the Bitmap added to its display list. We will be running an EnterFrame event that will use the generic Event class in its listener function. This EnterFrame Event will be used to simulate the frame by frame tile-based animation of our game character. The Point and Rectangle classes are needed for our blitting operations.

2. Now we will take a look at the variable definition section for our class. This will include the embed compiler directive for our tile sheet.

[cc lang="javascript" width="550"]
public class TileSheetEmbed extends Sprite{
[Embed(source = 'skyKingGraphics.png')]
private var TileSheet:Class;

private var backGroundBitmapData:BitmapData =
new BitmapData(100, 100, false, 0x000000);

private var canvasBitmapData:BitmapData =
new BitmapData(100, 100, false, 0xffffff);

private var canvasBitmap:Bitmap = new Bitmap(canvasBitmapData);
private var tileCounter:int = 0;
private var tileList:Array = [0,0,0 ,1,1,1, 2,2,2];;
private var tileSheet:Bitmap=new TileSheet();;
private var backBlitPoint:Point = new Point(0,0);
private var tileBlitPoint:Point = new Point(20,20);
private var blitRect32:Rectangle = new Rectangle(0, 0, 32, 32);
private var tileWidth:int = 32;
private var tileHeight:int = 32;
private var tilesPerRow:int = tileSheet.width / tileWidth;

[/cc]

 

(We had to break a couple lines for space reasons, so make sure you re-attach them. I have added extra blank lines around them for emphasis)

Notice that our class extends Sprite. It can extend MovieClip, but there is no need since we only have a single dynamically created frame for this example. We actually take care of a lot of business in this code section, so let's take a closer brief look at each of the variables.

First we embed the "skyKingGraphics.png" file as a class named TileSheet. Bitmap files are embedded into the swf as instances of the BitmapAsset class (a sub class of Bitmap) and not as BitmapData instances as in the Flash Library.

Next we create the backGroundBitmapData instance that will act as both the black background for our demo and as the "eraser" between blit operations. The canvasBitmapData will be the actual viewable blit canvas. It will be added to the display list inside the canvasBitmap instance. Remember, BitmapData instances cannot be attached directly to Sprite or MovieClip instances (as in AS2).

Our animation has three frames that will play in order repeatedly. We are copied them each into the array three times each (in a row) to create a rudimentary delay between tiles. This helps to slow down the animation and not let it run too fast. There are other more elegant methods to do the same procedure (such as creating a delay variable), but this gets the job done just fine for the demo. The tileList Array instance holds the tile numbers from the tile sheet that we want to play in order. Notice there that this is where we have three of each tile id number. This will allow us to easily change the tiles to display, and the order in which they display, etc. We would just need to change the tile id numbers in this array. The tileCounter represents the index on this array to display. Our current tile sheet has 20 tiles available. The top left tile is tile id 0 and the bottom right is tile 19. The tile numbers go from left to right and then down and back to the left again. So, the right most tile on the first row would be tile id 9 while the left most tile on row 2 would be tile 10 and so on. The only three tiles that have characters in them right now are tiles 0, 1, and 2.

The tileSheet variable is an instance of the TileSheet class that we embedded.

For the blitting operations we have set up a reusable set of Point instances at the class level. We do this so we don;t waste memory and processor time re-instantiating these two Point instances on each frame for the two blitting operations. The backBlitPoint (0,0) represents the top left x, y coordinates that out backGroundBitmapData will start copying to the canvasBitmapData. The tileBlitPoint (20,20) represents the top left corner of the where our 32x32 character will start copying to the canvasBitmapData.

The blitRect32 represents a 32x32 Rectangle on the tileSheet where we will find the pixels we want to copy to our canvas (starting at the tileBlitPoint). The x and y coordinates of this Rectangle will be changed on each frame we want to display a new tile from the tileSheet.. Note that the y coordinate will not change in this example because all three of our tiles are on the first row.

The tileWidth and tileHeight are both 32. These much match the actual tile width and height on our tile sheet. The tilesPerRow variable is used in our blitRect32 calculation to determine what row we will need to blit from our tile sheet. In this example we will always be on the first row, but our code can support any number of rows of tiles.

3. Next we will take a look at our constructor function:

[cc lang="javascript" width="550"]
public function TileSheetEmbed() {
addChild(canvasBitmap);
addEventListener(Event.ENTER_FRAME, runDemo, false, 0, true);
}

[/cc]

 

The constructor function is very simple. We add the canvasBitmap to the display list and then set up our animation loop with a ENTER_FRAME event.

4. The runDemo function

[cc lang="javascript" width="550"]
private function runDemo(e:Event):void {
canvasBitmapData.lock();

canvasBitmapData.copyPixels(backGroundBitmapData,
backGroundBitmapData.rect, backBlitPoint);

blitRect32.x=int(tileList[tileCounter]% tilesPerRow)*tileWidth;
blitRect32.y=int(tileList[tileCounter] / tilesPerRow)*tileHeight;

canvasBitmapData.copyPixels(tileSheet.bitmapData,
blitRect32, tileBlitPoint);

canvasBitmapData.unlock();
tileCounter++;
if (tileCounter > tileList.length-1) {
tileCounter = 0;
}
}
}
}

[/cc]

(We had to break a couple lines for space reasons, so make sure you re-attach them. I have added extra blank lines around them for emphasis)

The runDemo function starts by locking our canvasBitmapData instance. This keeps the canvasBitmap instance from drawing the canvasBitmapData to the output screen until the unlock method is called. We first blit the entire 100x100 black backGroundBitmapData to the canvasBitmapData using the backBlitPoint (0,0) as the location to start the copyPixels operation.

Next we calculate the top left-hand position on the tileSheet to start our copy of the game character. This is done using the current tile id number (0, 1 or 2) in the tileList array. The blitRect32 will represent the 32x32 rectangle that encompasses the picture on the tile sheet that we want to copy to the canvasBitmapData. It is modified each frame based on the tile id # in the tileList Array that we want to display. We use the tileCounter variable as the array index.

We now have the information to cut a 32x32 piece of our tileSheet and copy it to the canavsBitmapData. This is done with the copyPixels operation. We pass in the tileSheet as the source of the image data, as well as the blitRect32 that represents the rectangle of pixels to copy, and we also pass in the tileBlitPoint (which is 20,20) as the location to place the copied pixels on our canavsBitmapData. We unlock the canvasBitmapData and this allows the canvasBitmap instance to refresh itself with the new canvasBitmapData.

The final operation is to update the tileCounter so we will show so the next animation tile in the tileList array will display on the next frame tick.

That's all there is to it.

Here is a version running. It's not too pretty, but it gets the job done. He kind of looks like is a directing traffic on acid, but I'll work on improving it before I finish the game.

23Feb/102

HTML 5 Canvas For Flash Developers #1: What The #@*! Is The HTML 5 Canvas?

In this set of tutorials, we will explore how the HTML Canvas relates to Flash developers.

So, the first question to answer about the HTML 5 Canvas appears to be this:

"What The #@*! Is The HTML 5 Canvas?"

The short answer is this:

It is an area on a web page set-aside to display and manipulate bit-mapped pixels in "immediate" mode. "Immediate" mode is a way to draw pixels that gives a programmer ultimate control, but requires the screen objects to be tracked and be redrawn entirely in code. You create it with the HTML tag, and you manipulate it with JavaScript. The Canvas is a window into a bitmapped world that, until recently, only existed with plug-ins like Silverlight , Java ,and of course Flash. The Canvas is currently supported (in part) in the latest versions of Firefox, Opera, and Chrome, but not Internet Explorer

The tag was created by Apple initially to support their OSX widgets, and as extension to Safari. The tag was recently adopted as part of the HTML 5 spec after being pushed by both Apple and Google. The HTML 5 spec has many parts besides the , and many of them should seem familiar to Flash developers:

  • H.264 video support (Flash has this already, had it since 2007. Oh, and by the way, Apple's Steve Jobs can go on and on about Flash being proprietary...even though the SWF format is open, but at the same time his love for H.264 makes no sense, as it is CLOSED, proprietary format! )
  • Local storage (more like a Flash shared object, not a Cookie)
  • SVG support: SVG is a vector graphics engine for web browsers. Yep, Flash has had that for a long, long time too.

Since the works in "immediate" mode, it does not have a Display List built-in, like Flash AS2/AS3 (which operate on a "Retained" mode, but can be made to work in "immediate" mode by going right to bitmaps themselves). Instead, you must keep track of all objects and their movement yourself. That means that very simple things that you are used to in Flash, are simply not available in the unless you build support for them yourself. For example, There is no easy way to create tweens, except to create them all in-code. The good news for hardcore programmers, is that sheds a lot of layers and forces you to write clean display code from the get-go. Blitting engines should be par for the course because they will be the an efficient way to display graphics on the Canvas. However, for the legions of designers and designers/programmers who use Flash on a daily basis to create interactive (but not hardcore) web content, the state of the Canvas is a freaking nightmare. While there are a lot of promising tools for JavaScript (i.e. http://processingjs.org/     http://www.ambiera.comcopperlicht/ -thanks Paul) to help c  no tools currently exist that will make the process of creating apps or animations easy for designers. In a few months, when producers at over-zealous agencies have sold their big-name clients on this "Flash replacement that works on the iPad", design and development teams will suffer long and troublesome hours trying to make their claims a reality.

Things For Flash Developers To Note About the HTML 5 Canvas

For Flash developers, a few things stick out as interesting about the .

  • Browser compatibility is an issue. You will have to write code that targets one browser or another. One of first test with Mouse events proved this to me.
  • Recall, that many of us "old" guys (web developers before 2000) used HTML until Flash became viable. We didn't adopt Flash for web sites because Macromedia made it, we adopted it because it worked. The problems with HTML 5 are still the same problems we tried to solve with Flash and HTML 2, HTML 3 and HTML 4: Cross browser issues and the need for real interactivity.
  • Again, There are no real "designer friendly" tools for the Canvas. In fact, a product like the Flash IDE would be perfect to make Canvas apps because it could spit put all the complex JavaScript to make it all work much faster than coding it by hand. I fully expect Flash CS6 will have an "Export as Canvas" option,
  • JavaScript is essentially ActionScript 2.0, so it will be familiar, but also seem like a few steps back from AS3
  • There is still no easy way to "hide" your code, but you can use an JavaScript Obfuscator (thanks to Chris Cutler for this point) that does essentially what a SWF encryptor does for Flash apps.

HTML 5 Canvas Hello World

So, now that you know a bit about the Canvas, here is a "Hello World" app using the element. Be sure to view this with the latest version of Firefox, Chrome, or Safari. In the next lesson we will discuss a bit more about how a program like this works, but for now: Hello World!

The area below is a :

See demo here

Here is the HTML code used to create that Canvas: (by the way, a lot of this code was adapted from the code presented here: http://dev.opera.com/articles/view/html-5-canvas-the-basics/) I've added a "Main()" function because I suspect that is the way my code will be going in the future.

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




Your browser does not support the HTML 5 Canvas.



[/cc]

There you have it, a short introduction to the HTML 5 . Next time We'll explore some code in more depth.

 

15Feb/100

Tutorial (AS3): Using Fonts Embedded At Compile-Time

Tutorial (AS3): Using Fonts Embedded At Compile-Time

I am continuing to explore the use of external assets embedded at compile time in AS3. Today we will look at how to embed and use a font in this manner. The Embed compile-time directive is available to users of the Flex SDK, Flex Builder, Flash Builder (if and when it comes out), and Flash IDE users (CS4 and beyond).

First, let's find a font

To test this properly you might want to use a font that you do not already have installed. I went to SearchFreeFonts.com and found a nice font that is free for commercial use (this isn't really a commercial use as I lose my shirt on this site, but just in case...). The name of the font is :01-10-00. I recommend that you donate to the creator of any font you find useful.

Let's start the project
I am going to use Flash Develop to do this small project. You don't have to use this. In any case, place all of the files we create in the same folder. This is the /src folder for a Flash develop project. So, for now you will have a single file (we'll use the True Type version of the font) "01-01-00.ttf". I know it is a strange name, but it is a pretty nice font.

Now we will create our main class file
In the same project folder, let's create a class file called: "FontEmbedTest.as". This will need to be the "always compile" class in Flash develop or the "document" class in the IDE.

1. First we will start off our class and import the other classes we need:

[cc lang="javascript" width="550"]
package
{
import flash.display.Sprite;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFieldAutoSize;
import Libary;

public class FontEmbedTest extends Sprite {
[/cc]

2. Next we will start the variable definition section of our code and create the TextField and TextFormat objects. We will also embed the font using the file name we downloaded and placed in the project folder. Notice that I set both the fontName and fontFamily to be 'font010100". I did this because when I just set fontName or just fontFamily (as I have seen in some other examples) I was not able to get the font to show up properly across all of the different players (Win and Mac) that I tried. We will not use the class that we create "Font010100:Class" but a class or some variable creation is required after the embed directive as the "linkage" name for the embedded asset. Notice that the embed line was broken to fit in the space alotted.

[cc lang="javascript" width="550"]
private var textField:TextField = new TextField();
private var textFormat:TextFormat = new TextFormat();

[Embed(source='01-01-00.ttf', fontFamily = 'font010100', fontName = 'font010100',
mimeType="application/x-font-truetype")]
public static const Font010100:Class;
[/cc]

3. Now we will create the constructor for our class. It will set all of the properties of the TextField and TextFormat instances as well as place the TextField on the screen. Notice that we also finish the class and package brackets to be complete.

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

public function FontEmbedTest() {
textFormat.font = "font010100";
textFormat.size = 48;
textFormat.color = 0x000000;

textField.defaultTextFormat = textFormat;
textField.autoSize = TextFieldAutoSize.LEFT;

textField.embedFonts = true;

textField.text = "Embedded Font";

addChild(textField);

}

}

}
[/cc]

Here is a screen shot of this file working:

fontembed.jpg

That's it. It is pretty simple actually.

9Feb/105

AS3 Tutorial: Controlling the Main Timeline of a SWF Embedded at Compile-Time

AS3 Tutorial: Controlling the Main Timeline of a SWF Embedded at Compile-Time

Today I was working a a new piece of the Essential Flash Games framework that allows sponsor swf files to be embedded at compile time and displayed before a game reaches the title screen. I am using Flash Develop and the Flex SDK, but the embed compiler directive is also available in the latest versions of the Flash IDE. I wanted to embed rather than load to ensure that the final swf file is a single self-contained unit that can be easily spread to game portals. I tried this a number of different ways to no avail. Most of the standard swf embed code works fine with assets inside the library of embedded swf, but not when trying to control the main time line of the swf itself. So, this is what we are going to cover: If you have been trying to embed swf files at compile time but cannot get your code to recognize the embedded swf, or the totalFrames attribute of the embedded swf returns 0, or if the swf only shows the final frame of your animation then read on. I had all of those problems today and more, but finally figured out a method to make this work properly.

Controlling the Main Time Line of a swf file embedded at run time.

1. Here is the asset that we are going to embed. I Embed all of my assets in a separate Library class as Static Const variables. This could just as easily be in the main document class for your game or application. I will do it the later way here to keep it uncomplicated. This assumes that the 8bitrocketlogo2.swf is in the same folder as the document class. This logo swf is a multi frame animation with 85 frames on the main time line (its just important that there is more than a single frame because you will want to see if you can play and jump to frames to ensure you have control over the time line).

[cc lang="javascript" width="550"]
[Embed(source = '8bitrocketlogo2.swf', mimeType="application/octet-stream")]
public static const LogoAnimation:Class;
[/cc]

This embeds the swf file as a BiteArrayAsset or a pure binary data stream. The mime-type of "application/octet-stream" is needed to embed as this data type.

2. Next we need to be sure to import the necessary classes

[cc lang="javascript" width="550"]
import flash.display.Loader;
import flash.display.MovieClip;
import mx.core.ByteArrayAsset;
import flash.events.Event;
[/cc]

3. We will need to create these class level variables

[cc lang="javascript" width="550"]
public var loader:Loader=new Loader;
public var logo:ByteArrayAsset;
public var clip:MovieClip;
[/cc]

4. Even though the swf file was embedded at compile time we need to use an asset Loader object to make use of it. First we must instantiate the LogoAnimation class we created that is effectively the equivalent of a "linkage name" in the IDE. We do this by assigning the logo variable to be a new instance of this embedded swf:

[cc lang="javascript" width="550"]
logo = new LogoAnimation();
[/cc]

5. We cannot access this data right away for a couple of reasons. First, the data inside this class is not yet an instance of MovieClip. It is still a ByteArray. Second, even though the asset swf has been embedded, we have no idea whether or not the code can access it yet. I found this out the hard way (hair pulling trial and error). I don't know the actual reason for this, but  I know a way to mitigate it. The best way (that I have found) to know when this embedded data is be ready to be used is to "load" it into an asset Loader class instance and check to see when it's INIT event has fired off. Fair enough. Add these lines to do just that:

[cc lang="javascript" width="550"]
loader.contentLoaderInfo.addEventListener(Event.INIT,loadCompleteListener,false,0,true);
loader.loadBytes(logo);
[/cc]

The contentLoaderInfo attribute of the Loader class will fire off the Event.INIT event when the Loader.content is ready to be used. Also notice that we have also called the loader.loadBytes method and passed in the instance of our embedded LogoAnimation class swf (the logo variable).

To be sure that the swf is completely ready before we try to access it, we will create the loadCompleteListener function to catch the INIT event.

[cc lang="javascript" width="550"]
private function loadCompleteListener(e:Event):void {
 
clip = MovieClip(loader.content);
addChild(clip);
clip.stop();
}
[/cc]

The clip variable in a MovieClip instance that we set to be the content of a MovieClip cast of the loader.content attribute. The loader.content contains the actual binary data of embedded swf but AS3 has no idea what to do with it. We cast it as a MovieClip and assign it to our clip variable so we can use this reference throughout our code without having to re-cast every time. The clip variable can now be used to control an instance of the embedded swf just like any other MovieClip instance.

When a swf is embedded all of the time line code is ignored. We need to call the clip.stop() function to make sure the clip stays on frame 1 until we want to actually play it. We also called the addChild(clip) function here, but it does not need to be called until you want to display the clip on the screen.

That's it. It was pretty simple after I finally got the hang of what was going on.