Why Bother With jQuery? A Guide for (Former) Flash Developers
If you, like many Flash developers, are looking into using HTML5 for your web apps, you’ll almost certainly have come across jQuery. It’s a very popular JavaScript library, used by a large percentage of the most visited websites – but what’s all the fuss about, and should you use it?
Background
If you know AS3, you basically know a lot of JavaScript already; the two languages are very similar. So it’s tempting to just jump straight in and code – but there are a few important concepts you need to understand first. One of these is the idea of the DOM.
When you load a webpage, your browser turns flat HTML into a tree-like structure of JavaScript objects called the DOM (Document Object Model). The JavaScript DOM, then, is very similar to the ActionScript Display List; and if you’re a Flex developer, you’ll be able to see the similarities between MXML and HTML.
In Flash, we can access specific display objects by navigating to them through the display list, like stage.getChildAt(3).getChildAt(0), but this is pretty ridiculous. Instead, we’re more likely to give display objects instance names (through the Flash IDE), or store references to them in variables, arrays, or object properties, like dialogBox.okButton = new SimpleButton().
In JavaScript, we could construct our DOM entirely through JS and then tell the browser to render it, but this is an unusual approach; we’re much more likely to define the DOM via HTML, perhaps with a little JS augmentation. So, JavaScript has various different methods for accessing elements of the DOM.
The simplest of these is document.getElementById(). If we have an HTML document defined like this:
<!DOCTYPE html> <html> <head> <title>Example Page</title> </head> <body> <div> <span id="example"> <p>Here's some example text.</p> </span> </div> </body> </html>
…then document.getElementById("example") will get us the highlighted span object from the DOM. We could then add a second p tag like so:
var newPara = document.createElement("p");
var newTextNode = document.createTextNode("More example text that we created on the fly.");
newPara.appendChild(newTextNode);
document.getElementById("example").appendChild(newPara);
This would update the DOM, making it equivalent to what would have been created if the original HTML was as follows:
<!DOCTYPE html> <html> <head> <title>Example Page</title> </head> <body> <div> <span id="example"> <p>Here's some example text.</p> <p>More example text that we created on the fly.</p> </span> </div> </body> </html>
What if you wanted to then access the two p elements? We can’t access them directly with document.getElementById(), since they have no ID, but we can use document.getElementsByTagName("p") to obtain an array containing both of them.
And if we’d had another span, like this:
<div> <span id="example1"> <p>Here's some example text.</p> <p>More example text that we created on the fly.</p> </span> <span id="example2"> <p>A second span.</p> <p>We don't care about these paragraphs.</p> </span> </div>
…and we only wanted to obtain the first two p tags, we could call document.getElementById("example1").getElementsByTagName("p") just to retrieve those two – all these DOM functions work at any level in the tree structure, just like how every Flash DisplayObjectContainer has methods like getChildAt().
This is simple enough to understand, but there are problems. The first, you may not be surprised to hear, concerns Internet Explorer.
Cross-Browser Compatibility
Impressive Webs has a great outline of Internet Explorer’s getElementById() problem. Essentially, if you have an HTML element like this:
<span name="exampleName"></span>
…then, in most browsers, document.getElementById("exampleName") will not give you the span in question, but in IE7, it will. (Other browsers could use document.getElementsByName("exampleName")[0] to return this particular span.)
This means that, to be consistent across browsers (and assuming we can’t change the HTML), we’ll need to write code like this:
var theSpan;
if (usingIE) { //I won't explain how to detect the browser here
var temp = document.getElementById("exampleId");
//this might have returned an element with a name of "exampleId", so we must check:
if (temp.getAttribute("id") == "exampleId") {
theSpan = temp;
}
}
else {
theSpan = document.getElementById("exampleId");
}
More generally, we could wrap that up into a re-usable function:
function getElById(id) {
if (usingIE) {
var temp = document.getElementById(id);
if (temp.getAttribute("id") == id) {
return temp;
}
}
else {
theSpan = document.getElementById(id);
}
}
Great! But, unfortunately, there are so many of these irritating little differences; it will probably surprise you, if you’re coming from a straight-Flash background, where “compatibility issue” generally means that the Flash Player is a little slow on certain platforms.
jQuery solves this. It papers over the cracks between different browsers with its own set of functions, so if the user’s browser is at least as new as IE6, your code can have a consistent interface.
That’s not the only way that jQuery makes JavaScript simpler…
Easy Syntax
Let’s go back to this snippet of HTML, and assume we want to retrieve the highlighted p elements from the DOM:
<div> <span id="example1"> <p>Here's some example text.</p> <p>More example text that we created on the fly.</p> </span> <span id="example2"> <p>A second span.</p> <p>We don't care about these paragraphs.</p> </span> </div>
With jQuery, we can just write:
jQuery("#example1 p")
That’s all we need! #example1 says “get the element with an ID of example1” and p says “get all the p elements that are children of that element”. It returns a “jQuery object”, which is a JavaScript object that contains the two p elements themselves from the JS DOM, plus a few extra properties and methods specific to jQuery.
We can make it even shorter still, by replacing jQuery with $ – there’s no mystery here; $ is just a short variable name. Compare it to native JavaScript code:
//regular JS, without cross-browser compatibility:
document.getElementById("example1").getElementsByTagName("p")
//jQuery, with cross-browser compatibility built in:
$("#example1 p")
It’s not just shorter, it’s consistent with CSS, which makes it easy to pick up. We could use the exact same selector as inside our jQuery() call to style these specific paragraphs in a CSS stylesheet:
#example1 p { color: red }
That’s just a very simple example; I’m not going to go into detail, but I’m sure you can see the benefit of being able to use the same selectors in both CSS and jQuery.
More Than Just Selection
I mentioned that the jQuery objects returned by a $() call had additional methods and properties. These give you an easy syntax for writing other common pieces of code.
For example, we could add a mouse click event listener and handler function to both of those p elements like so:
$("#example1 p").click(function() {
alert("You clicked a paragraph!");
});
Or, you could make them all invisible:
$("#example1 p").hide();
Or, you could run some more general JavaScript on them:
var allText = "";
$("#example1 p").each(function() {
allText += $(this).text();
});
In each case, jQuery provides a simple, short, consistent way of writing. It means it’s faster to get code from your head to the browser – and not just because it requires fewer characters.
Tweens and Transitions
Since Flash has its roots in animation, we’re used to it having plenty of tweening capabilities built in – both in the Flash IDE and in the Tween class, not to mention the various tweening libraries available.
JavaScript is different; animation is not an intrinsic part of the platform. But little tweens and transitions are expected parts of the user interaction of modern web app: if I add a new comment on a thread, it slides into place; if I remove an item from my shopping basket, it flashes red and disappears. Without these, apps look unpolished.
jQuery adds these little transitions.
For example, let’s say we want to make either of the aforementioned paragraphs fade out when they’re clicked. Easy:
$("#example1 p").click(function() {
$(this).fadeOut();
});
Nothing to it! And you can pass a duration and callback to the fadeOut() function, if you don’t like the defaults.
For something a little more powerful, we can use the animate() method. This is essentially the equivalent of a tween; pass it a set of CSS properties and values to animate towards, a duration, a type of easing, and a callback, and it takes care of it all for you.
It’s not exactly Greensock, but it’s far more convenient than writing these effects from scratch, and ideal for web app interfaces.
Speaking of which…
UI Widgets
HTML has a few UI components built in, of course: buttons, text fields, and so on. HTML5 defines a few new ones, like a pop-up calendar picker and color picket (though these are currently only supported in Opera).
But these aren’t enough, on their own, to make up a full, modern web app interface. There’s nothing to handle tabs within a page, or a progress bar, or even a simple dialog window (outside of alert() and prompt()).
jQuery UI, an optional library build on top of jQuery, adds these extra widgets, with methods and events that are consistent with the usual jQuery syntax. Think of it as a JavaScript equivalent to Keith Peters’ AS3 MinimalComps. The demo page shows it off better than I can explain it.
Every widget can support custom themes, so you can create a single skin that fits your site and apply it to every component, making it possible to modify their appearance all at once. Again: consistency! Plus, you can apply UI-related effects to other elements; make it possible for the user to drag or resize a DOM element, or click and drag a box around a group of elements to select them for submission.
Other Reasons jQuery is So Popular
The cross-browser compatibility, easy syntax, tween capabilities, and UI elements are the main benefits of jQuery over plain JS in my eyes. There are other reasons to like it, though:
- It’s widely-used, and has been around for six years: it’s not some flash-in-the-pan “new hotness” that’s still unproven and might just die off in a few months. You can trust that it’ll be around for a while.
- There’s a big community surrounding it: this means there are plenty of tutorials and books and forums and people to learn from; you’re not going to be fumbling around in the dark.
- The documentation is excellent: seriously, take a look; it’s very clean and full of examples and demos
- There are even alternative sets of docs with different interfaces, if that’s what you require (another example of the great community)
- It’s open source: the community can add to it, and you can hack on it and learn from it
So why wouldn’t you use jQuery? As with most technology choices, it’s a question of making sure you’re using the right tool for the job. If you have a simple DOM structure, or don’t require fancy animations and transitions, or are mainly using the canvas to render your interface, rather than widgets, jQuery probably isn’t necessary.
If you’re already using one or more JavaScript libraries – Dojo, MooTools, YUI, etc. – you may well find that you don’t need jQuery’s functionality on top of what they offer. But my goal in this article is not to try to sell you on any particular library above any other.
I hope this article has helped to explain just what the big deal is about jQuery, particularly if you’re coming from the world of AS3 and Flash. If you’d like to learn it, check out the jQuery tag over on Nettuts+ – jQuery for Absolute Beginners is a good place to start.
Let me know if you’ve any questions!
View full post on Activetuts+

If you, like many Flash developers, are looking into using HTML5 for your web apps, you’ll almost certainly have come across jQuery. It’s a very popular JavaScript library, used by a large percentage of the most visited websites – but what’s all the fuss about, and should you use it?
Background
If you know AS3, you basically know a lot of JavaScript already; the two languages are very similar. So it’s tempting to just jump straight in and code – but there are a few important concepts you need to understand first. One of these is the idea of the DOM.
When you load a webpage, your browser turns flat HTML into a tree-like structure of JavaScript objects called the DOM (Document Object Model). The JavaScript DOM, then, is very similar to the ActionScript Display List; and if you’re a Flex developer, you’ll be able to see the similarities between MXML and HTML.
In Flash, we can access specific display objects by navigating to them through the display list, like
stage.getChildAt(3).getChildAt(0), but this is pretty ridiculous. Instead, we’re more likely to give display objects instance names (through the Flash IDE), or store references to them in variables, arrays, or object properties, likedialogBox.okButton = new SimpleButton().In JavaScript, we could construct our DOM entirely through JS and then tell the browser to render it, but this is an unusual approach; we’re much more likely to define the DOM via HTML, perhaps with a little JS augmentation. So, JavaScript has various different methods for accessing elements of the DOM.
The simplest of these is
document.getElementById(). If we have an HTML document defined like this:…then
document.getElementById("example")will get us the highlightedspanobject from the DOM. We could then add a secondptag like so:var newPara = document.createElement("p"); var newTextNode = document.createTextNode("More example text that we created on the fly."); newPara.appendChild(newTextNode); document.getElementById("example").appendChild(newPara);This would update the DOM, making it equivalent to what would have been created if the original HTML was as follows:
What if you wanted to then access the two
pelements? We can’t access them directly withdocument.getElementById(), since they have no ID, but we can usedocument.getElementsByTagName("p")to obtain an array containing both of them.And if we’d had another
span, like this:…and we only wanted to obtain the first two
ptags, we could calldocument.getElementById("example1").getElementsByTagName("p")just to retrieve those two – all these DOM functions work at any level in the tree structure, just like how every FlashDisplayObjectContainerhas methods likegetChildAt().This is simple enough to understand, but there are problems. The first, you may not be surprised to hear, concerns Internet Explorer.
Cross-Browser Compatibility
Impressive Webs has a great outline of Internet Explorer’s
getElementById()problem. Essentially, if you have an HTML element like this:…then, in most browsers,
document.getElementById("exampleName")will not give you thespanin question, but in IE7, it will. (Other browsers could usedocument.getElementsByName("exampleName")[0]to return this particularspan.)This means that, to be consistent across browsers (and assuming we can’t change the HTML), we’ll need to write code like this:
var theSpan; if (usingIE) { //I won't explain how to detect the browser here var temp = document.getElementById("exampleId"); //this might have returned an element with a name of "exampleId", so we must check: if (temp.getAttribute("id") == "exampleId") { theSpan = temp; } } else { theSpan = document.getElementById("exampleId"); }More generally, we could wrap that up into a re-usable function:
function getElById(id) { if (usingIE) { var temp = document.getElementById(id); if (temp.getAttribute("id") == id) { return temp; } } else { theSpan = document.getElementById(id); } }Great! But, unfortunately, there are so many of these irritating little differences; it will probably surprise you, if you’re coming from a straight-Flash background, where “compatibility issue” generally means that the Flash Player is a little slow on certain platforms.
jQuery solves this. It papers over the cracks between different browsers with its own set of functions, so if the user’s browser is at least as new as IE6, your code can have a consistent interface.
That’s not the only way that jQuery makes JavaScript simpler…
Easy Syntax
Let’s go back to this snippet of HTML, and assume we want to retrieve the highlighted
pelements from the DOM:With jQuery, we can just write:
jQuery("#example1 p")That’s all we need!
#example1says “get the element with an ID ofexample1” andpsays “get all thepelements that are children of that element”. It returns a “jQuery object”, which is a JavaScript object that contains the twopelements themselves from the JS DOM, plus a few extra properties and methods specific to jQuery.We can make it even shorter still, by replacing
jQuerywith$– there’s no mystery here;$is just a short variable name. Compare it to native JavaScript code://regular JS, without cross-browser compatibility: document.getElementById("example1").getElementsByTagName("p") //jQuery, with cross-browser compatibility built in: $("#example1 p")It’s not just shorter, it’s consistent with CSS, which makes it easy to pick up. We could use the exact same selector as inside our
jQuery()call to style these specific paragraphs in a CSS stylesheet:#example1 p { color: red }That’s just a very simple example; I’m not going to go into detail, but I’m sure you can see the benefit of being able to use the same selectors in both CSS and jQuery.
More Than Just Selection
I mentioned that the jQuery objects returned by a
$()call had additional methods and properties. These give you an easy syntax for writing other common pieces of code.For example, we could add a mouse click event listener and handler function to both of those
pelements like so:$("#example1 p").click(function() { alert("You clicked a paragraph!"); });Or, you could make them all invisible:
$("#example1 p").hide();Or, you could run some more general JavaScript on them:
var allText = ""; $("#example1 p").each(function() { allText += $(this).text(); });In each case, jQuery provides a simple, short, consistent way of writing. It means it’s faster to get code from your head to the browser – and not just because it requires fewer characters.
Tweens and Transitions
Since Flash has its roots in animation, we’re used to it having plenty of tweening capabilities built in – both in the Flash IDE and in the
Tweenclass, not to mention the various tweening libraries available.JavaScript is different; animation is not an intrinsic part of the platform. But little tweens and transitions are expected parts of the user interaction of modern web app: if I add a new comment on a thread, it slides into place; if I remove an item from my shopping basket, it flashes red and disappears. Without these, apps look unpolished.
jQuery adds these little transitions.
For example, let’s say we want to make either of the aforementioned paragraphs fade out when they’re clicked. Easy:
$("#example1 p").click(function() { $(this).fadeOut(); });Nothing to it! And you can pass a
durationandcallbackto thefadeOut()function, if you don’t like the defaults.For something a little more powerful, we can use the
animate()method. This is essentially the equivalent of a tween; pass it a set of CSS properties and values to animate towards, a duration, a type of easing, and a callback, and it takes care of it all for you.It’s not exactly Greensock, but it’s far more convenient than writing these effects from scratch, and ideal for web app interfaces.
Speaking of which…
UI Widgets
HTML has a few UI components built in, of course: buttons, text fields, and so on. HTML5 defines a few new ones, like a pop-up calendar picker and color picket (though these are currently only supported in Opera).
But these aren’t enough, on their own, to make up a full, modern web app interface. There’s nothing to handle tabs within a page, or a progress bar, or even a simple dialog window (outside of
alert()andprompt()).jQuery UI, an optional library build on top of jQuery, adds these extra widgets, with methods and events that are consistent with the usual jQuery syntax. Think of it as a JavaScript equivalent to Keith Peters’ AS3 MinimalComps. The demo page shows it off better than I can explain it.
Every widget can support custom themes, so you can create a single skin that fits your site and apply it to every component, making it possible to modify their appearance all at once. Again: consistency! Plus, you can apply UI-related effects to other elements; make it possible for the user to drag or resize a DOM element, or click and drag a box around a group of elements to select them for submission.
Other Reasons jQuery is So Popular
The cross-browser compatibility, easy syntax, tween capabilities, and UI elements are the main benefits of jQuery over plain JS in my eyes. There are other reasons to like it, though:
So why wouldn’t you use jQuery? As with most technology choices, it’s a question of making sure you’re using the right tool for the job. If you have a simple DOM structure, or don’t require fancy animations and transitions, or are mainly using the
canvasto render your interface, rather than widgets, jQuery probably isn’t necessary.If you’re already using one or more JavaScript libraries – Dojo, MooTools, YUI, etc. – you may well find that you don’t need jQuery’s functionality on top of what they offer. But my goal in this article is not to try to sell you on any particular library above any other.
I hope this article has helped to explain just what the big deal is about jQuery, particularly if you’re coming from the world of AS3 and Flash. If you’d like to learn it, check out the jQuery tag over on Nettuts+ – jQuery for Absolute Beginners is a good place to start.
Let me know if you’ve any questions!
Hey Flash developers! In this tutorial series we are going to go through the process of developing a very basic Tower Defense game. In this first part of the series, we’ll learn how to deploy turrets on the game field, give them the ability to aim at an object (in this case, the mouse) and make them fire particles.
Final Result Preview
Once we complete this tutorial, we are going to have this:
Click a circle to mount a turret on it. Notice how all the turrets rotate so that they point towards the mouse cursor. Click, and all mounted turrets will fire a particle towards the cursor.
Step 1: What is a Tower Defense Game?
Wikipedia’s definition sums it up nicely:
That is essentially what we will be developing in this tutorial series. Remember that we refer to the towers as turrets in this tutorial.
Cursed Treasure is a great example of a TD game, if you’re still unsure.
Step 2: Setup – IDE
Before we start developing the game, we need to setup the project on our IDE. I’ll be using FlashDevelop here. If you want to read about how to setup the project on Flash Develop, please have a read Steps 1 and 2 of this tutorial, or this full guide to FlashDevelop.
So now you should have a main class,
Main.as, with the following code:package { import flash.display.Sprite; public class Main extends Sprite { public function Main():void { } } }Step 3: Understand the Game Elements
For this part, our game will have following game elements:
All the above elements will be created in the
Main.asexcept the turrets which will be a separateTurretclass.Lets start coding now!
Step 4: Creating the Placeholders
First we’ll create a function called
createTurretPlaceholder()which will create and return us a placeholder sprite. Add the following to theMainclass:private function createTurretPlaceholder():Sprite { var placeholder:Sprite = new Sprite(); // draw the graphics var g:Graphics = placeholder.graphics; g.beginFill(0xCE7822); g.drawCircle(0, 0, 20); g.endFill(); return placeholder; }This function is simply creating a
Spritevariableplaceholder. Then using Actionscript drawing API we create the graphic, which is a simple circle. Finally it returns that sprite.Step 5: Adding Some Placeholders
Now we’ll create three placeholders using the previous function and put them at different positions on the field. Add the following code in the
Main()constructor:In the above statement we create a variable
placeholder1of typeSpritewhich receives a placeholder from the abovecreateTurretPlaceholder()function.We position the placeholder on the field.
And then we add the placeholder to the stage.
Using the same code, we’ll add two more placeholders to the field – so your
Main()function should look like this:public function Main() { var placeholder1:Sprite = createTurretPlaceholder(); placeholder1.x = 200; placeholder1.y = 60; var placeholder2:Sprite = createTurretPlaceholder(); placeholder2.x = 60; placeholder2.y = 260; var placeholder3:Sprite = createTurretPlaceholder(); placeholder3.x = 350; placeholder3.y = 260; addChild(placeholder1); addChild(placeholder2); addChild(placeholder3); }Step 6: Turret – Creating the Class
As I mentioned before, the turret is going to be a separate class. This is because turrets need to have specific properties and methods of their own, and to be extended in future to create different type of turrets. This makes them a perfect candidate to be defined in a separate class.
Go on and create a new class called
Turret, derived fromSprite, in a file namedTurret.as. It should have the following basic code:package { import flash.display.Sprite; public class Turret extends Sprite { public function Turret() { } } }Step 7: Turret – The Graphics
Now that we have base structure of the
Turretclass, the next step is to give the turret some graphics. For that we create a new function calleddraw()in the class. So put the following function just below the constructor:private function draw():void { var g:Graphics = this.graphics; g.beginFill(0xD7D700); g.drawCircle(0, 0, 20); g.beginFill(0x800000); g.drawRect(0, -5, 25, 10); g.endFill(); }As you might have noticed in the code, we draw a circle and a rectangle on it. That’s how our turret is going to look. Now we call this function from the constructor itself.
public function Turret() { draw(); }Step 8: Creating a Ghost Turret
The next thing we do is to display a ghost turret when we hover the placeholders that we have put on the game field. What is a ghost turret? Well, its just a transparent turret that appears when hovering the mouse on the placeholders, to tell the player that a turret can be deployed there.
To begin with this, we need a ghost turret. Go ahead and declare a variable for it in the
Mainclass.Now create a new turret in the
Main()constructor:Give the ghost turret some properties so that it looks like a ghost:
In the above code we lower down the opacity of the turret to half (0.5) and set the
mouseEnabledproperty of the turret tofalseso that the ghost turret does not receive any mouse events. Why? We will see that later. And as the ghost turret will be invisible by default, we hide it.Finally, add the turret to the display list:
Your
Mainconstructor should look something like this:public function Main() { var placeholder1:Sprite = createTurretPlaceholder(); placeholder1.x = 200; placeholder1.y = 60; var placeholder2:Sprite = createTurretPlaceholder(); placeholder2.x = 60; placeholder2.y = 260; var placeholder3:Sprite = createTurretPlaceholder(); placeholder3.x = 350; placeholder3.y = 260; addChild(placeholder1); addChild(placeholder2); addChild(placeholder3); ghost_turret = new Turret(); ghost_turret.alpha = 0.5; ghost_turret.mouseEnabled = false; ghost_turret.visible = false; addChild(ghost_turret); }If you run the movie now (Ctrl+Enter), all you’ll see is three placeholders on the stage. Boring huh? Lets add some interactivity.
Step 9: Showing/Hiding the Ghost Turret
We want the ghost turret to appear when the mouse hovers over any placeholder. So let’s attach mouse listeners to each placeholder in the
createTurretPlaceholder()function just before it returns theplaceholdervariable.private function createTurretPlaceholder():Sprite { var placeholder:Sprite = new Sprite(); // draw the graphics var g:Graphics = placeholder.graphics; g.beginFill(0xCE7822); g.drawCircle(0, 0, 20); g.endFill(); placeholder.addEventListener(MouseEvent.MOUSE_OVER, showGhostTurret, false, 0, true); placeholder.addEventListener(MouseEvent.MOUSE_OUT, hideGhostTurret, false, 0, true); return placeholder; }The code attaches listeners to
MOUSE_OVERandMOUSE_OUTevents.Next we define the two handler functions for the same. Add the following two functions below the
createTurretPlaceholder()function:private function showGhostTurret(e:MouseEvent = null):void { var target_placeholder:Sprite = e.currentTarget as Sprite; ghost_turret.x = target_placeholder.x; ghost_turret.y = target_placeholder.y; ghost_turret.visible = true; } private function hideGhostTurret(e:MouseEvent = null):void { ghost_turret.visible = false; }hideGhostTurret()just hides the ghost turret but whats going on in theshowGhostTurretfunction? Lets see.We get the reference of the placeholder on which mouse is present using the
MouseEvent‘scurrentTargetproperty, typecasted toSprite.This is simple…we position the ghost turret to the placeholder’s cordinates and make it visible. Run the movie and you should see the ghost turret appear when hovering over the placeholders. Nice!
Step 10: Deploying Turrets
Our next objective is to deploy a turret when a placeholder is clicked. For that we need a
CLICKlistener on the placeholder. But before that we need anArrayvariable which will hold all our turrets, so we can reference them at any time later. Make one in theMainclass.Then attach another listener just below the two previous listeners we added in the
createTurretPlaceholder()function:Declare the
addTurret()handler function below thehideGhostTurret()function:private function addTurret(e:MouseEvent):void { }Now lets write the code for the function. Add the following code to the
addTurret()function.We first get the reference to the placeholder that was clicked just like we did in the
showGhostTurret()function.The we create a new turret in a variable named
turret.Next, we position the
turretat the coordinates of thetarget_placeholder.When the turret is created, we add it to the stage and push it onto the array.
Your
addTurretfunction should look like this in the end:private function addTurret(e:MouseEvent):void { var target_placeholder:Sprite = e.currentTarget as Sprite; var turret:Turret = new Turret(); turret.x = target_placeholder.x; turret.y = target_placeholder.y; addChild(turret); turrets.push(turret); }One thing to note here. Remember we set the
mouseEnabledproperty of the ghost turret tofalse? If we hadn’t, the ghost turret in between the placeholder and mouse would have captured the click event, thus preventing the event from reaching the placeholder. As a result theCLICKlistener attached to the placeholder would not have get called.You might want to remove the
CLICKlistener from the placeholder. I haven’t done it here since the new turret blocks any clicks, but if you use a different turret design, it’s a good idea.Well thats all we need to place our turrets. Try running the movie and you should be able to deploy turrets on placeholders.
Step 11: Making the Turret Move
For this tutorial we will make the turret rotate to face the mouse. We’ll keep the rotation functionality in a separate method of the
Turretclass. This method will be calledupdate().Lets add this method to
Turretnow:public function update():void { var angle:Number = Math.atan2(stage.mouseY - this.y, stage.mouseX - this.x) / Math.PI * 180; this.rotation = angle; }All we do in ths function is calculate the angle of the mouse from the turret and set the turret’s angle to it. (Take a look at Trigonometry for Flash Developers if you’re not sure how this works.)
But notice that we have not called this function from anywhere and so nothing happens yet. This function will be called from the game loop that we define next. Lets go!
Step 12: The Game Loop
Whats a game loop? Its a function that is attach to the
ENTER_FRAMEevent and so it gets called on every frame. It updates all the elements in the game…in our case, Turrets. More details in this article.Create a function called
gameLoop()below theMain()constructor which will be a listener to theENTER_FRAMEevent of the movie:private function gameLoop(e:Event):void { }Now that we have the listener function defined, we need to attach it to the corresponding event. We do so in the
Main()constructor. Add the following line to theMain()function:public function Main() { var placeholder1:Sprite = createTurretPlaceholder(); placeholder1.x = 200; placeholder1.y = 60; var placeholder2:Sprite = createTurretPlaceholder(); placeholder2.x = 60; placeholder2.y = 260; var placeholder3:Sprite = createTurretPlaceholder(); placeholder3.x = 350; placeholder3.y = 260; addChild(placeholder1); addChild(placeholder2); addChild(placeholder3); ghost_turret = new Turret(); ghost_turret.alpha = 0.5; ghost_turret.mouseEnabled = false; addChild(ghost_turret); stage.addEventListener(Event.ENTER_FRAME, gameLoop); }Step 13: Updating All the Turrets
Lets put some code in our
gameLoop()function.for each(var turret:Turret in turrets) { }We iterate over the
turretsarray, which has references to all turrets on the stage, using afor each...inloop.for each(var turret:Turret in turrets) { turret.update(); }And call the
update()function of each turret. And we are done. If you run the movie now, you should be able to deploy turrets which always face the mouse. Something like this:Step 14: Making the Turrets Shoot
Our next objective is to make the turrets shoot bullets. At whom? For this tutorial, we’ll make the turrets shoot towards any point we click on the stage. We’ll do this like so:
Lets declare the listener function named
shootAdd the function toMainclass:private function shoot(e:MouseEvent):void { }And then attach the above listener to the
CLICKevent of stage in theMain()constructor:Step 15: Creating the Bullet
Before we proceed to writing the code for shooting in the
shoot()function, we will define a new function for creating a bullet, just like we did for creating a placeholder. So put the following function below thecreateTurretPlaceholder():private function createBullet():Sprite { var bullet:Sprite = new Sprite(); // draw the graphics var g:Graphics = bullet.graphics; g.beginFill(0xEEEEEE); g.drawCircle(0, 0, 5); g.endFill(); return bullet; }Nothing much here. We just create a new
Sprite, draw an off-white color circle inside it, and return it. Now let’s continue to define ourshoot()function.Step 16: How to Shoot?
Time to add some code to the
shoot().for each(var turret:Turret in turrets) { }First, we iterate over all the turrets on the stage using the
for each...inloop.Now for every turret we create a bullet using the function we created previously.
Here we store the value of turret’s rotation property in bullet’s rotation. Why? Well…rotating the bullet is not what we want. The bullet needs to continue moving in direction determined by the turret, and for this we need the turret’s rotation value from the time it shot the bullet. We just store this as the bullet’s rotation for future use.
These two lines set the bullet’s initial position, which is 25 pixels away from the turret in the direction of facing (remember the
rotationproperty). Again, read up on trigonometry if this is unfamiliar to you.And as the usual last step, we add the bullet to the stage’s display list.
This is how your
shoot()function should look like:private function shoot(e:MouseEvent):void { for each(var turret:Turret in turrets) { var new_bullet:Sprite = createBullet(); new_bullet.rotation = turret.rotation; new_bullet.x = turret.x + Math.cos(new_bullet.rotation * Math.PI / 180) * 25; new_bullet.y = turret.y + Math.sin(new_bullet.rotation * Math.PI / 180) * 25; addChild(new_bullet); } }If you run you game now and click on any placeholder, the turret will get get deployed – but oops… we have a problem here. An extra bullet also gets created with the turret. Lets fix this.
Step 17: Why Did That Extra Bullet Appear?
To understand this we need to understand event propagation in AS3.
Any event which gets generated passes through three phases: capturing, target and bubbling. The events starts from the topmost parent of the target which generated the event. It passes through all the inner child elements, which is the capturing phase. Then it reaches the actual target which is the target phase. Then the event goes back to the top, passing through same elements in reverse, which is the bubbling phase. In the bubbling phase, all the elements which have a listener defined for the propagating event get triggered and their listeners are executed.
We have a
CLICKlistener bound to the stage and the placeholders. But the placeholders are also children of the stage. So when we click the placeholder, aCLICKevent gets generated with the target as the placeholder. The event propagates from the stage towards the placeholder – the capture phase. It reaches the placeholder and itsCLICKhandler, theaddTurret()function, gets executed and we have a turret on the stage. Now the event propagates backwards – the bubbling phase – and when it reaches the stage again it finds aCLICKlistener for it as well, which gets executed. As a result theshoot()function gets executed and a bullet is added to the stage.So thats the problem, but how to solve it? What we need to do is stop the event’s further propagation when it reaches the target. That means in the
addTurret()we stop the event’s propagation. So go ahead and add a line at the end ofaddTurret():private function addTurret(e:MouseEvent):void { var target_placeholder:Sprite = e.currentTarget as Sprite; var turret:Turret = new Turret(); turret.x = target_placeholder.x; turret.y = target_placeholder.y; addChild(turret); turrets.push(turret); e.stopPropagation(); }The line we added stops the event’s propagation and it doesn’t reaches the stage. For a more comprehensive understanding of event framework in Actionscript 3.0 read the AS3 101 post. Let’s continue with the game.
Step 18: Making the Bullet Move
We create the bullet on clicking the stage but it doesn’t move still. Our next step is to add an event listener to the bullet which gets called on every frame and moves it. First, we declare a variable for the bullet’s speed. Add the variable where the other variables are declared:
Then, add the following listener function to the
Mainclass:private function moveBullet(e:Event):void { var bullet:Sprite = e.currentTarget as Sprite; bullet.x += Math.cos(bullet.rotation * Math.PI / 180) * bullet_speed; bullet.y += Math.sin(bullet.rotation * Math.PI / 180) * bullet_speed; }What we do in this listener is:
bullet_speedamount in the direction of the bullet’s rotation.Lastly, attach the listener we just created to the
ENTER_FRAMEevent of the bullet in theshoot()function:private function shoot(e:MouseEvent):void { for each(var turret:Turret in turrets) { var new_bullet:Sprite = createBullet(); new_bullet.rotation = turret.rotation; new_bullet.x = turret.x + Math.cos(new_bullet.rotation * Math.PI / 180) * 25; new_bullet.y = turret.y + Math.sin(new_bullet.rotation * Math.PI / 180) * 25; new_bullet.addEventListener(Event.ENTER_FRAME, moveBullet, false, 0, true); addChild(new_bullet); } }If you test you game now, you should see bullets moving when you click on the stage. But if you look carefully, you will notice that the bullets keep moving once created…even after they go out of the visible stage area. Our next step is to destroy the bullets as soon as they leave the movie boundaries.
Step 19: Destroying the Bullets
Add the following code in the end of
moveBullet()function:private function moveBullet(e:Event):void { var bullet:Sprite = e.currentTarget as Sprite; bullet.x += Math.cos(bullet.rotation * Math.PI / 180) * bullet_speed; bullet.y += Math.sin(bullet.rotation * Math.PI / 180) * bullet_speed; if (bullet.x < 0 || bullet.x > stage.stageWidth || bullet.y < 0 || bullet.y > stage.stageHeight) { bullet.removeEventListener(Event.ENTER_FRAME, moveBullet); bullet.parent.removeChild(bullet); bullet = null; } }Here we check whether the bullet is out of the stage boundaries. If it is, we remove its
ENTER_FRAMElistener and remove the actual bullet from the stage. We also set thebulletvariable tonullso that the bullet has no reference left and is available for garbage collection.Conclusion
We have completed our basic tower defense game in which you can deploy turrets on specific placeholders which shoot towards any point we click on the stage. Cool, huh?
In the next part we’ll add enemies to the game and intelligence to the turrets so that they can do what they are suppose to do: defend. Also we’ll add some more elements that make the game look more complete and cool. Till then, try adding some more features to this on your own.
It’s Premium tutorial time! This week, Tuts+ members will learn how to use the EaselJS JavaScript library (along with SoundJS and TweenJS) to create a version of the classic Pong game in HTML5.
Premium Preview
Click to play the demo
Read the Full Tutorial
Premium members can access the full tutorial right away!
If you’re not yet a Premium member, you can still read the first few steps of the tutorial.
Tuts+ Premium Membership
We run a Premium membership system 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, Photo Premium, and the new Mobile 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, circle us on Google+, like us on Facebook, and grab the Activetuts+ RSS Feed to stay up to date with the latest tutorials and articles.
There are a lot of games out there with jerky, unrealistic movements and that can do only one thing to your product: make it unappealing to the audience. But smooth movement is not hard to achieve – let’s get to work!
Final Result Preview
Let’s take a look at the final result we will be working towards:
Step 1: Set Up the Environment
This is a straight forward tutorial, so the setting up will also be straight forward.
Create a New ActionScript 3.0 Flash Project. The stage size and color don’t matter, just use what you are confortable with.
I use FlashDevelop for coding, but also this could be done in any AS editor, like Flash Pro (or any text editor, maybe Notepad
). So, create a Class file, make sure your code looks pretty much like mine; see below. I called mine “Movement”. (If you’re using Flash Pro, check out this guide to creating a class.)
package { import flash.display.Sprite; public class Movement extends Sprite { public function Movement():void { } } }After you’re done, make sure your Class is linked to the Flash project as the Main Class.
Step 2: Create the Square and Variables
So after you’ve linked the Movement Class to your document, define the variables as I did below
package { import flash.display.Sprite; import flash.events.Event; public class Movement extends Sprite { //The object that will move private var square:Sprite; //The maximum speed private var _max:Number = 10; //The variables that are going to be applied to move the square private var dx:Number = 0; private var dy:Number = 0; public function Movement():void { //Listen for added to stage event addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event):void { removeEventListener(Event.ADDED_TO_STAGE, init); //Creating a new Sprite and draw inside a square square = new Sprite(); square.graphics.beginFill(0x333333); square.graphics.drawRect(0, 0, 30, 30); square.x = stage.stageWidth / 2 - square.width / 2; square.y = stage.stageHeight / 2 - square.height / 2; addChild(square); } } }This is pretty much all that we’ll do for creating the object. You can use your own object but for this simple movement tutorial I used this simple square.
Step 3: Introducing the Input.as Class
Hi guys, this is the Input.as Class; Input.as Class these are the guys I told you about – be nice to them!
So what is this class about, you may wonder. Basically it does your key handling job for you. It adds a listener to ENTER_FRAME events – with low priority – and a key listener which fills some private Dictionaries. Also it uses another Class for key codes. You can take a look inside and see for yourself how is working.
Note: The Input.as Class does not belong to me. It was created by Matthew Bush, who ported Box2D to Flash.
//Example of Input.as Class usage //You have to always initialize it as this with the stage parameter Input.initialize(stage); //After initializing, you can use kd(), kp() or ku() methods, which //return a Boolean value if the conditions are met. //These methods accept multiple arguments, //so for one event you can use multiple keys. //This makes it a lot easier to give a boost of accessibility to your app. //e.g See below as I use one call for detecting UP arrow or W for going up. Input.kd("UP", "W");Step 4: Importing the Classes
So now that you are familiar with the Input.as Class, we are going to import it in our Movement Class and initialize it.
package { import flash.display.Sprite; import flash.events.Event; import Input; public class Movement extends Sprite { //The object that will move private var square:Sprite; //The maximum speed private var _max:Number = 10; //The variables that are going to be applied to move the square private var dx:Number = 0; private var dy:Number = 0; public function Movement():void { //Listen for added to stage event addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event):void { removeEventListener(Event.ADDED_TO_STAGE, init); //Creating a new Sprite and draw inside a square square = new Sprite(); square.graphics.beginFill(0x333333); square.graphics.drawRect(0, 0, 30, 30); square.x = stage.stageWidth / 2 - square.width / 2; square.y = stage.stageHeight / 2 - square.height / 2; addChild(square); //Initialize the Input.as Class with handler on stage Input.initialize(stage); //Add the refresh loop addEventListener(Event.ENTER_FRAME, refresh); } private function refresh(e:Event):void { } } }Step 5: Handling the Key Inputs
I use an ENTER_FRAME-based loop for detecting the key inputs; below is the
refresh()method which is the handler function for this event.private function refresh(e:Event):void { //Key Handler if (Input.kd("A", "LEFT")) { //Move to the left } if (Input.kd("D", "RIGHT")) { //Move to the right } if (!Input.kd("A", "LEFT", "D", "RIGHT")) { //If there is no left/right pressed } if (Input.kd("W", "UP")) { //Move up } if (Input.kd("S", "DOWN")) { //Move down } if (!Input.kd("W", "UP", "S", "DOWN")) { //If there is no up/down action } }Step 6: Explaining the Calculations – Handling the Velocity
This is pretty straight forward. Detect whether any of the keys are pressed, then act accordingly.
I use the ternary operator a lot:
value = condition ? true : false;This is basically an if-statement that’s been condensed to a single line.
For every key detection, I use this method: if the value is bigger than
_maxthen set it equal to_max; otherwise, increment or decrement that particular value as appropriate. This way, it’s kept within certain bounds. Simple, right?Below you can study the conditions:
private function refresh(e:Event):void { //Key Handler if (Input.kd("A", "LEFT")) { //Move to the left dx = dx < 0.5 - _max ? _max * -1 : dx - 0.5; } if (Input.kd("D", "RIGHT")) { //Move to the right dx = dx > _max - 0.5 ? _max : dx + 0.5; } if (!Input.kd("A", "LEFT", "D", "RIGHT")) { //If there is no left/right pressed if (dx > 0.5) { dx = dx < 0.5 ? 0 : dx - 0.5; } else { dx = dx > -0.5 ? 0 : dx + 0.5; } } if (Input.kd("W", "UP")) { //Move up dy = dy < 0.5 - _max ? _max * -1 : dy - 0.5; } if (Input.kd("S", "DOWN")) { //Move down dy = dy > _max - 0.5 ? _max : dy + 0.5; } if (!Input.kd("W", "UP", "S", "DOWN")) { //If there is no up/down action if (dy > 0.5) { dy = dy < 0.5 ? 0 : dy - 0.5; } else { dy = dy > -0.5 ? 0 : dy + 0.5; } } //After all that, apply these to the object square.x += dx; square.y += dy; }If you’re unfamiliar with the ternary operator, grab a piece of paper and a pen and write out a few of them in if…else format; it’s a great exercise to get to grips with that’s going on.
Keep in mind I manipulate the
dxanddyvariables, and only set the actual x and y values at the end. This helps us make the motion fluid; it’s not jerking around as we alter their values directly throughout the function..Go on, test it! See how nicely it’s moving?
Step 7: Handling Boundary Collisions
Okay. Everything is right, moving fluidly – but OUT of the stage! Below I added the collision detection conditions.
private function refresh(e:Event):void { //Key Handler if (Input.kd("A", "LEFT")) { //Move to the left dx = dx < 0.5 - _max ? _max * -1 : dx - 0.5; } if (Input.kd("D", "RIGHT")) { //Move to the right dx = dx > _max - 0.5 ? _max : dx + 0.5; } if (!Input.kd("A", "LEFT", "D", "RIGHT")) { //If there is no left/right pressed if (dx > 0.5) { dx = dx < 0.5 ? 0 : dx - 0.5; } else { dx = dx > -0.5 ? 0 : dx + 0.5; } } if (Input.kd("W", "UP")) { //Move up dy = dy < 0.5 - _max ? _max * -1 : dy - 0.5; } if (Input.kd("S", "DOWN")) { //Move down dy = dy > _max - 0.5 ? _max : dy + 0.5; } if (!Input.kd("W", "UP", "S", "DOWN")) { //If there is no up/down action if (dy > 0.5) { dy = dy < 0.5 ? 0 : dy - 0.5; } else { dy = dy > -0.5 ? 0 : dy + 0.5; } } //Boundary detection if (square.x - dx < 0 || square.x + dx + square.width > stage.stageWidth) { //x axis detection } if (square.y - dy < 0 || square.y + dy + square.height > stage.stageHeight) { //y axis detection } //After all that, apply these to the object square.x += dx; square.y += dy; }It’s looking for boundaries in a more precise fashion, by checking whether the edges of the square hit the boundaries (before this, it was just checking the center of the square against the boundaries).
Great. Now we need to add the code to make the square bounce off the boundaries. What I do for that is multiply by -1 the axis value
dxordy. But that is not enough! If the speed is quite fast, then the square will get through the margins or just go nuts. So before we multiply we need to set the x or y of the object to be the same as the boundary that it meets.So if x < 0 (and so it is colliding with the left edge), then we move the object to be exactly on the left edge, like so:
object.x = 0; and then multiply thedxby -1.//Margin detection if (square.x - dx < 0 || square.x + dx + square.width > stage.stageWidth) { //x axis detection square.x = square.x - dx < 0 ? 0 : stage.stageWidth - square.width; dx *= -1; } if (square.y - dy < 0 || square.y + dy + square.height > stage.stageHeight) { //y axis detection square.y = square.y - dy < 0 ? 0 : stage.stageHeight - square.height; dy *= -1; }Test it now! Bouncy right?
To make it even better, go on experimenting with different values - like instead of multiplying by -1, try -0.7 and see the results.
Conclusion
So you met the Input.as Class, got to know how to work with it, and made a nice fluid movement in just a few minutes. I think this counts as a great time!
Please leave your comments below and any other questions, I will gladly answer.
But if you encounter any problem please check twice your code, compare it with the source file and then if you can't make it work, feel free to post a question.
As HTML games begin to gradually increase in popularity, vendors are starting to introduce some exciting new APIs to make gaming that little bit sweeter for both us developers and our end players. One of these is the GamepadAPI, which allows you to connect your good old console gamepad into your computer and use it for browser based games, plug and play style. Let’s dive in!
Introduction: What Is the Gamepad API?
In a nutshell, the Gamepad API allows you to interact with your browser using a video game console controller, AKA a gamepad. This doesn’t require a special driver or plugin to work, it’s as simple as plug and play!
Being a console gamer rather than a desktop gamer myself, I much prefer to interact with games using a gamepad, and with the upcoming rise of HTML and JavaScript based games, this is going to become a really useful tool in making games more easily accessible for your users.
The Gamepad API is not readily available for public release, but we can start using it for ourselves with preview versions of Firefox. So before we get stuck in, we need a few things.
What You’ll Need
As I mentioned, the Gamepad API isn’t available for public release just yet so you will need to first get yourself a Nightly build of Firefox and make sure you have the Firebug add-on installed (for debugging purposes only).
Also, you can’t forget a gamepad! I’m going to be using a PlayStation 3 controller for this tutorial but an Xbox controller will do just fine.
Once you have installed Nightly and added on Firebug you are ready to go!
(NB. Recent builds of Chromium have Gamepad API support as well, but this tutorial has not been tested against them.)
Step 1: Connecting a Gamepad to Your Browser
Let’s start with a basic HTML file (index.html), sourcing “gamepad.js” (a blank JavaScript file).
index.html
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Introduction to the Gamepad API</title> </head> <body> <h1>Gamepad API</h1> <script src="gamepad.js"></script> </body> </html>The connection of a gamepad is detected with a simple JavaScript event listener, the event fired is called “MozGamepadConnected”. So the first thing we need to do is add an event listener to the window to detect that event.
I’m also adding a callback function that will log the details of the event to Firebug’s console. This is the information we are most interested in and what will actually let us know that we have connected a gamepad successfully.
function gamepadConnected(evt) { console.log(evt); } window.addEventListener('MozGamepadConnected', gamepadConnected);Run your index.html in Nightly and open up Firebug’s console, here we’ll be able to see the logging of the event from our callback function.
Make sure your controller is turned off and not connected wirelessly to a games console. Plug it in to your computer via USB and power on the controller, watching the event log in the console.
Great, we have a gamepad connecting to a browser, no extra plugins or drivers required!
Step 2: Disconnecting a Gamepad
It’s just as important to know whether a gamepad has been disconnected as well, so let’s look at the event, “MozGamepadDisconnected”.
Similarly to step one, add an event listener for a disconnect event and a callback function to log the event details.
function gamepadDisconnected(evt) { console.log(evt); } window.addEventListener('MozGamepadDisconnected', gamepadDisconnected);If you’re gamepad is still connected, refresh your page (which you’ll see connected event be logged) and then disconnect your gamepad by ejecting it from the USB port. You should get an event log like this one.
Now we know when a gamepad has been connected and disconnected, it’s probably a good idea to record the state inside a variable and get ready to detect button events!
var gamepadActive = false; function gamepadConnected(evt) { console.log(evt); gamepadActive = true; } function gamepadDisconnected(evt) { console.log(evt); gamepadActive = false; } window.addEventListener('MozGamepadConnected', gamepadConnected); window.addEventListener('MozGamepadDisconnected', gamepadDisconnected);Step 3: Detecting Button Presses
Button presses, again, use an event listener and callback function with two events, “MozGamepadButtonDown” and “MozGamepadButtonUp”.
I would suggest logging the entire event from the button press yourself to see what is going on, but the key piece of information we need to get from this event is
evt.button. This is the numerical id of the button that was pressed.The callback function this time takes a second parameter, a boolean value to test if the button was pressed or released. We set this ourselves in the callback functions of the event listeners.
function buttonPressed(evt, pressed) { console.log(evt.button, pressed); } window.addEventListener("MozGamepadButtonDown", function(evt) { buttonPressed(evt, true); } ); window.addEventListener("MozGamepadButtonUp", function(evt) { buttonPressed(evt, false); } );This should now output the IDs of the buttons that are pressed and whether they were pressed or released (true for button down, false for button up).
Next we’ll create an array with all the PlayStation 3 buttons in. The indices of the array will map to the IDs used on this gamepad, with the values being the name of the button.
var gamepadActive = false, ps3Buttons = new Array(); ps3Buttons[12] = 'triangle', ps3Buttons[15] = 'square', ps3Buttons[14] = 'cross', ps3Buttons[13] = 'circle', ps3Buttons[4] = 'up', ps3Buttons[7] = 'left', ps3Buttons[6] = 'down', ps3Buttons[5] = 'right', ps3Buttons[10] = 'L1', ps3Buttons[8] = 'L2', ps3Buttons[11] = 'R1', ps3Buttons[9] = 'R2', ps3Buttons[1] = 'L3', ps3Buttons[2] = 'R3', ps3Buttons[16] = 'PS', ps3Buttons[0] = 'select', ps3Buttons[3] = 'start';If you’re using a different controller, take the time to figure out which index goes with which button, and store that info in a similar array.
If we now modify the
buttonPressed()function ever so slightly, we can easily tell which button on the controller has been pressed.function buttonPressed(evt, pressed) { console.log(ps3Buttons[evt.button] + ' was pressed'); }Give it a go! Pressing buttons on your controller should now log the name of buttons being pressed. This will be a lot easier to understand than “button 5″ (which, in my case, is on the D-pad).
Step 4: Detecting Axis Events
Detecting axis events is basically keeping track of where the left and right analog sticks on the gamepad are positioned using the “MozGamepadAxisMove” event.
Add the new event handler and callback function.
function moveAnalogSticks(evt) { console.log(evt.axis, evt.value); } window.addEventListener("MozGamepadAxisMove", moveAnalogSticks);This is what we get – confusing, right?
There is only one event fired by both analog sticks; each event gives us one of four possible axis and a value between -1.0 and +1.0. Axis 0 and 1 belong to left analog stick and axis 2 and 3 belong to the right.
In the diagram above you’ll see axis 0 and 2 correspond to the x axis, and 1 and 3 correspond to the y axis. By using both the x and y axis for each individual analog stick, you can figure out which way the analog stick is facing!
On different gamepads, you may have other axes. For instance, the shoulder triggers on an Xbox controller are also analog.
Step 5: Putting It Into Practice
That covers all of the events that we can currently take from a gamepad, so let’s put what we’ve learnt into practice.
Now, I don’t want to go too heavily into the game development side of things, as we are focusing on what we use to control games themselves. One of the key things to look at, though, is switching control schemes. As not everyone will have a gamepad ready to hand, we need to make sure we provide controls for both the keyboard and gamepad.
Step 6: Setting Up Your Canvas
To get a small demo up and running, create a
canvaselement in your html file with an id of “game” and set the width to 600 and height to 540. As you may know, the canvas element is commonly used to render HTML games on.You will also want to copy the “ship.png” and “space.jpg” images from the source download to your working folder as these are what we’ll be rendering to the canvas. Alternatively, find some graphics of your own to have a play with!
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Introduction to the Gamepad API</title> </head> <body> <h1>Gamepad API</h1> <canvas id="game" width="600" height="540"></canvas> <script src="gamepad.js"></script> </body> </html>Step 7: Creating the Game Loop
Now that the
canvaselement is in our DOM, we want to create a game loop to render our game.I’m using a shim for “requestAnimationFrame” by Paul Irish that will be the base for our loop. Next, we get the 2D context of the
canvaswhich we’ll use to draw on and create two new image objects, one for the background and one for our spaceship.Next, the player object. It has x and y coordinates which keep track of where it should appear on the canvas; four direction states (up, down, left and right) so we can know which way the ship is moving; a
render()function, which first callsupdatePosition()and then draws the image of the ship onto the canvas based on the x and y coordinates, and finally theupdatePosition()function itself, which tests to see which way the ship is set to move and updates its position accordingly.var player = { x: 200, y: 250, up: false, down: false, left: false, right: false, render: function() { this.updatePosition(); ctx.drawImage(ship,this.x,this.y); }, updatePosition: function() { this.up ? this.y-- : false; this.down ? this.y++ : false; this.left ? this.x-- : false; this.right ? this.x++ : false; } }After that we have our “renderGame” function which draws the space background image onto the
canvasfirst, then draws our spaceship on top of that.And finally, our loop. This function calls itself again and again, each time calling our “renderGame” function.
function renderGame() { ctx.drawImage(space,0,0); player.render(); } ;(function animloop(){ requestAnimFrame(animloop); renderGame(); })();Your canvas should now have a nice space looking background with a spaceship sat in the middle of it – not too exciting, I know. So let’s add some controls!
Step 8: Hooking Up the Ship’s Controls
In our player code we named the four buttons which we want to control our ship with. These match up to the names of the buttons inside the
ps3Buttons[]array. So, all we have to do is modify ourbuttonPressed()function ever so slightly and we’ll be moving.var player = { ... up: false, down: false, left: false, right: false, ... }Now when a gamepad button is pressed or released it will set its state within the player object, so when the “up” button is pressed,
player.up = true/falsewill be set.function buttonPressed(evt, pressed) { console.log(evt.button, pressed); player[ps3Buttons[evt.button]] = pressed ? true : false; }Head back over to your demo and you should be able to move your ship around!
Step 9: Adding a Keyboard Fallback
As not everyone playing your game will have a gamepad, you’ll probably still want to allow them to play the game with a keyboard.
Lets first create a new
keys[]array, and map the keyboard’s arrow keys’keyCodeproperties to the equivalent buttons on the gamepad. This will allow us to reusebuttonPressed()function that the gamepad utilises.var gamepadActive = false, ps3Buttons = new Array(), keys = new Array(); ps3Buttons[12] = 'triangle', ps3Buttons[15] = 'square', ps3Buttons[14] = 'cross', ps3Buttons[13] = 'circle', ps3Buttons[4] = 'up', ps3Buttons[7] = 'left', ps3Buttons[6] = 'down', ps3Buttons[5] = 'right', ps3Buttons[10] = 'L1', ps3Buttons[8] = 'L2', ps3Buttons[11] = 'R1', ps3Buttons[9] = 'R2', ps3Buttons[1] = 'L3', ps3Buttons[2] = 'R3', ps3Buttons[16] = 'PS', ps3Buttons[0] = 'select', ps3Buttons[3] = 'start'; keys[38] = 4; keys[37] = 7; keys[40] = 6; keys[39] = 5;Now we need a “onkeyup” and “onkeydown” event listener for the arrow keys. When a key is pressed or released, we make sure that a gamepad is not in use. Then we prevent the arrow key from doing its usual task (scrolling the browser window up or down in this case) and then call the same
buttonPressed()function that the gamepad calls.To do this, a fake event object is passed with the key’s “keyCode” mapped to an item in the
keys[]array, which in turn, passes the corresponding gamepad button ID.window.onkeydown = function(evt) { if (gamepadActive == false) { evt.preventDefault(); buttonPressed({ button: keys[evt.keyCode] }, true); } } window.onkeyup = function(evt) { if (gamepadActive == false) { evt.preventDefault(); buttonPressed({ button: keys[evt.keyCode] }, false); } }This should now let you use the arrow keys for controlling the ship when a gamepad isn’t plugged in, while still letting the gamepad take over when it’s present.
Conclusion
So we’ve covered the basics of connecting a gamepad to your computer, learnt how to hook into the events that the gamepad fires, and then use them in practice. Not forgetting, the crucial fall-back support for the keyboard!
A quick challenge for those of you with a controller other than a PS3 Dual Shock: adjust the button mapping based on whichever controller is plugged in.
Thank you for taking the time to learn about the Gamepad API. If you have any questions, please leave them in the comments.
In the previous Quick Tips, we’ve looked at collision detection: essentially, detecting that two shapes have overlapped. Now, we’re ready to look at collision reaction: making something happen due to a collision. In this Quick Tip, we’ll look at the reactions of reflection and sliding.
Final Result Preview
Let’s look at the end result we’ll achieve at the end of this tutorial. Each Flash demo has a restart button; click it to reset the position of the circles at the top of stage.
The first demo shows off reflection:
The second shows sliding:
Step 1: The Reflection Formula
I’ve run through this topic several rounds with students, and experience has taught me that the head-on approach of explaining vector math to freshers results in blank faces and confused minds. So instead of putting up a Math lecture here, I shall refer those who are interested in investigating this topic further to Wolfram’s page on reflection.
Here, I shall simplify my explanations with diagrams below. Recall vector addition:
Now, observe the diagram below. A is the circle’s velocity before a collision, and A’ is its velocity after the collision.
It’s obvious that
A' = A + 2 V(Ap), whereV(Ap)represents the vector with a magnitude of Ap, in the direction of the left normal. (You can see this by following the dashed lines.)In order to obtain
V(Ap), we shall project A onto the left normal.Step 2: Implementation
Here comes the ActionScript implementation of reflection. I’ve highlighted the important parts. Line 67 – 69 is to calculate
V(Ap) (v_leftNormSeg2) and line 70 implements the formula. You may refer to the full Actionscript underReaction1.as.(You should recognise most of the code from the previous Quick Tip.)
private function refresh(e:Event):void { for (var i:int = 0; i < circles.length; i++) { //calculating line's perpendicular distance to ball var c1_circle:Vector2D = new Vector2D(circles[i].x - x1, circles[i].y - y1); var c1_circle_onNormal:Number = c1_circle.projectionOn(leftNormal); var c1_circle_onLine:Number = c1_circle.projectionOn(line); //if collision happened, undo movement if (Math.abs(c1_circle_onNormal) <= circles[i].radius && line.dotProduct(c1_circle) > 0 && c1_circle_onLine < line.getMagnitude()){ //redefine velocity var v_leftNormSeg2:Vector2D = leftNormal.clone(); var leftNormSeg2_mag:Number = Math.abs(velos[i].projectionOn(leftNormal)) v_leftNormSeg2.setMagnitude(leftNormSeg2_mag); velos[i] = velos[i].add(v_leftNormSeg2.multiply(2)); } circles[i].x += velos[i].x; circles[i].y += velos[i].y; } }Step 3: An Interactive Version
Take note that this reflection formula is applicable to line of any gradient. In fact, you can program your line to be adjustable at runtime and see it reflecting circles like the Flash presentation below. Just click and drag near the lower end of the to redefine it.
Step 4: Sliding Along Line
The concept of sliding along the line is almost identical to reflection. Observe the diagram below.
The vector of slide is
A' = A + V(Ap)withV(Ap)representing a vector with magnitude ofAp. Again, to obtain Ap we shall project A onto the left normal.Note that as the circle is sliding along the line, it is colliding with the line. Of course, collision points differ among circles that collide onto line, so some overlap the line as they move along it. This doesn’t look good, so we’ll have to reposition them.
Step 5: Redefine Location
Now, let’s reposition circles on the line while maintaining their contact with line. Refer to the diagram below.
An important variable to calculate is the projection of A along line. The radius of circle is readily available, and we already have B, so we can form the vectors of B and C. Adding the two will give us A, the exact location to reposition circle. Simple!
The Flash presentation below is coded according to the mentioned idea. But there is one problem: the circles jitter along the line.
There’s one final detail we missed. Diagram above shows magnitude of C should be equivalent to radius of circle. However, this will position circle back above the line. Since there’s no collision detected there, the circle will fall onto the line again, which in turn will flag the collision detection and cause the circle to be repositioned.
This cycle will repeat until the is past the end of the line segment; the visual result of this cycle is the jittering effect.
The solution to this problem is to set the magnitude of C to slightly less than the radius of the circle:
(radius of circle - 1), say. Observe the Flash demo below which uses this idea:Step 6: Implementation
So here’s the important ActionScript snippet for sliding along the line. I’ve highlighted the important parts.
private function refresh(e:Event):void { for (var i:int = 0; i < circles.length; i++) { //calculating line's perpendicular distance to ball var c1_circle:Vector2D = new Vector2D(circles[i].x - x1, circles[i].y - y1); var c1_circle_onNormal:Number = c1_circle.projectionOn(leftNormal); var c1_circle_onLine:Number = c1_circle.projectionOn(line); //check for collision if (Math.abs(c1_circle_onNormal) <= circles[i].radius){ //check if within segment //if within segment, reposition and recalculate velocity if (line.dotProduct(c1_circle) > 0 && c1_circle_onLine < line.getMagnitude()) { //repostion circle var v_lineSeg:Vector2D = line.clone(); v_lineSeg.setMagnitude(c1_circle_onLine); var v_leftNormSeg1:Vector2D = leftNormal.clone(); v_leftNormSeg1.setMagnitude(circles[i].radius - 1); //v_leftNormSeg1.setMagnitude(circles[i].radius); //uncomment this to check out the error: jittering effect var reposition:Vector2D = v_lineSeg.add(v_leftNormSeg1) circles[i].x = x1+reposition.x; circles[i].y = y1+reposition.y; //redefine velocity var v_leftNormSeg2:Vector2D = leftNormal.clone(); var leftNormSeg2_mag:Number = Math.abs(velos[i].projectionOn(leftNormal)) v_leftNormSeg2.setMagnitude(leftNormSeg2_mag); var veloAlongLine:Vector2D = velos[i].add(v_leftNormSeg2); circles[i].x += veloAlongLine.x; circles[i].y += veloAlongLine.y; } //if not in segment (e.g. slide out of segment), continue to fall down else { circles[i].x += velos[i].x; circles[i].y += velos[i].y; } } //No collision in the first place, fall down else { circles[i].x += velos[i].x; circles[i].y += velos[i].y; } } }Conclusion
Hope this is helpful. Thanks for reading. Prompt me if there are questions, and I’ll see you next Quick Tip.
In this tutorial I will introduce you to FZip, an AS3 Library that lets you open zip files inside your Flash projects. This can save a lot of bandwidth; in this tutorial we will load an 2.5MB zip file which contains 9.3MB worth of assets.
Final Result Preview
Let’s take a look at the final result we will be working towards. Click here to open a SWF that will in turn load a zip file full of images, and display them in a tiled grid.
(The blurring visible on some icons is due to Flash automatically attempting to scale them up to 32x32px, even though those particular images are 16x16px.)
Step 1: Getting the Library and Zip Archive
You will need to grad a copy of the FZip library from Claus Wahlers’ github.
Extract the library. Inside the src folder there is a folder named “deng”; copy this folder to the folder where you will store your FLA.
Next we need a zip archive to work with. I choose the WooFunction icon set, available for free from woothemes.com.
Save this to the same directory where you will store your FLA.
Step 2: Create New Flash Document
Open a new FLA and give it the following properties:
Save this as
fzip.fla.Step 3: Add Components to Stage
Go to Window > Components and drag a TileList component to the stage.
Under “Component Parameters” set the following properties:
columnCount: 16columnWidth: 32rowCount: 8rowHeight:32Give the TileList the instance name
imageTileList, and set the following properties in the “Position and Size” panel:Next select the Text Tool and make sure the following properties are set in the “Character” panel:
Now drag a TextField onto the stage, and give it the instance name
imagesLoaded. Make sure the TextField is set to “Classic Text” and “Dynamic Text”, respectively, and set the following properties:Step 4: Create new AS3 Document
Go to File > New and choose “Actionscript File”.
Save this file as
Main.as.Step 5: Package, Imports and Constructor
Inside
Main.asadd the following:private function demonstrate():void package { import flash.display.MovieClip; import deng.fzip.FZip; import deng.fzip.FZipFile; import flash.display.Loader; import flash.net.URLRequest; import flash.events.*; import fl.controls.TileList; import fl.data.DataProvider; public class Main extends MovieClip { public function Main() { setupZip(); } } }Here we imported the classes we will need for this tutorial, and set up the
Main()constructor function.Step 6: Add Variables
Define the following variables above
public function Main():Here we add some variables we will need throughout the tutorial. See the comments for their usage.
Step 7: setupZip()
Add the following new function below
Main():private function setupZip():void{ zip = new FZip(); zip.addEventListener(Event.OPEN, onOpen); zip.addEventListener(Event.COMPLETE, onComplete); zip.load(new URLRequest("wootheme.zip")); //change this to match your zip file's URL imageTileList.visible = false; }Here we create a new instance of the FZip class, add two event listeners, and load our zip file. Last, we set
imageTileListto be invisible (We don’t want it to show until all the images from the zip have loaded).Step 8: onOpen()
Add the following new function beneath the
setupFzip()function you entered above:private function onOpen(evt:Event):void { addEventListener(Event.ENTER_FRAME, onEnterFrame); }This function gets called when the zip archive has been opened. Here we add an
ENTER_FRAMEevent listener.Step 9: onComplete()
Add the following code new function beneath the
onOpen()function you entered in the step above.private function onComplete(evt:Event):void { done = true; }This function gets called when there are no more files to process from the zip archive.
Step 10: onEnterFrame()
Add the following beneath the
onComplete()function you entered above. This function will be triggered every frame after the zip file has been opened:private function onEnterFrame(evt:Event):void { //Only load 32 files per frame, to save processing power for(var i:uint = 0; i < 32; i++) { // any new files available? if(zip.getFileCount() > numFiles) { //yes so get it var file:FZipFile = zip.getFileAt(numFiles); // is this a png in the icons folder? if(file.filename.indexOf("woofunction-icons") == 0 && file.filename.indexOf(".png") != -1) { var loader:Loader = new Loader(); loader.loadBytes(file.content); tileListDp.addItem({source:loader}); numFilesLoaded++; } numFiles++; } else { // no new files available // check if we're done if(done) { removeEventListener(Event.ENTER_FRAME, onEnterFrame); removeChild(imagesLoaded); imageTileList.visible = true; imageTileList.dataProvider = tileListDp; } //Exit the Loop break; } } imagesLoaded.text = numFilesLoaded + " Images Loaded"; }Here’s the meat of the code.
Since this is running every frame, we’ll place an artificial restriction on the number of files within the archive that we deal with at once. That’s the purpose of the for-loop.
zip.getFileCount()reveals how many files are in the zip;numFilesstores how many files we’ve dealt with so far. So, line 5 checks whether there are still more files to deal with.If there aren’t any files left, we skip to line 17 and just do some basic clearup: remove the
ENTER_FRAMElistener, remove the “loading” text field , make the tile list visible, and link it to the data.If there are files left, we get the next one in our list using
numFilesas an index. We then check whether it’s a PNG from the icons folder; since we know the structure of the zip beforehand, we can cheat and just check whether the file’s name and path contains “woofunction-icons” and “.png”.To get the image from the zip and into a DisplayObject, we can use a Loader. This class is often used to load an image from a URL, but here we’re using its loadBytes() method to get the data from the ByteArray created by FZip.
Since
LoaderextendsDisplayObject, we can just add it straight to the TileList’s DataProvider. Then we increment bothnumFilesLoadedandnumFiles.Why do we have two integers to keep track of how many files are loaded? Well,
numFileskeeps count of all the files we’ve examined from the zip, whereasnumFilesLoadedkeeps count specifically of the image files that we’ve loaded into the DataProvider. It’s the latter variable that we use to update the “loading” text at the end of the function.Conclusion
FZIP is an amazing little utility to save some loading time and bandwidth. I hope you’ve found this tutorial useful, and thanks for reading!
Building real time networked games and applications can be challenging. This tutorial will show you how to connect flash clients using Cirrus, and introduce you to some vital techniques.
Premium Preview
Click the start button in the SWF above to create a ‘sending’ version of the application. Open this tutorial again in a second browser window, copy the nearId from the first window into the textbox, and then click Start to create a ‘receiving’ version of the application.
In the ‘receiving’ version, you’ll see two rotating needles: one red, one blue. The blue needle is rotating of its own accord, at a steady rate of 90°/second. The red needle rotates to match the angle sent out by the ‘sending’ version.
(If the red needle seems particularly laggy, try moving the browser windows so that you can see both SWFs at once. Flash Player runs EnterFrame events at a much lower rate when the browser window is in the background, so the ‘sending’ window transmits the new angle much less frequently.)
Read the Full Tutorial
Premium members can access the full tutorial right away!
If you’re not yet a Premium member, you can still read the first few steps of the tutorial.
Tuts+ Premium Membership
We run a Premium membership system 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, Photo Premium, and the new Mobile 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, circle us on Google+, like us on Facebook, and grab the Activetuts+ RSS Feed to stay up to date with the latest tutorials and articles.
Building a button from a bitmap can be bothersome. If you’re using the Flash IDE, you can create a mask to determine which pixels are part of the button and which aren’t, but in any other workflow, the entire rectangle containing the bitmap – including any transparent pixels – will be clickable. In this tutorial, you’ll learn how to automatically make all transparent pixels unclickable, with just a few lines of code.
Final Result Preview
Let’s take a look at the final result we will be working towards:
Notice that the hand cursor only appears when hovering over the actual image; even the gaps in the hair don’t cause it to appear. And it’s not just for show: the button presses only register when clicking on these areas.
Introduction: What’s So Special?
At a first glance, the SWF above appears extremely simple. But look closer! Notice how in the demo above, a button press is only counted if you click somewhere on the actual image. This isn’t what would normally happen. Since a bitmap image is always a rectangle, clicking anywhere inside its rectangle normally would count as a button press. Take a look at the example below, where I have outlined the boundary rectangle of our image. Here, you can click anywhere inside the rectangle, including the transparent areas.
As you can see, this is not what we want! For starters, a user could accidentally click the button when he doesn’t mean to. In addition, it looks strange when a hand cursor appears over blank space. In this tutorial, you’ll learn how to easily correct these problems.
If you aren’t already familiar with Object-Oriented Programming or FlashDevelop, I recommend checking out the provided links before starting this tutorial.
Step 1: Getting Started
Open up FlashDevelop and create a new AS3 project (Project > New Project) and call it something like
BitmapButtonProj. On the right, open up the src folder and double-clickMain.asto open it. Add a new class to your project (right-click /src/ > Add > New Class) calledBitmapButtonStep 2: Embedding an Image
We now need an image to work with. Here is the one I’m using:
To use a bitmap image (such as a .jpeg file or a .png file) in Actionscript 3, we have to embed it. FlashDevelop makes this easy. After saving the above image somewhere, right-click the lib folder on the right, mouse over Add, and select the Library Asset option.
If you want to use your own image, be sure to select one with transparency.
The image you selected will now appear in the lib folder in FlashDevelop. Right-click the image and select Generate Embed Code.
public class Main extends Sprite { [Embed(source = "../lib/face.png")] private var ButtonImg:Class;This code
embedsthe image in to our project. Whenever you embed an image, you need to define on the next line a class that represents the image you embedded. In this case, our class is calledButtonImg.Step 3: Adding a TextField
Next, we’ll add a
TextFieldto display how many times we have clicked the button (which will be added next). Add this toMain():The code above simply formats the text to display at the top center of our project. Don’t miss how we declared our
TextFieldin line 15.Step 4: Adding the Button
This code should also go in
Main():When we instantiate our
BitmapButtonin line 36, we pass our embedded image class as a parameter. This will be used by theBitmapButtonclass. After this, we can simply treat ourBitmapButtoninstance like any otherDisplayObject: we can position it and add aMouseEvent.CLICKlistener as we normally would.Step 5: Making the Button Do Something
Add this event handler function to
Main.as:private function onButtonClick(e:MouseEvent):void { numClicks++; clicksTextField.text = "Button Presses: " + numClicks; }The final piece of code in our
Mainclass is the event listener for button clicks. In it, we simply add one to the number of clicks,numClicks,and update the text inclicksTextField.Step 6: The BitmapButton Constructor
Flip over to
BitmapButton.as. First, import these classes:Then, declare these:
You must make sure that the
BitmapButtonclass extendsSprite, since aBitmapby itself cannot have any mouse interactivity. (TheBitmapclass doesn’t extend InteractiveObject.)public function BitmapButton(ImageData:Class) { var image:Bitmap = new ImageData(); addChild(image); bitmapData = image.bitmapData; addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove); addEventListener(MouseEvent.CLICK, onClick); }In our
BitmapButtonconstructor, we accomplish a couple of important things.Bitmap, calledimage, from the image class passed as a parameter to the constructor.Bitmapas a child of ourSprite.bitmapDatato equal thebitmapDataof our image.CLICKandMOUSE_MOVEevent listeners.Step 7: The MOUSE_MOVE Event Listener
private function onMouseMove(e:MouseEvent):void { var pixel:uint = bitmapData.getPixel32(mouseX, mouseY); useHandCursor = buttonMode = ((pixel >>>24) > THRESHOLD); }Our simple looking
MOUSE_MOVEevent listener is the real brains behind our class. Its main purpose is to determine whether the mouse cursor is over a transparent pixel or not. Let’s look at how it does this.The getPixel32() Function
The first line gets the color and transparency of the pixel that the cursor is currently over. To do this, we use the
getPixel32()method of the variablebitmapData(which, remember, is the bitmap data representation of our image).We must pass an x- and y-coordinate to
getPixel32(), so naturally we use the mouse’s position.The call then returns a
uintrepresenting the color and transparency of the pixel at the location we supplied.Colors in Flash are normally treated as a hexadecimal
uintin the format RRGGBB. The first two digits represent the amount of red in the color, the next two, green, and the final two, blue. However,getPixel32()provides us with a specialuintrepresenting our pixel in the format AARRGGBB. Here, the first two digits represent the alpha, or the amount of transparency, from 00 (clear) to FF (opaque).So, for example,
FF980000would represent a fully opaque red color, while00980000would represent a fully transparent red color. You’ll typically see these represented as0xFF980000or0x0098000: the “0x” lets you (and Flash!) know that the number is in hexidecimal (0-f), rather than decimal (0-9).The Bitwise Unsigned Right Shift Operator
At this point, we have a
uintcalledpixelwhich is holding the color and alpha of the pixel beneath our mouse in the AARRGGBB format. Unfortunately, this is too much information. All we care about is the transparency of this pixel, or the AA part.You could write a mathematical expression to obtain this section – in fact,
int(pixel/Math.pow(16,6))would work. This is a somewhat awkward statement, though, and slower performance-wise than another option we have: the bitwise unsigned right shift operator,>>>.Our variable
pixelis just a binary number to Flash. We normally write it in hexadecimal just to make it more readable. Without going into too much detail, every digit of a hexadecimal number can be represented by a string of four binary digits, each one either a 0 or a 1. (So, hexadecimal uses digits 0-f, decimal uses 0-9, and binary uses 0-1.)Say we have a hexadecimal number, D4. In binary, this would be represented as 11010100. Notice how we use eight binary digits for the binary representation: four times as many as in hexadecimal.
With this is mind, let’s examine what the
>>>operator actually does. Lets use our previous example, the hexadecimal number D4, or0xD4for clarity. Now let’s use>>>as so:Notice that 4 is a normal decimal representation of a number (there’s no “0x” at the start). This expression essentially shifts every binary digit in D4 four places to the right, and forgets about any digit that would go off the end of the number.
0xD4, in binary, is 11010100. Apply four shifts, and it becomes 1101. In hexadecimal, this is 0xD.
If you’re having trouble understanding this, imagine the binary digits as blocks sitting at the right side of a table. The
>>>operator is just like pushing the blocks to the right. Here’s our original binary number:Now here’s our new number, after we do
0xD4 >>> 4:Notice how after we shifted 0xD4 by 4 bits, we ended up with just 0xD? That’s not a coincidence. As said before, each hexadecimal digit is made up of 4 binary digits – so, each time we shift it to the right by 4, we essentially knock one hexadecimal digit off the end. You can probably see where we are going with this!
Back to our pixel, in 0xAARRGGBB format. If we shift it by 24, we are actually shifting by 6 hexadecimal digits. This means the RRGGBB portion gets knocked off, and we end up with just the 0xAA part left, which is our alpha component.
A quick numerical example: Say our pixel is equal to FF980000. In binary, this is 1111 1111 1001 1000 0000 0000 0000 0000. (Each group of 4 digits represents one hexadecimal digit.) When we shift this by 24, we simply end up with 1111 1111, or FF, our two transparency digits.
Take a look at it again:
Okay, the
(pixel >>> 24)part makes sense now, but what about the rest?It’s easy. We check whether our alpha component (the result of
pixel >>> 24) is greater than the value ofTHRESHOLD(which is currently set to 0). If it is,useHandCursorandbuttonModeare set to true, which will make the cursor change to a hand. This makes our image seem like a button.If our alpha component is less than or equal to
THRESHOLD, the cursor will remain normal, since the mouse is over a (semi-)transparent pixel. Since we have it set to 0, only fully transparent pixels will not be included as part of our button, but you could set this to, say, 0×80, and then it would display the hand cursor for anything that’s more than half transparent.Step 8: The CLICK Event Listener
private function onClick(e:MouseEvent):void { if (!useHandCursor) { e.stopImmediatePropagation(); } }The final part of our
BitmapButtonclass is theMouseEvent.CLICKevent listener. This function will be called every time our image is clicked, no matter whether that pixel is transparent or not. (Changing the mouse cursor as we did before will not affect the actualMouseEvent.)So, every time there is a click, we check the
useHandCursorproperty. If it istrue, this means the mouse is over a normal pixel in our image, and we don’t need to do anything. This makes sense – the event will then continue on to the event listener we added inMain.as. However, ifuseHandCursoris false, we have to do something to stop the event from continuing on to other event listeners.For this, we use the
stopImmediatePropagation()method that allEventobjects have. Simply put, this stops the flow of the event, and no more event listeners will receive it. So, our event listener function inMain.aswill never be called.Warning: this could have a nasty side effect – any global event listener will not get the event either. If you are worried about this, you can try adding the line
parent.dispatchEvent(e.clone());aftere.stopImmediatePropogation(). While this is beyond the scope of the tutorial, I recommend reading more about the event system here.Conclusion
This wraps up our tutorial! Thanks for reading, and I hope you have learned something new.
A note of caution when using our
BitmapButtonclass – otherMouseEventswill still work as normal, since we only dealt withMouseEvent.CLICK. If you wanted, you could use the same technique we used forMouseEvent.CLICKand apply it to other events, such asMouseEvent.MOUSE_DOWN.Our
BitmapButtonclass allows us to quickly and easily make great buttons from bitmap images with transparency, as we did in this demo. If you have any questions, I’ll be glad to answer them, just leave a comment below.Are you looking to start your new year with a fresh set of prints to help promote your design business? Today, our friends at UPrinting are kicking off 2012 by giving away business cards, posters, or postcards to 36 lucky Tuts+ readers. To enter, all you have to do is submit your entry using the form below and select which prize you would prefer.
Update: The winners for this giveaway have been selected. Thanks to everyone who participated.
If you are a frequent reader of our site, chances are, you are already familiar with UPrinting. They are an online printer that offers business cards, poster printing, postcard printing, and much more. UPrinting is a frequent sponsor of this site and we are very excited to partner with them on another giveaway.
Submit Your Entry
Up for Grabs
Today, UPrinting is giving you several options to choose from. You can enter to win 500 standard business cards, 100 postcards, or 1 poster print. The choice is yours! More details can be found below.
500 Standard Business Cards
2" x 3.5" US Standard, 2" x 2" Square, 1.75" x 3.5" Slim
14pt Cardstock Gloss / Matte, 13pt Cardstock Uncoated
Front and Back Printing
3 Business Days Turnaround time
1 Poster Print
18" x 24"
Semi Gloss / High Gloss
1 Business day print turnaround time
100 Postcards
5" x 7"
14pt Cardstock Gloss
Front Only Printing
2 Business days print turnaround time
Rules