logo
468x60-2-495


  • Home
  • Privacy Policy
  • About
search
Sep 7, 2011 Posted on Sep 7, 2011 in Hints and Tips | 10 comments

Create a Flickr-Based Pairs Game With Flash: The Interface – Active Premium

In the first part of this members-only tutorial, you built the basic structure of a Flash card-matching game, using Flickr to get its images. Now you’ll complete the game, adding multiple levels and a full interface.

Become a Premium member to follow this awesome tutorial, as well as hundreds of other advanced tutorials and screencasts.


Preview

Here’s what you’ll create by the end of this tutorial:


Active Premium Membership

Activetuts+ Premium Membership

We run a Premium membership system which costs $9 a month (or $22 for 3 months!) which periodically gives members access to extra tutorials, like this one! You’ll also get access to Psd Premium, Vector Premium, Audio Premium, Net Premium, Ae Premium, Cg Premium and Photo Premium too. If you’re a Premium member, you can log in and download the tutorial. If you’re not a member, you can of course join today!

Also, don’t forget to follow @envatoactive on twitter and grab the Activetuts+ RSS Feed to stay up to date with the latest tutorials and articles.



View full post on Activetuts+

Aug 30, 2011 Posted on Aug 30, 2011 in Hints and Tips | 10 comments

Create a Flickr-Based Pairs Game With Flash: The Basics – Active Premium

In this tutorial, available exclusively to Tuts+ Premium members, you’ll learn to build a card-matching game, in Flash, that uses Flickr as the source of its images. The first part of this two-part series will get you started, leaving you with a basic game structure that you can improve on later.

Become a Premium member to follow this awesome tutorial, as well as hundreds of other advanced tutorials and screencasts.


Preview

Here’s what you’ll create in this part of the tutorial:

In the second part of the series, you’ll add multiple levels and improve the interface, ending up with this:


What You’ll Learn

Naturally, you’ll learn how to search for and retrieve images from Flickr, so that you can use them in other apps and games. But since this tutorial explains how to build a Pairs game from scratch, you’ll also learn about game logic (such as how to detect a match) and interface tweaks (such as the way the cards “flip” over when clicked).


Active Premium Membership

Activetuts+ Premium Membership

We run a Premium membership system which costs $9 a month (or $22 for 3 months!) which periodically gives members access to extra tutorials, like this one! You’ll also get access to Psd Premium, Vector Premium, Audio Premium, Net Premium, Ae Premium, Cg Premium and Photo Premium too. If you’re a Premium member, you can log in and download the tutorial. If you’re not a member, you can of course join today!

Also, don’t forget to follow @envatoactive on twitter and grab the Activetuts+ RSS Feed to stay up to date with the latest tutorials and articles.



View full post on Activetuts+

Aug 19, 2011 Posted on Aug 19, 2011 in Hints and Tips | 10 comments

Create a Lytro-Esque Dynamically-Focused Photo in Flash

In this tutorial, I’ll show you how to mimic the photos created by the Lytro camera. The end result will allow you to create your own dynamic and interesting pictures to display on the web, allowing viewers to alter the focal point! No special camera required — you can use any camera which allows manual focusing (which includes most phone cameras).


Final Result Preview

Let’s take a look at the final result we will be working towards:

Click a guitar to focus on it.


Step 1: Setting Up

Camera

Before you start, make sure you have a camera that can select a focus point manually. In this tutorial you cannot use the focus lock method to change points of focus. A DSLR would be best when doing this tutorial but a compact camera with decent manual controls will be fine! Also make sure you have a sturdy tripod.

First, set your camera up on a tripod. Make sure it’s secure and not going to fall or move in any way because we don’t want the composition to change at all between the shots.

Ensure your subject remains static throughout taking your images, otherwise in the final result, your images will appear to move, and that wouldn’t look good at all!


Step 2: Shooting Modes

Manual Mode

To get a really effective image, your need to set your camera to a mode were you can control the aperture. This will allow you to set a wide aperture (Small f number), and blow the background (or foreground) out of focus. Remember, the lower the f number, the greater the blur!

Aperture Priority mode (A/Av on Canon) is a great mode which will allow you to set the aperture, and the camera will choose all the other settings for you. Though be warned, this mode may produce two images with slightly different exposures, and each may have slightly different color so be careful!

Manual mode (M) would be better because you can dial in the exposure settings, and keep your shots consistent. Manual white balance settings are also a good idea to keep the colors in your images consistent.

For this tutorial, I would recommend anywhere around f1 – f5.6, depending on the distance from your subject and the camera.


Step 2: Shooting

AutoFocus Markers

Now with a wide aperture selected on your camera (Small f number), move your AF marker over your foreground subject by using the directional pad on the back of your camera. Let the camera focus, and then shoot. Without moving your camera at all, select a new focus point in the background, focus, and then shoot as shown in the picture above.

A handy tip if you have a DSLR is to go into live view mode. Here you can select any point on the screen, allowing for a greater flexibility in choosing your points of focus. If you have a compact camera, use your control pad to select your focus point.

You should now have two images that are exactly the same, apart from the focal point. Keep shooting until you are happy with your composition and depth of field. Then transfer your photos to your computer and get started in Flash!


Step 3: Now Onto the Computer

Opening Photoshop

First set up a suitably named project folder. I always like to keep projects in clearly named folders so I can find them if I need to look back on what I’ve done at a later date. Here you will keep your original images, resized images, and your Flash/ActionScript files.

To make the images web-safe, we need to resize them. Open up your favourite image editor and just resize your photos to a suitable size you feel is appropriate. Here, I’ve resized them both to 600x400px, but you can use any size you wish, and make sure the ‘constrain proportions’ box is checked.

Next, save the images as a high quality JPEG. This is important because Flash will compress the whole file later, so losing image quality at this stage is pointless.


Step 4: Flash Setup

Resizing The Flash Stage

Boot up Flash, and open a new ActionScript 3.0 document. Click the edit button under the properties tab, and resize your stage to the size of your images; I picked 600x400px.


Step 5: Document Layers and Frames

Layers and Frames

Next, create two layers on your stage; name the bottom layer “Photos”, and the top layer “Buttons”. Add another key-frame on the “Photos” layer, but make sure on the “Buttons” layer, you just add another frame. See the picture above.

Next, import both your photos to the library, so drag and drop them both from your project folder into the library panel. Now make sure you have both of your photos on a different key-frame.

The photo layer is now complete!


Step 6: Button Masking

Masking With The Pen Tool
Filled In Mask

Now, we need to make the buttons that users will click on to change the focal point. To do this, we will mask the out of focus areas of both images, fill them, and then make them invisible.

First, select the first frame on the “Buttons” layer. Use the pen tool and mask the shape of the out of focus areas, and complete the path. You don’t need to be too accurate here; just make sure you get the general shape of the object.

Next, select the paint bucket tool and fill in the paths you just created. Delete the black stroke around the edges if necessary.


Step 7: Creating a Symbol

Converting To Symbol

Next, select the shapes and go-to Modify > Convert to Symbol (F8). Make sure the type of symbol is a button, and call this what you like. I have named my shape ‘Guitar1’.


Step 8: Making It Invisible

Invisible Button

Double click on your new button, and you will then enter the button isolation mode. Here you can edit the states of the button.

In this case, we want to make it invisible, so click and drag from the ‘UP’ state, and release over the ‘HIT’ state.

Then return to your original timeline and your shape should have a see-through mask. Congrats! You just made an invisible button!

Blue Mask

Step 9: Naming Your Button

Naming Buttons

The next step is to name this button. While it is selected, on the right panel, under the properties tab, name your button ‘btn_back’.


Step 10: More Button Masking!

Both Masks

Next, click on the next frame on your “Buttons” layer over the second image, so the other part of the image is out of focus. This just makes it clearer to see where to mask.

Now do exactly the same as before:

  • Mask the out of focus area with the pen tool.
  • Fill it with the paint bucket tool and delete the black stroke around the button.
  • Convert it to a symbol. (F8) (Remember to make sure the type of symbol is a button, and you call it a different name than before. I have named mine ‘Guitar2’.
  • Double click the new button, and drag from the ‘UP’ state, to the ‘HIT’ state.
  • Return to the timeline.

Now, name this new button, ‘btn_fore’.

You should now have two transparent buttons that span over both images as shown. They may overlap slightly, but this does not matter too much. The buttons layer is now complete!


Step 11: Preparing to Code Your File

Setting Up To Code

The final part is to create the code which will make the buttons work. This step involves the use of document classes so use this tutorial to help you understand them if you are new to them, or just want a refresh.

First, locate the ‘Class’ box in your FLA file under the properties panel. In here, type Main. This is now the name of your document class, and will be the name of your ActionScript file that you will be creating next.

Next, go to File > ActionScript settings. You need to un-check the box that says ‘Automatically declare stage instances’. This means that you have to now manually declare the two buttons in code, which is what we want. (The location of this dialog may be different in earlier versions of Flash, so if you cannot find it, use a search engine.)


Step 12: Building the Code

Coding Step 1

Next it’s time to create the ActionScript file that holds all our code in. Click File > New > ActionScript file.

Now save this as Main.as, and make sure it is held within the same file directory as the FLA file. Now, the FLA and ActionScript file are linked!

First, use this piece of code as your base:

package
{

	import flash.display.MovieClip;

	public class Main extends MovieClip
	{

		public function Main():void
		{

		}//Function

	}//Class

}//Package

I have included an import statement for MovieClip to start with, but we will be adding more statements as we go along.


Step 13: Building the Buttons

To start, you need to declare your 2 buttons as variables. These go between public class Main extends MovieClip and before public function Main():

public var btn_back:SimpleButton;
public var btn_fore:SimpleButton;

To make Flash understand what SimpleButton is, you need to add this with the other import statement at the top:

import flash.display.SimpleButton;

After you have done that, your code should look like this:

package
{

	import flash.display.MovieClip;
	import flash.display.SimpleButton;

	public class Main extends MovieClip
	{

		public var btn_back:SimpleButton;
		public var btn_fore:SimpleButton;

		public function Main():void
		{

		}//Function

	}//Class

}//Package

Step 14: Stopping on the First Frame

The next step is to make sure the clip stops on the first photo. To do this, simply add a stop() command inside the public function Main()

public function Main():void
{
	stop();
}//Function

Step 15: Adding Event Listeners

Coding Step 2

The next step is to add event listeners. These go inside public function Main(), and below the stop(); command you just added. You must have an event listener for both buttons; btn_back and btn_fore, so first create one for the back button:

btn_back.addEventListener(MouseEvent.CLICK, onClickBackground);

Now create another event listener for the next button. You need to simply change the name of both the button, and the name of the function:

btn_fore.addEventListener(MouseEvent.CLICK, onForeBackground);

First, I’m referencing the button name btn_back. Then I’m adding the event listener via .addEventListener. In the brackets, MouseEvent.CLICK is simply telling it to listen for a click on the button. Finally we’re telling it that, when the button is clicked, it should call onClickBackground() — we will create this function next.

Now, because we have used mouse events, we need to put an import statement at the top along with the other statements to make Flash understand what one is. Add this line below the other imports:

import flash.events.MouseEvent;

Your code should now look like this:

package
{

	import flash.display.MovieClip;
	import flash.display.SimpleButton;
	import flash.events.MouseEvent;

	public class Main extends MovieClip
	{

		public var btn_back:SimpleButton;
		public var btn_fore:SimpleButton;

		public function Main():void
		{
			stop();

			btn_back.addEventListener(MouseEvent.CLICK, onClickBackground);
			btn_fore.addEventListener(MouseEvent.CLICK, onClickForeground);
		}//Function

	}//Class

}//Package

Step 16: Adding Functions

Finally, two public functions need to be created to tell the buttons we made what to do. Add these inside the public class Main extends MovieClip, but below the public function Main():void.

The names of the functions must match the names in the event listeners. So in this case onClickBackground, and onClickForeground:

public function onClickBackground(evt:MouseEvent):void
{
	gotoAndStop(2);
}

public function onClickForeground(evt:MouseEvent):void
{
	gotoAndStop(1);
}

Inside the function, we need to tell Flash which frame to go to when the button is clicked. The simplest method is to use gotoAndStop(). The number inside the brackets denotes the frame number that Flash will go to. Simply add the number 2 for the onClickBackground function, and the number 1 for the onClickForeground function.

A complete example of the code is below to help you understand the complete ActionScript code:

package
{

	import flash.display.MovieClip;
	import flash.display.SimpleButton;
	import flash.events.MouseEvent;

	public class Main extends MovieClip
	{

		public var btn_back:SimpleButton;
		public var btn_fore:SimpleButton;

		public function Main():void
		{
			stop();

			btn_back.addEventListener(MouseEvent.CLICK, onClickBackground);
			btn_fore.addEventListener(MouseEvent.CLICK, onClickForeground);
		}//Function

public function onClickBackground(evt:MouseEvent):void
	{
	gotoAndStop(2);
	}

public function onClickForeground(evt:MouseEvent):void
	{
	gotoAndStop(1);
	}

	}//Class

}//Package

The only thing left to do now is to test the movie, so go to Control > Test Movie > Test (or hit Ctrl + Enter). It should be working perfectly! Now all that’s left to do is publish it, and share it on the web!

Conclusion

You have now made a Lytro style photo animation! This is just an example of how to use this technique to mimic the Lytro camera images. Use this technique to create compositions and effects of your own for your portfolio, website or blog!

To take it a step further, you could create your own effects and transitions between frames to give your project a personal touch.

Thanks for reading, and I hope you enjoyed this tutorial. Let me know what you think! Now let’s see some of your own. Pop a link in the comments below!



View full post on Activetuts+

Aug 12, 2011 Posted on Aug 12, 2011 in Hints and Tips | 10 comments

Create a Nifty Polaroid Photo Viewer With Flash and Photoshop

Ever wanted to make a manageable and beautiful photo viewer app? In this tutorial, I’ll show you how to design and code a nifty Polaroid photo viewer, focusing on the design and tweening.


Final Result Preview

Let’s take a look at the final result we will be working towards:


Section 1: Organization

In this section we’ll discuss our folder structure, also we’ll acquire our stock imagery.


Step 1: Creating Our Folders

First let’s create a folder in which our project will be located. We’ll name it “Polaroid Viewer”.

Afterwards just copy this folder structure:

The folder structure which we'll use in our tutorial

Step 2: Understanding Our Folder Structure

When creating very visual projects, it’s very important to first really know what you want to design. Inside the Design folder I always have my visual concepts and also in the Design folder I edit the images I’ll need for a project.

After I’ve finished working on my concept I proceed to making a working product, I do this inside the Project folder. Inside the Project folder is the very known bin, lib and src folder structure:

  • bin: Everything which is being exported and is needed to launch the final product, including data assets.
  • lib: Our Flash documents.
  • src: All our code, our own classes, as well as other frameworks.

Step 3: Acquire Stock Imagery

Now that we’ve created our folders, let’s proceed to the images. In this tutorial we’ll need at least two images:

  • A polaroid frame
  • A picture of a wooden floor

You’ll need a Stock Xchange account to download these images, if you don’t yet have one, you should sign up! It’s a website filled with free stock imagery!

Click the links to download the images, afterwards save or move them to our Design folder.

Also we’ll need images to place inside the polaroid frames. Here’s a list of images which I’ve used, but feel free to use your own images.

  • Image #1
  • Image #2
  • Image #3

Section 2: Design

In this section we’re going to edit our stock images. After doing so, we’ll import these images inside our Flash document, which we’ll create later.

We’re going to edit our images with Photoshop, I’ll be using CS5, however you can always use older versions. If you’re not so confident in your Photoshop skills, you can download the images which I’ve already edited. Just download the source files from this tutorial, and you can find the images inside the Edited Images folder.


Step 4: Preparing Our Wooden Floor

In the final example all our polaroid pictures lie on a wooden floor. We’re now going to create this background.

Create a New Document in Photoshop with the dimensions 600x600px.

Add the wooden texture image to the document. Scale it so you’re pleased with the final result.

Our background has been scaled down.

I’ve also added a Gradient Map, setting the colors to black and white.

Add a gradient map to our background

Lastly I’ve changed the gradient map’s transition to 80%. To do so, click on one of the lower arrows, and afterwards click on the dot in the middle of the gradient. Drag this dot to 80%.

Editing the Gradient Map

And voila! We’ve created our wooden background! Save it as a Photoshop Document (.psd) in our Design folder, and name it Background.psd.

Our background which we'll use in our project

Step 5: Preparing the Polaroid Frame

We want to place our own images inside the polaroid frame. We must do two things, we’ll first need to remove the polaroid frame from its (white) background, and also we’ll need to cut the “photo” part away.

To do this, we’ll first create a new 600x600px Photoshop document. Paste the polaroid picture inside the document, scale it down till it fits.

The polaroid frame image has been opened in Photoshop

Why do we scale the image down? Because we’re creating a 600x600px Flash document, and it’s unnecessary to have and load a huge image if we won’t even scale it larger than 600 px.


Step 6: Isolating the Polaroid Frame

Select the pen tool (P) and click around the polaroid frame’s outer edge. When working with the Pen tool, be sure to set the Pen tool in its Paths mode.

Set the Pen Tool to paths mode.

Press Control + Enter (Windows) or Cmd + Enter (Mac) to select your current path.

Select with the pen tool the outer area of the polaroid picture

With your path selected click on the “Add Layer Mask” button. We’ve now isolated the polaroid from its background!

Add a layer mask to the polaroid

Step 7: Cutting the Photo Part Away From the Polaroid Frame

This step is very similar to Design Step 4. Again select the pen tool, and click around the inner area of the polaroid’s frame. Afterwards select the current path with Ctrl + Enter/Cmd + Enter.

Select with the pen tool the inner area of the polaroid picture

Now Alt-click on the mask in the Layers Panel. You’re now editing the mask directly.

Editing the Layer Mask of our polaroid picture

Select the Marquee tool (M). Right-click your selection and select Fill > Black.

Alt-click again on the mask icon, to return back to our “normal” Layer view. We’ve successfully edited our mask and now our polaroid is ready for use. Let’s save the image as a Photoshop document (.psd), I’ve named my document Polaroid.psd.

The polaroid picture has been edited

Section 3: Flash

In this section we’re (finally) going to use Flash. We’re going to set our Flash document to use the folder bin for exporting, and the folder src for our code. Time to open Flash!


Step 8: Creating Our Flash Document

Create a new Flash Document (ActionScript 3.0) with Flash Professional. Set the dimensions to 600x600px and the framerate to 24 fps. Set the Document Class to Main. Also change the background to black (#000000). Save it as “Polaroid.fla”, inside the lib folder.


Step 9: Changing Our Publish Settings

Afterwards go to File > Publish Settings, change Polaroid.swf to ../bin/Polaroid.swf, you can uncheck the HTML option. Now everything we export (only a SWF) will be in the bin folder. Do not close the Publish Settings window.

Change our publish settings of our Flash Document

Step 10: Changing Our Source Path

Click the Flash tab. Afterwards click on the Settings button next to Script: ActionScript 3.0.

Now we can change the source path. You’ll see a folder icon with the text “.”, change that to “../src”.

Flash will use the src folder for all its classes.

Change the ActionScript settings of our Flash Document

Step 11: Importing Our Graphics to Flash

It’s now time to start importing our graphics in to our Flash Document. If you had any trouble with the images, you can always download the source files from this tutorial at the top. There’ll be a folder named “Edited Images”, inside there’ll be the edited images which you can use.

Create a new Flash Document (ActionScript 3.0) with Flash Professional. Set the dimensions to 600x600px and the framerate to 24 fps. Also change the background to black (#000000).

We’ve saved our images as .psd files because they work so greatly with other Adobe products, like Flash. After creating our document, select File > Import to Library and select Polaroid.psd and Background.psd. After doing so our library looks like this:

We've imported our assets in to our Flash Document's library

Step 12: Creating Our Background Sprite

Drag Background.psd from the Library to our stage, afterwards convert it to a movie clip. Name it Background, and check the box “Export for ActionScript”. Set the base class to flash.display.Sprite. After doing so, you can remove the background again from the stage.

Convert the background to a symbol

Why did I set Background’s base class to Sprite instead of the default base class MovieClip? Because the only difference between a MovieClip and a Sprite is that MovieClips can contain multiple frames, and Sprites cannot. We don’t need multiple frames for our Background, so let’s set the base class of Background to Sprite.


Step 13: Creating and Our Polaroid Movie Clip

Just like we did with our background, drag polaroid.psd from the Libary to our stage. Convert it to a movie clip and name it Polaroid, again check the Export for ActionScript box. Set the base class to flash.display.Sprite.


Step 14: Creating Our Polaroid’s Layers

Our Polaroid is still on the stage, double-click it, so we can edit it. So far Polaroid only has one layer, rename this layer to Polaroid Frame.

Create two new Layers, naming them Mask and Image Holder.

The layers inside the symbol Polaroid

Step 15: Creating the Image Holder

Our Polaroid will contain an image, this image must be displayed somewhere, this will be done in a movie clip named “Image Holder”.

Draw a rectangle of approximately the size as the “photo area” of the polaroid, do this in the Image Holder layer. It doesn’t matter which color the rectangle will be. After creating this rectangle, convert it to a movie clip, name it “Image Holder”.

Give Image Holder an instance name of imageHolder.


Step 16: Adding a Mask to the Image Holder

Image Holder will contain the images which we will load. Sometimes these images are too big — take for example a 400x100px image; this image is much too wide, and its edges would extend out of the polaroid. We want to prevent this. Therefore we must hide all imagery which extends outside of the Image Holder’s area.

To do this, we must create a mask. A mask tells a layer which part should be displayed, and which part should be hidden.

If we want every image to be displayed inside Image Holder, without any edges protruding, we should set the mask’s size to the same dimensions as Image Holder itself.

Copy Image Holder, and paste it in place with Ctrl + Shift + V (Windows) / Cmd + Shift + V (Mac) on the layer Mask. Afterwards break the copied Image Holder with Ctrl + B (Windows) / Cmd + B (Mac).

Lastly we must tell our Mask layer that it is a Mask. Right-click on the layer Mask and select the option Mask.


Section 4: Code

It’s time to start coding, if you had any trouble with the Flash Section, you can just copy the folder Flash Section Completed. Inside that folder is everything you need to start following these steps.


Step 17: Creating an XML File

To load our images, we’ll use an XML file. The XML file will tell our product where the images are located.

Open a text editor and type the following:

<images>
	<image>
		<file>IMAGE_1.jpg</file>
	</image>
	<image>
		<file>IMAGE_2.jpg</file>
	</image>
	<image>
		<file>IMAGE_3.jpg</file>
	</image>
</images>

You should change IMAGE_1.jpg, IMAGE_2.jpg, etc. to real filenames, which refer to images located inside the bin folder. If you don’t have any images, download the images that I mentioned in Step 3.

Save this XML file as images.xml, inside the bin folder.


Step 18: Which Classes Do We Need?

Before we start coding, it’s important to know which tasks our project must do:

  • We must load our images from an XML file.
  • We must display these images inside a Polaroid.
  • We must prevent other Polaroids from zooming in when we are already zoomed in on a Polaroid.

To code all these functionalities in one class would make that class extremely complex, it’s better to seperate each task to a different class.

  • ImageLoader: Will load the XML file and pass the images to PolaroidHolder.
  • PolaroidHolder: When it receives the images, it will create a Polaroid for every image, so it also makes sure that only one Polaroid is zoomed in at a time.
  • Polaroid: The image will be displayed inside this class.

Step 19: Creating Our Main Class

The Main class is the class which Polaroid.fla calls when it gets initiated.

Create a new ActionScript file and name it “Main.as”, save it inside the src folder. The Main class looks as follows.

package {

	import flash.display.MovieClip;

	public class Main extends MovieClip {

		public function Main():void {

		}
	}
}

It’s so empty because we first need to create our other classes, while we’re busy we’re going to keep on adding code to the Main class.


Step 20: Explaining Absolute and Relative File Paths

In this project we’re loading several assets: an xml file and various images. We can use relative file paths or absolute paths. Let me explain the difference with an example; we have the following folder structure:

An imaginary folder setup, we're using this example to understand the difference between absolute and relative file paths

Loader.swf must load image1.jpg. From its current position Loader.swf must “open” the images directory and load image1.jpg. We tell Loader.swf to load images/image1.jpg. This type of loading is called relative file path loading, because the url is relative to Loader.swf’s current position.

So how do you load image1.jpg, regardless of where Loader.swf is located? This is done with absolute file path loading. The file path starts from the upper core, from the root. If it’s on a computer, its root is a hard drive. We’re deploying our project online, so the root would be the site server.


Step 21: Why We Need Absolute File Paths

When our project is done, we’d like to deploy it. Most probably it would be embedded inside an HTML page. There’s one small problem. If Loader.swf wants to load, it will load from its current location, from the location of the HTML page. We don’t want this, and therefore we must use absolute file path loading.

Now comes the biggest problem, we won’t know the location of the HTML file. We need to find the absolute path to the .swf file, and edit that path so we can load our imagery.

We can get the absolute path to the .swf file with root.loaderInfo.url.

It’s time to create our ImageLoader class.


Step 22: Creating Our ImageLoader Class

Create a new ActionScript file and name it ImageLoader.as, save it inside the .src folder. The class looks like this, it has been thoroughly commented:

package  {
	//import the classes we need
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.events.Event;
	import flash.display.Loader;
	import flash.display.Bitmap;
	import flash.display.Sprite;

	public class ImageLoader extends Sprite{
		//will load our xml
		private var xmlLoader:URLLoader = new URLLoader();

		//stores the xml
		private var xml:XML;

		//a list of file paths to the images
		private var imageFileList:XMLList;

		//the path to the .swf file
		private var swfPath:String;

		//the project folder path (where the folders bin, src and lib are located)
		private var projectPath:String;

		//we must know the title of our project, so we can edit the .swf url to get our projectPath
		private var projectTitle:String;

		//we need to count how many images we've already loaded
		private var count:uint = 0;

		//we must store the images, the images are Bitmaps
		public var images:Vector.<Bitmap> = new Vector.<Bitmap>;

		public function ImageLoader(_projectTitle:String) {
			//we must know when ImageLoader has been added, then we can figure out what the root's url is
			addEventListener(Event.ADDED, whenAdded);

			//store the project's title
			projectTitle = _projectTitle;

			//when imageLoader is done with loading, call xmlLoaded
			xmlLoader.addEventListener(Event.COMPLETE, xmlLoaded);
		}

		private function whenAdded(event:Event):void {
			//store the swf path
			swfPath = root.loaderInfo.url;

			//get our projectPath
			projectPath = swfPath.substr(0, swfPath.length - projectTitle.length);
		}

		//this function will load the images from an xml, and store the images
		public function load(url:String):void {
			//load the xml, which we'll need to load the images
			xmlLoader.load(new URLRequest(projectPath + url));
		}

		private function xmlLoaded(event:Event):void {
			//store the xml, which xmlLoader has loaded
			xml = new XML(event.target.data);

			//store all the file links in an XMLList
			imageFileList = new XMLList(xml.image.file);

			//now that we have the file information, we can load the images
			loadImages();
		}

		private function loadImages():void {
			for each (var imageFile:String in imageFileList){
				//create
				var imageLoader:Loader = new Loader();
				//whenever an image has been loaded, call imageLoaded
				imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageLoaded, false, 0, true);
				imageLoader.load(new URLRequest(projectPath + imageFile));
			}
		}

		private function imageLoaded(event:Event):void {
			//we're counting how many images we've already loaded, we've just loaded (another) one
			count++;

			//get the image Bitmap
			var image:Bitmap = Bitmap(event.target.loader.content);

			//store the image in our images Vector
			images.push(image);

			//if this is the last image we needed to load
			if(count == imageFileList.length()){
				//we're done, dispatch an event
				dispatchEvent(new Event('imagesLoaded'));
			}

			//--cleanup
			//unload the loader
			event.target.loader.unload();
		}

		public function cleanUp():void {
			xmlLoader.data = null;
			xmlLoader = null;
			xml = null;
			imageFileList = null;
			count = 0;
			images = null;
		}
	}

}

Step 23: Improving Our Main Class

Now that we have our ImageLoader class, we can improve our Main class.

package {

	import flash.display.MovieClip;
	import flash.events.Event;

	public class Main extends MovieClip {
		//a custom class we've made which will load all images from an xml file as Bitmaps
		private var imageLoader:ImageLoader;

		//this class will hold and display all our Polaroids
		private var polaroidHolder:PolaroidHolder;

		public function Main():void {
			//load our images, give imageLoader the name of the .swf file, so it can load using absolute paths
			imageLoader = new ImageLoader('Polaroid.swf');
			addChild(imageLoader);
			imageLoader.addEventListener('imagesLoaded', displayImages, false, 0, true);
			imageLoader.load('pictures.xml');
		}

		private function displayImages(event:Event):void {
			//we've loaded all our images
			polaroidHolder = new PolaroidHolder(imageLoader.images);
			addChild(polaroidHolder);

			//also clean up the imageLoader
			imageLoader.cleanUp();
			imageLoader = null;
		}
	}
}

Step 24: Creating Our PolaroidHolder Class

The PolaroidHolder will receive the images as a Vector. After receiving the images, it will create a Polaroid for each image. Let’s create our PolaroidHolder class:

package {
	import flash.display.MovieClip;
	import flash.display.Bitmap;
	import flash.events.MouseEvent;

	public class PolaroidHolder extends MovieClip {

		//this vector will hold all our Polaroids
		public var polaroids:Vector.<Polaroid>  = new Vector.<Polaroid>;
		//with this variable we no
		private var zoomedIn:Boolean;
		//the Polaroid we're zoomed in on
		private var currentPolaroid:Polaroid;

		public function PolaroidHolder(images:Vector.<Bitmap>) {
			// constructor code
			for each (var image:Bitmap in images) {
				//create a new Polaroid
				var polaroid:Polaroid = new Polaroid(image);

				//position and rotate it
				polaroid.x = Math.random() * 200 + 100;
				polaroid.y = Math.random() * 200;
				polaroid.rotation = Math.random() * 60 - 30;

				//add a clicking eventListener
				polaroid.addEventListener(MouseEvent.CLICK, onPolaroidClick);
				addChild(polaroid);

				//add it to the polaroids vector
				polaroids.push(polaroid);
			}
		}

		private function onPolaroidClick(event:MouseEvent):void {
			//there's been a click on a Polaroid!
			if (currentPolaroid == null) {
				//no currentPolaroid has been set, we're zooming in
				//set the clicked Polaroid as the current Polaroid
				currentPolaroid = event.currentTarget as Polaroid;

				//mute all other Polaroids (the ones which aren't zoomed in)
				for each (var polaroid:Polaroid in polaroids) {
					if (polaroid != currentPolaroid) {
						polaroid.mute();
					}
				}
			} else {
				//check if the click was on the current polaroid
				if (event.currentTarget == currentPolaroid) {
					//the same polaroid has been clicked (again), time to zoom out

					//unmute all other Polaroids, they can respond again to events
					for each (var polaroid:Polaroid in polaroids) {
						if (polaroid != currentPolaroid) {
							polaroid.unmute();
						}
					}

					//now we're not zoomed in, so we don't have a current polaroid
					currentPolaroid = null;
				}
			}
		}

	}

}

In lines 50 and 62, we call the functions mute() and unmute(). Mute makes the Polaroid numb to all events, it won’t respond to mouse overs, or clicks. This is precisely what we want when we’re zoomed in. We don’t want other Polaroids to do anything. After we’re zooming out, we want the Polaroids to respond to events; by calling unmute, the Polaroids will respond again to mouse overs and clicks.


Step 25: Creating Our Polaroid Class

Now it’s time to create our most interesting class, our Polaroid class! For this class we’ll need the popular tweening library tweener. Download it, and save the library inside the src folder (see this guide for more details).

package {
	import flash.display.MovieClip;
	import flash.events.MouseEvent;

	import caurina.transitions.*;
	import caurina.transitions.properties.*;
	import flash.display.Bitmap;
	import flash.events.Event;

	public class Polaroid extends MovieClip {

		//the scale at which the Polaroid is zoomed in
		private static const ZOOMED_IN_SCALE:Number = 1;
		//the scale at which the Polaroid is zoomed out
		private static const ZOOMED_OUT_SCALE:Number = .6;

		//the max height an image can be
		private var IMAGE_MAX_HEIGHT:Number;
		//the max width an image can be
		private var IMAGE_MAX_WIDTH:Number;

		//we must scale the loaded images, remember the scale
		private var scale:Number;

		//when we zoom out, we zoom out back to the Polaroid's original position and rotation
		//the Polaroid's original x coordinate.
		private var origX:Number;
		//we also need the original y coordinate
		private var origY:Number;
		//store the Polaroid's original rotation
		private var origRotation:Number;
		//when this Boolean becomes true, the Polaroid will not respond to mouse events
		private var muted:Boolean;
		//we need to know if the Polaroid has been clicked
		public var clicked:Boolean;

		public function Polaroid(image:Bitmap) {
			//make the Polaroid a button
			buttonMode = true;

			//the maximum dimensions for an image are limited by the dimensions of imageHolder
			IMAGE_MAX_HEIGHT = imageHolder.height;
			IMAGE_MAX_WIDTH = imageHolder.width;

			//scale the Polaroid down
			scaleX = ZOOMED_OUT_SCALE;
			scaleY = ZOOMED_OUT_SCALE;

			//we'll need to resize the image to make it fit
			//first check if it is a portrait or landscape
			if (image.width > image.height) {
				//it is a landscape
				//the scale is limited by the image's height
				scale = IMAGE_MAX_HEIGHT / image.height;
			} else {
				//it is a portrait
				//the scale is limited by the image's width
				scale = IMAGE_MAX_WIDTH / image.width;
			}

			//scale the image
			image.scaleX = scale;
			image.scaleY = scale;

			//add the scaled image
			imageHolder.addChild(image);

			//add event listeners to the Polaroid;
			addEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
			addEventListener(MouseEvent.MOUSE_OUT, onMouseOut);
			addEventListener(MouseEvent.CLICK, onClick);
			addEventListener(Event.ADDED, whenAdded);
		}

		public function mute():void {
			//calling this function will make the Polaroid not respond to events
			buttonMode = false;
			muted = true;
		}

		public function unmute():void {
			//calling this function will make the Polaroid respond to events
			buttonMode = true;
			muted = false;
		}

		private function whenAdded(event:Event):void {
			//store the Polaroid's original coordinates and rotation
			origX = x;
			origY = y;
			origRotation = rotation;

			//we won't need this event listener anymore, remove it
			removeEventListener(Event.ADDED, whenAdded);
		}

		private function onMouseOver(event:MouseEvent):void {
			//only respond if the Polaroid has not been muted
			if (! muted) {
				//only respond if the Polaroid has not been clicked, clicked Polaroids should not respond to mouse overs.
				if (! clicked) {
					//move the polaroid to the front
					parent.setChildIndex(this, parent.numChildren - 1);
					//move and turn the polaroid in a random direction
					Tweener.addTween(this, {x: origX + Math.random() * 30 - 15, y: origY + Math.random() * 30 -15, rotation:origRotation + Math.random() * 20 - 10, time:1});
				}
			}
		}

		private function onMouseOut(event:MouseEvent):void {
			//only respond if the Polaroid has not been muted
			if (! muted) {
				//only respond if the Polaroid has not been clicked
				if (! clicked) {
					//move the polaroid back to it's original position and rotation
					Tweener.addTween(this, {x: origX, y:origY, rotation:origRotation, time:1});
				}
			}
		}

		private function onClick(event:MouseEvent):void {
			//only respond if the Polaroid has not been muted
			if (! muted) {
				//if it hasn't been clicked before
				if (! clicked) {
					//we've been clicked, let's make that true
					clicked = true;

					//we're zooming in, let's dispatch an event (Main will pick this up)
					dispatchEvent(new Event('zoomIn'));

					//zoom the Polaroid in to the center of the stage
					Tweener.addTween(this, {x:stage.stageWidth/2 - width/2, y:stage.stageHeight/2 - height/2, rotation:0, scaleX:ZOOMED_IN_SCALE, scaleY:ZOOMED_IN_SCALE, time: 1});
				} else {
					//we've already been clicked, so make clicked false
					clicked = false;

					//we're zooming out, let's dispatch an event (Main will pick this up)
					dispatchEvent(new Event('zoomOut'));

					//zoom the polaroid out, back to its original position
					Tweener.addTween(this, {x: origX, y:origY, rotation:origRotation, scaleX: ZOOMED_OUT_SCALE, scaleY:ZOOMED_OUT_SCALE, time:1});
				}
			}
		}
	}
}

Step 26: Adding Our PolaroidHolder to Our Main Class

Make the changes as seen in the code below.

package {

	import flash.display.MovieClip;

	public class Main extends MovieClip {

		//a custom class we've made which will load all images from an xml file as Bitmaps
		private var imageLoader:ImageLoader;

		private var polaroidHolder:PolaroidHolder;

		public function Main():void {
			//load our imagges
			imageLoader = new ImageLoader();
			imageLoader.addEventListener('imagesLoaded', displayImages, false, 0, true);
			imageLoader.load('pictures.xml');
		}

		private function displayImages(event:Event):void {
			//we've loaded all our images
			polaroidHolder = new PolaroidHolder(imageLoader.polaroids);
			addChild(polaroidHolder);

			//also clean up the imageLoader
			imageLoader.cleanUp();
			imageLoader = null;
		}
	}
}

Step 27: The Result So Far

It seems to work, but it’s not very interesting. When zooming in, the user is not “pulled in”, our product is not immersive enough. What we need is a background! By chance we created one in Step 4. Time to add our background!


Step 28: Adding Our Background

We had exported our background with the class Background.

Not only will we add Background as a child of Main, we’ll also make Background darken when we’re zoomed in, and lighten when we’re zoomed out. How do we do this? Whenever a Polaroid is clicked, it dispatches a custom event, zoomIn or zoomOut. We can trace this event, just add event listeners to our Main class, they’ll pick up the zoomIn and zoomOut events, and whenever they’re picked up, we can tell our Background to lighten or darken:

package {

	import flash.display.MovieClip;
	import flash.events.Event;

	import caurina.transitions.*;
	import caurina.transitions.properties.*;

	public class Main extends MovieClip {

		//a custom class we've made which will load all images from an xml file as Bitmaps
		private var imageLoader:ImageLoader;

		//this class will display all our Polaroids, by receiving a Vector of Bitmaps
		private var polaroidHolder:PolaroidHolder;

		//the wooden background, bg is an abbrivation for background
		private var bg:Background

		public function Main():void {
			//load our images
			imageLoader = new ImageLoader('Polaroid.swf');
			imageLoader.addEventListener('imagesLoaded', displayImages, false, 0, true);
			addChild(imageLoader);
			imageLoader.load('pictures.xml');

			//add our bg as a child
			bg = new Background();
			addChild(bg);

			//add event listener, so we know when we're zoomed in or out
			//3rd parameter of addEventListener is true, so we can capture the event, when it "bubbles" up from the Polaroid
			addEventListener('zoomIn', onZoomIn, true);
			addEventListener('zoomOut', onZoomOut, true);
		}

		private function displayImages(event:Event):void {
			//we've loaded all our images
			polaroidHolder = new PolaroidHolder(imageLoader.images);
			addChild(polaroidHolder);

			//also clean up the imageLoader
			imageLoader.cleanUp();
			imageLoader = null;
		}

		private function onZoomIn(event:Event):void {
			//upon zooming in, we'll darken the bg
			Tweener.addTween(bg, {alpha:.6, time:1});
		}

		private function onZoomOut(event:Event):void {
			//upon zooming out, we'll lighten the bg
			Tweener.addTween(bg, {alpha:1, time:1});
		}
	}
}

Step 29: Looking Back

Tada! We’re done, this was quite a tutorial! I hope you guys had fun following this tutorial, and also learnt something from it. Why not play around with the code? You could try adding text fields and somehow display the title of the image. Or use several types of Polaroids. Thanks, and feel free to comment if you have any questions or comments. Keep an eye out for my next tutorial!



View full post on Activetuts+

Jul 29, 2011 Posted on Jul 29, 2011 in Hints and Tips | 0 comments

Create a Mechanical Snake With Inverse Kinematics

Imagine a chain of particles animating in symphony together: A train moving as all attached compartments follow suit; a puppet dancing as its master pulls its string; even your arms, when your parents hold your hands as they lead you in an evening walk. Movevment ripples down from the last node to the origin, abiding to constraints as it goes. This is inverse kinematics (IK), a mathematical algorithm that calculates necessary motions. Here, we’ll use it to create a snake that’s a little more advanced than the one from Nokia games.


Final Result Preview

Let's take a look at the final result we will be working towards. Press and hold UP, LEFT and RIGHT keys to make it move.


Step 1: Relationships in a Chain

A chain is constructed of nodes. Each node represents a point in the chain where translation and rotation may happen. In IK chain, motion ripples down in reverse from the last node (last child) to the first node (root node) as opposed to Forward Kinematics (FK) where kinematics traverse from the root node to the last child.

All chains begins with the root node. This root node is the acting parent unto which a new child node is attached. In turn, this first child will parent the second child in the chain, and this is repeated until the last child is added. The animation below depicts such a relationship.


Step 2: Remembering Relationships

The IKshape class implements the notion of a node in our chain. Instances of IKshape class remember their parent and child nodes, with the exceptions of the root node which has no parent node and the last node which has no child node. Below are the private properties of the IKshape.

private var childNode:IKshape;
private var parentNode:IKshape;
private var vec2Parent:Vector2D;

Accessors of these properties are shown as below:

public function set IKchild(childSprite:IKshape):void
{
	childNode = childSprite;
}

public function get IKchild ():IKshape
{
	return childNode
}

public function set IKparent(parentSprite:IKshape):void
{
	parentNode = parentSprite;
}

public function get IKparent():IKshape
{
	return parentNode;
}

Step 3: Vector from Child to Parent

You may notice that this class does store a Vector2D that points from child node to parent node. The rationale for this direction is due to motion flowing from child to parent. Vector2D is used because the magnitude and direction of vector pointing from child to parent will be manipulated frequently while implementing behaviour of an IK chain. Thus, keeping track of such data is necessary. Below are methods to maniplate vector quantities for IKshape.

public function calcVec2Parent():void
{
	var xlength:Number = parentNode.x - this.x;
	var ylength:Number = parentNode.y - this.y;

	vec2Parent = new Vector2D(xlength, ylength);
}

public function setVec2Parent(vec:Vector2D):void
{
	vec2Parent = vec.duplicate();
}

public function getVec2Parent():Vector2D
{
	return vec2Parent.duplicate();
}

public function getAng2Parent():Number
{
	return vec2Parent.getAngle();
}

Step 4: Drawing Node

Last but not least, we need a method to draw our shape. We shall draw a rectangle to represent each node. However, any other preferences can be put in by overriding the draw method here. Iv included an example of a class overriding the default draw method, the Ball class. (A quick switch between shapes will be demonstrated at the end of this tutorial.) With this, we complete the creation of the Ikshape class.

protected function draw():void
{
	var col:Number = 0x00FF00;
	var w:Number = 50;
	var h:Number = 10;
	graphics.beginFill(col);
	graphics.drawRect(-w/2, -h/2, w, h);
	graphics.endFill();
}

Step 5: The IK Chain

IKine class implements behaviour of an IK chain. Explanation regarding this class follows this order

  1. Introduction to private variables in this class.
  2. Basic methods used in this class.
  3. Mathematical explanation on the workings of specific functions.
  4. Implementation of those specific functions.

Step 6: The Data in a Chain

Code below shows the IKine class private variables.

private var IKineChain:Vector.<IKshape>;		//members of chain

//Data structure for constraints
private var constraintDistance:Vector.<Number>;		//distance between nodes
private var constraintRangeStart:Vector.<Number>;	//start of rotational freedom
private var constraintRangeEnd:Vector.<Number>;		//end of rotational freedom

Step 7: Instantiate the Chain

IKine chain will store a Sprite datatype that remembers the relationship of its parent and child. These sprites are instances of IKshape. The resultant chain sees the root node at index 0, the next child at index 1, … until the last child in sequential manner. However, construction of chain is not from root to last child; it is from last child to root.

Assuming that the chain is of length n, construction follows this sequence: n-th node, (n-1)-th node, (n-2)-th node … 0-th node. The animation below depicts this sequence.

Upon instantiation of IK chain, the last node is inserted. Parent nodes will be appended later. The last node appended is the root. The code below are methods of IK chain construction, appending and removing nodes to chain.

public function IKine (lastChild:IKshape, distance:Number)
{
	//initiate all private variables
	IKineChain = new Vector.<IKshape>();
	constraintDistance = new Vector.<Number>();
	constraintRangeStart = new Vector.<Number>();
	constraintRangeEnd = new Vector.<Number>();

	//Set constraints
	this.IKineChain[0] = lastChild;
	this.constraintDistance[0] = distance;
	this.constraintRangeStart[0] = 0;
	this.constraintRangeEnd[0] = 0;
}

/*Methods to manipulate IK chain
*/
public function appendNode(nodeNext:IKshape, distance:Number = 60, angleStart:Number = -1*Math.PI, angleEnd:Number = Math.PI):void
{
	this.IKineChain.unshift(nodeNext);
	this.constraintDistance.unshift(distance);
	this.constraintRangeStart.unshift(angleStart);
	this.constraintRangeEnd.unshift(angleEnd);
}

public function removeNode(node:Number):void
{
	this.IKineChain.splice(node, 1);
	this.constraintDistance.splice(node, 1);
	this.constraintRangeStart.splice(node, 1);
	this.constraintRangeEnd.splice(node, 1);
}

Step 8: Getting Chain Nodes

These following methods are used to retrieve nodes from the chain whenever there's a need.

public function getRootNode():IKshape
{
	return this.IKineChain[0];
}

public function getLastNode():IKshape
{
	return this.IKineChain[IKineChain.length - 1];
}

public function getNode(node:Number):IKshape
{
	return this.IKineChain[node];
}

Step 9: Constraints

We have seen how the chain of nodes is being represented in an array: Root node at index 0, … (n-1)-th node at index (n-2), n-th node at index (n-1), n being length of chain. We can conveniently arrange our constraints in such order as well. Constraints come in two forms: distance between nodes and degree of bending freedom between nodes.

Distance to maintain between nodes is recognised as a child node's constraint upon its parent. For the sake of referencing convenience, we can store this value as constraintDistance array with index similar to that of the child node's. Note that the root node has no parent. However, distance constraint should be registered upon appending the root node so that if the chain is extended later, the newly-appended "parent" of this root node can utilise its data.

Next, the angle of bending for a parent node is restricted to a range. We shall store the start and end point for range in constraintRangeStart and ConstraintRangeEnd array. Figure below shows a child node in green and two parent nodes in blue. Only the node marked "OK" is allowed because it lies within the angle constraint. We can use similar approach in referencing values in these arrays. Note again that root node's angle constraints should be registered even though not in use due to similar rationale as previous. Plus, angle constraints does not apply to the last child because we want flexibility in control.

All constraints drawn onto this diagram.

Step 10: Constraints: Getting and Setting

The methods as follow may prove useful when you have initiated constraints on a node but would like to alter the value in future.

/*Manipulating corresponding constraints
*/
public function getDistance(node:Number):Number
{
	return this.constraintDistance[node];
}

public function setDistance(newDistance:Number, node:Number):void
{
	this.constraintDistance[node] = newDistance;
}

public function getAngleStart(node:Number):Number
{
	return this.constraintRangeStart[node];
}

public function setAngleStart(newAngleStart:Number, node:Number):void
{
	this.constraintRangeStart[node] = newAngleStart;
}

public function getAngleRange(node:Number):Number
{
	return this.constraintRangeEnd[node];
}

public function setAngleRange(newAngleRange:Number, node:Number):void
{
	this.constraintRangeEnd[node] = newAngleRange;
}

Step 11: Length Constraint, Concept

 The following animation shows the calculation of length constraint.


Step 12: Length Constraint, Formula

In this step, we'll have a look at commands in a method that help to constrain distance between nodes. Note the highlighted lines. You may notice only the last child is applied this constraint. Well, as far as the command goes, this is true. Parent nodes are required to fulfill not only length but angle constraints. All these are handled with the implementation of method vecWithinRange(). Last child need not be constrained in angle because we need maximum bend flexibility.

private function updateParentPosition():void
{
	for (var i:uint = IKineChain.length - 1; i > 0; i--)
	{
		IKineChain[i].calcVec2Parent();
		var vec:Vector2D;

		//handling the last child
		if ( i == IKineChain.length - 1)
		{
			var ang:Number = IKineChain[i].getAng2Parent();
			vec = new Vector2D(0, 0);
			vec.redefine(this.constraintDistance[IKineChain.length - 1], ang);
		}
		else
		{
			vec = this.vecWithinRange(i);
		}

		IKineChain[i].setVec2Parent(vec);
		IKineChain[i].IKparent.x = IKineChain[i].x + IKineChain[i].getVec2Parent().x;
		IKineChain[i].IKparent.y = IKineChain[i].y + IKineChain[i].getVec2Parent().y;
	}
}

Step 13: Angle Constraint, Concept

First, we calculate the current angle sandwiched between the two vectors, vec1 and vec2. If the angle is not within the constrained range, assign the minimum or maximum limit to the angle. Once an angle is defined, we can calculate a vector that is rotated from vec1 together with the constraint of distance (magnitude).

Angle sandwiched between two vectors.

 The following animation offers another alternative to visualising the idea.


Step 14: Angle Constraint, Formula

The implementation of the angle constraints is as below.

private function vecWithinRange(currentNode:Number):Vector2D
{
	//getting the appropriate vectors
	var child2Me:Vector2D = IKineChain[currentNode].IKchild.getVec2Parent();
	var me2Parent:Vector2D = IKineChain[currentNode].getVec2Parent();

	//Implement angle bounds limitation
	var currentAng:Number = child2Me.angleBetween(me2Parent);
	var currentStart:Number = this.constraintRangeStart[currentNode];
	var currentEnd:Number = this.constraintRangeEnd[currentNode];
	var limitedAng:Number = Math2.implementBound(currentStart, currentEnd, currentAng);

	//Implement distance limitation
	child2Me.setMagnitude(this.constraintDistance[currentNode]);
	child2Me.rotate(limitedAng);

	return child2Me
}

Step 15: Angle with Directions

Perhaps it is worthy to go through here the idea of getting an angle that interprets clockwise and counter-clockwise direction. The angle sandwiched between two vectors, say vec1 and vec2, can be easily obtained from the dot product of those two vectors. The output will be the shortest angle to rotate vec1 to vec2. However, there is no notion of direction as the answer is always positive. Therefore modification on the regular output should be done. Before outputing the angle, I used vector product between vec1 and vec2 to determine whether the current sequence is positive or negative rotation and incorporated the sign into the angle. I have highlighted the directional feature in lines of code below.

public function vectorProduct(vec2:Vector2D):Number
{
	return this.vec_x * vec2.y - this.vec_y * vec2.x;
}

public function angleBetween(vec2:Vector2D):Number
{
	var angle:Number = Math.acos(this.normalise().dotProduct(vec2.normalise()));

	var vec1:Vector2D = this.duplicate();
	if (vec1.vectorProduct(vec2) < 0)
	{
		angle *= -1;
	}

	return angle;
}

Step 16: Orienting Nodes

Nodes that are boxes needs to be oriented to the direction of their vectors so that they look nice. Otherwise, you will see a chain like below. (Use the arrow keys to move.)

The function below implements the right orientation of nodes.

private function updateOrientation():void
{
	for (var i:uint = 0; i < IKineChain.length - 1; i++)
	{
		var orientation:Number = IKineChain[i].IKchild.getVec2Parent().getAngle();

		IKineChain[i].rotation = Math2.degreeOf(orientation);
	}
}

Step 17: Last Bit

Now that everything is set, we can animate our chain using animate(). This is a composite function making calls to updateParentPosition() and updateOrientation(). However before that can be achieved, we have to update relationships on all nodes. We make a call to updateRelationships(). Again, updateRelationships() is a composite function making calls to defineParent() and defineChild(). This is done once and whenever there's a change in the chain structure, eg nodes are added or dropped at runtime.


Step 18: Essential Methods in IKine

In order to make IKine class work for you, these are the few methods you should look into. I’ve documented them in a table form.

Method Input Parameters Role
IKine() lastChild: IKshape, distance:Number Constructor.
appendNode() nodeNext:IKshape, [distance:Number, angleStart:Number, angleEnd:Number] add nodes to chain, define constraints implemented by node.
updateRelationships() None Update parent-child relationships for all nodes.
animate() None Recalculating the position of all nodes in chain. Must be called every frame.

Note that angle inputs are in radians not degrees.


Step 19: Creating a Snake

Now lets create a project in FlashDevelop. In the src folder you will see Main.as. This is the sequence of tasks you should do:

  1. Initiate copies of IKshape or classes that extend from IKshape on the stage.
  2. Initiate IKine and use it to chain up copies of IKshape on stage.
  3. Update relationships on all nodes in chain.
  4. Implement user controls.
  5. Animate!

Step 20: Draw Objects

Object is drawn as we construct IKshape. This is done in a loop. Note if you'd like to change the outlook of the drawing to a circle, enable comment on line 56 and disable comment on line 57. (You’ll need to download my source files in order for this to work.)

private function drawObjects():void
{
	for (var i:uint = 0; i < totalNodes; i++) {

	var currentObj:IKshape = new IKshape();
	//var currentObj:Ball = new Ball();
	currentObj.name = &quot;b&quot; + i;
	addChild(currentObj);
	}
}

Step 21: Initialise Chain

Before initialising the IKine class to construct the chain, private variables of Main.as are created.

private var currentChain:IKine;
private var lastNode:IKshape;
private var totalNodes:uint = 10;

For the case here, all nodes are constrained to a distance of 40 between nodes.

private function initChain():void
{
	this.lastNode = this.getChildByName(&quot;b&quot; + (totalNodes - 1)) as IKshape;
	currentChain = new IKine(lastNode, 40);

	for (var i:uint = 2; i <= totalNodes; i++)
	{
		currentChain.appendNode(this.getChildByName(&quot;b&quot; + (totalNodes - i)) as IKshape, 40, Math2.radianOf(-30), Math2.radianOf(30));
	}
	currentChain.updateRelationships();

	//center snake on the stage.
	currentChain.getLastNode().x = stage.stageWidth / 2;
	currentChain.getLastNode().y = stage.stageHeight /2
}

Step 22: Add Keyboard Controls

Next, we declare variables to be utilised by our keyboard control.

private var leadingVec:Vector2D;
private var currentMagnitude:Number = 0;
private var currentAngle:Number = 0;

private var increaseAng:Number = 5;
private var increaseMag:Number = 1;
private var decreaseMag:Number = 0.8;
private var capMag:Number = 10;

private var pressedUp:Boolean = false;
private var pressedLeft:Boolean = false;
private var pressedRight:Boolean = false;

Attach onto stage the main loop and keyboard listeners. I’ve highlighted them.

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

	this.drawObjects();
	this.initChain();
	leadingVec = new Vector2D(0, 0);

	stage.addEventListener(Event.ENTER_FRAME, handleEnterFrame);
	stage.addEventListener(KeyboardEvent.KEY_DOWN, handleKeyDown);
	stage.addEventListener(KeyboardEvent.KEY_UP, handleKeyUp);
}

Write the listeners.

private function handleEnterFrame(e:Event):void
{
	if (pressedUp == true)
	{
		currentMagnitude += increaseMag;
		currentMagnitude = Math.min(currentMagnitude, capMag);
	}
	else
	{
		currentMagnitude *= decreaseMag;
	}

	if (pressedLeft == true)
	{
		currentAngle -= Math2.radianOf(increaseAng);
	}

	if (pressedRight == true)
	{
	currentAngle += Math2.radianOf(increaseAng);
	}

	leadingVec.redefine(currentMagnitude, currentAngle);
	var futureX:Number = leadingVec.x + lastNode.x;
	var futureY:Number = leadingVec.y + lastNode.y;

	futureX = Math2.implementBound(0, stage.stageWidth, futureX);
	futureY = Math2.implementBound(0, stage.stageHeight, futureY);

	lastNode.x = futureX;
	lastNode.y = futureY;
	lastNode.rotation = Math2.degreeOf(leadingVec.getAngle());

	currentChain.animate();
}

private function handleKeyDown(e:KeyboardEvent):void
{
	if (e.keyCode == Keyboard.UP)
	{
		pressedUp = true;
	}

	if (e.keyCode == Keyboard.LEFT)
	{
		pressedLeft = true;
	}
	else if (e.keyCode == Keyboard.RIGHT)
	{
		pressedRight = true;
	}
}

private function handleKeyUp(e:KeyboardEvent):void
{
	if (e.keyCode == Keyboard.UP)
	{
		pressedUp = false;
	}

	if (e.keyCode == Keyboard.LEFT)
	{
		pressedLeft = false;
	}
	else if (e.keyCode == Keyboard.RIGHT)
	{
		pressedRight = false;
	}
}

Notice that I’ve used a Vector2D instance to lead the snake moving around the stage. I’ve also constrained this vector within the boundary of stage so it will not move out. The Actionscript performing this constraint is highlighted.


Step 23: Animate!

Press Ctrl+Enter to see your snake animate!. Control its movement using the arrow keys.


Conclusion

This tutorial does require some knowledge in Vector Analysis. For readers who would like to get a familiar look at vectors, do have a read on the post by Daniel Sidhon. Hope this helps you in understanding and implementing inverse kinematics. Thanks for the read. Do drop suggestions and comments as Im always eager to hear from audiences. Terima Kasih.



View full post on Activetuts+

Page 5 of 20« First«...34567...1020...»Last »
search search search search search
Find an Article
Categories
  • Flash Video Training
  • Hints and Tips
  • Recommended
Please Support Our Sponsors
Recent Posts
  • The Math and ActionScript of Curves: Drawing Quadratic and Cubic Curves
  • Weekend Lecture: Understanding Games, a Flash Game About Game Design
  • Weekend Lecture: Understanding Games, a Flash Game About Game Design
  • Workshop Coding Challenge: Fix This Breakout Game
  • Enable the Latest AIR SDK in Flash Professional CS5.5+
Tag Cloud
2011 ActionScript Active Activetuts+ Adobe animation Basic Basix Best Build Button Character Create Creating Critique Custom design Effect Effects Files Flash from Game Guide HTML5 Introduction Macromedia Motion Muzzle part Player Premium Professional Quick Silverlight Simple Text Tool Tutorial Tuts+ Tween Using Video website Workshop
About Our Site:

Hey there and welcome to "Flash Video Training Source", a resource for anybody interested in learning more about Adobe's great tool. We feature educational videos, which will help you master Adobe Flash and help you get to know all of its features. We at "Flash Video Training Source" believe that video training and video... more

Why don't you follow us on Twitter and get the latest video tutorials twitted to your account. Just click on the floating twitter bar to your right!

Go Back In Time
May 2012
M T W T F S S
« Apr    
 123456
78910111213
14151617181920
21222324252627
28293031  
Pretty Blank Box
top

Blogroll

  • Development Blog
  • Documentation
  • Plugins
  • Suggest Ideas
  • Support Forum
  • Themes
  • WordPress Planet

Meta

  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org

Archives

  • May 2012
  • April 2012
  • March 2012
  • February 2012
  • January 2012
  • December 2011
  • November 2011
  • October 2011
  • September 2011
  • August 2011
  • July 2011
  • June 2011
  • May 2011
  • April 2011
  • March 2011
  • February 2011
  • January 2011
  • December 2010
  • November 2010
  • October 2010
  • September 2010
  • August 2010
  • July 2010
  • June 2010
  • May 2010
  • April 2010
Powered by WordPress  |  Designed by Elegant Themes  |  Lightning Fast Hosting by Site 5 Hosting