search
top

Create a Pong Game in HTML5 With EaselJS – Tuts+ Premium

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

HTML5 EaselJS Pong game tutorial
Click to play the demo

In this tutorial, we’ll create a clone of the classic game, Pong, in HTML5, using the EaselJS library. The game will have multiple screens, sound effects, and a (very simple) AI opponent.


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.



View full post on Activetuts+

Easy, Fluid Keyboard Movement in AS3 With the Input Class

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 _max then 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 dx and dy variables, 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 dx or dy. 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 the dx by -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.



View full post on Activetuts+

An Introduction to the HTML5 Gamepad API

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.

The logged event of connecting a gamepad

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.

The logged event of disconnecting a gamepad

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).

The logged button id's

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?

The logged button id's

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.

The logged button id's

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 canvas element 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 canvas element 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 canvas which we’ll use to draw on and create two new image objects, one for the background and one for our spaceship.

// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
window.requestAnimFrame = (function(){
  return  window.requestAnimationFrame       ||
          window.webkitRequestAnimationFrame ||
          window.mozRequestAnimationFrame    ||
          window.oRequestAnimationFrame      ||
          window.msRequestAnimationFrame     ||
          function(/* function */ callback, /* DOMElement */ element){
            window.setTimeout(callback, 1000 / 60);
          };
})();

var canvas  = document.getElementById('game'),
    ctx     = canvas.getContext('2d'),
    ship    = new Image(),
    space   = new Image();

space.src = "space.jpg";
ship.src  = "ship.png";

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 calls updatePosition() and then draws the image of the ship onto the canvas based on the x and y coordinates, and finally the updatePosition() 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 canvas first, 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 our buttonPressed() 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/false will 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’ keyCode properties to the equivalent buttons on the gamepad. This will allow us to reuse buttonPressed() 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.



View full post on Activetuts+

Quick Tip: Collision Reaction Between a Circle and a Line Segment

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:

Vector addition

Now, observe the diagram below. A is the circle’s velocity before a collision, and A’ is its velocity after the collision.

Reflection

It’s obvious that A' = A + 2 V(Ap), where V(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 under Reaction1.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.

Sliding along

The vector of slide is A' = A + V(Ap) with V(Ap) representing a vector with magnitude of Ap. 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.

Reposition circle on line

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!

Vector calculation of the exact location

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.



View full post on Activetuts+

Quick Tip: Use FZip to Open Zip Files Within AS3

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:

  • Size: 550x400px
  • Background Color: White

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: 16
  • columnWidth: 32
  • rowCount: 8
  • rowHeight:32

Give the TileList the instance name imageTileList, and set the following properties in the “Position and Size” panel:

  • X: 20
  • Y: 68
  • W: 100
  • H: 100

Next select the Text Tool and make sure the following properties are set in the “Character” panel:

  • Size: 50pt
  • Color: Black

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:

  • X: 54
  • Y: 161
  • W: 454
  • H: 60

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.as add 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():

private var zip:FZip; // Instance of FZIP class
private var numFiles:int = 0; //Number of files
private var numFilesLoaded:int = 0; //Number of files loaded
private var done:Boolean = false; //Done processing zip archive?
private var tileListDp:DataProvider = new DataProvider();//Data provider for the TileList

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 imageTileList to 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_FRAME event 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; numFiles stores 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_FRAME listener, 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 numFiles as 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 Loader extends DisplayObject, we can just add it straight to the TileList’s DataProvider. Then we increment both numFilesLoaded and numFiles.

Why do we have two integers to keep track of how many files are loaded? Well, numFiles keeps count of all the files we’ve examined from the zip, whereas numFilesLoaded keeps 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!



View full post on Activetuts+

Page 1 of 23612345...102030...Last »
top