Activetuts+ » Games http://active.tutsplus.com Flash, Flex & ActionScript Tutorials Fri, 19 Mar 2010 19:32:08 +0000 http://wordpress.org/?v=2.9.2 en hourly 1 Create a Pinball-Style Rolling Score Counter Class http://active.tutsplus.com/tutorials/effects/create-a-pinball-style-rolling-score-counter-class/ http://active.tutsplus.com/tutorials/effects/create-a-pinball-style-rolling-score-counter-class/#comments Mon, 08 Feb 2010 12:00:11 +0000 Cadin Batrack http://active.tutsplus.com/?p=3109 ]]> In this tutorial you’ll create a reusable Score class that counts up to the new total when points are added (instead of jumping up to the new score). We’ll cover graphics creation as well as code.

Final Result Preview

In some games when you gain points, you’ll see your score immediately jump to the new total. I think it’s much cooler if the score counts up one by one, so the player can “rack up points”. That’s what we’ll be making here.

Here’s an example of the score class in action:

The main idea behind this tutorial is to teach you how to program the “counting up” functionality, but I’ll also show you how to create the cool LED display seen in the preview. We’ll start by designing the numbers:

Step 1: Set up Your Flash File

Create a new Flash file (ActionScript 3.0). Your movie settings will vary depending on your game. For this demo I’m setting up my movie as 500×300, black background, and 30 fps.

Flash file setup

Step 2: Create the Digit Symbol

Create a new Movie Clip symbol (Insert > New Symbol). Give this symbol the name “digit”.

TK

Step 3: Create the Digit Text Field

Inside the digit movie clip use the Text tool to add a number 0 to the symbol. I’m using a font called Digital Readout, but any LED-style font should work.

Set the text size to 40 pt and make it a light amber/orange color (#F4C28B). Set the Paragraph Format to centered.

TK

Step 4: Add Glows

Add two separate glow filters to your text field. Set the color to red (#FF0000) for both and set the Strength of both to 200%.

Check the Inner Glow checkbox for one and set the Blur to 2px. Leave the other at 5px Blur.

TK

You can use a different color if you want (blue or green would both look cool). The trick in getting it to look realistic is to make the text color a little washed out and set the glows to a more saturated color. This makes it look like it’s emitting light.

Step 5: Add More Numbers

Create keyframes on frames 1-10 of the digit movie clip. An easy way to do this is to select frames 1-10 (click frame 1, then Shift-click frame 10) and press F6.

You should now have 10 frames, each with a keyframe with your glowing 0 text field. Go through each frame and change the numbers so you have the digits 0-9. Frame 1 will have “0″, frame 2 will have “1″, frame 3 will have “2″, etc.

TK

Name this layer “numbers”.

Step 6: Add the LED Background

We’ll now add an “off” state for the LED numbers, so you’ll be able to see the unlit segments of the LED display.

Copy your 8 digit (in frame 9). Create a new layer named “background”. With the new layer selected use Paste in Place (Edit > Paste in Place) to paste the 8 digit in the exact position as the one we copied.

Remove the glows from the new 8 digit and change its color to dark grey (#333333). Add a blur filter with the Blur set to 3px. Move this layer underneath the “numbers” layer.

TK

Now you can scrub through the frames and see how the unlit segments of the LED show behind each number.

Step 7: Add the Stop Action

Create another new layer named “actions”. Open the Actions Panel and add a stop() action on frame 1.

TK

This will keep the display showing ‘0′ until we tell it otherwise.

Step 8: Why Frames?

Why are we manually putting each digit on its own frame instead of using a dynamic text field? Good question.

The main reason is that doing so makes it more flexible for updating the graphics later. If you wanted to change the design and use bitmaps for the numbers, or have each digit displayed in a different font or color this makes it easy to do that.

Also, if designers and developers are working together on a project it’s best to create things in a way that gives designers easy access to as much of the graphics as possible.
I feel this setup does that more than using dynamic text.

Step 9: Create the Score Movie Clip

Create a new movie clip named “Score”. Check ‘Export for ActionScript’ and set the class name to “Score” also.

TK

Drag the digit movie clip from the Library into the Score movie clip. Duplicate the digit clip (Edit > Duplicate) six times (so you have seven digits) and space them evenly.

Since we only have seven digits the maximum score we’ll be able to display is 9,999,999. If your game will need to accommodate higher scores add more digits accordingly.

Add a bit more space between every third digit to allow for comma separators.

TK

Step 10: Name the Digit Clips

Select the leftmost digit movie clip and give it the instance name “digit1″. Name the next one to the right “digit2″, then “digit3″ and so on.

TK

Step 11: Add Commas

Create a new layer called “commas”.

The easiest way to get the commas to look exactly like the numbers is to go into one of the digit clips and copy one of the number text fields.
Back inside the Score movie clip, paste the text field into the commas layer, and change the number to a comma. Duplicate it and move it as many times as you need.

TK

Step 12: Add a Background

For the Score background we’ll just add a simple rounded rectangle.

Create a new layer called “background” and place it behind the numbers and commas layers. Select the Rectangle Tool and Option-click (Alt-click) the stage. Make a rectangle 200px x 40px with 3px corners (make yours longer if you have more digits). Make the fill black and the stroke 1px grey (#666666).

TK

For some reason Flash always distorts strokes on rounded rectangles. To get around this, select the stroke and choose Modify > Shape > Convert Lines to Fills. This converts the stroke from a line to a filled shape and it will no longer distort.

TK

If you think this is a total hack of a workaround for basic functionality that should have been fixed years ago, I urge you to contact Adobe and let them know.

Step 13: Add Shine

What graphic would be complete without some iPhone-esque shine?

Create a new layer above everything else called “shine”. Add a new rounded rectangle, slightly smaller than the background one. This time give it no stroke and fill it with a white gradient from 20% Alpha to 0% Alpha.

TK

Step 14: Create the Score Class

Create a new Actionscript file named “Score.as”. Save it in the same directory as your main Flash file. Since the name of this class and the Export Class name of our Score movie clip are the same, Flash will automatically link them.

Add this code to the Score.as file:

package {

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

	public class Score extends MovieClip {

		// CONSTRUCTOR
		public function Score() {

		}

	}
}

This is just an empty shell of a class for now. We have to extend the MovieClip class since this class is linked to a movie clip in the library, so we also have to import the MovieClip class. We’ll be using the ENTER_FRAME event, so we import the Event class as well.

Step 15: Add Variables and Constants

Add these two lines to the Score class just above the constructor function.

private const SPEED:int = 1; // how fast to count
private const NUM_DIGITS:int = 7; // how many digits there are in the score

These are two constants – kind of like settings for the class.

  • The first, SPEED, controls how fast the score counts. I have it set to count one by one, but if your game uses higher scores this might be too slow. You can change this to 5 or 10 or 50 to count up by those increments.
  • The second constant, NUM_DIGITS, defines how many digits we have in our Score movie clip. If you added more (or less) than 7 digits you’ll need to change this.

Now let’s add a couple of variables. Put these just below the constants:

private var _totalScore:int = 0;
private var _displayScore:int= 0;

These variables will hold the two different versions of our score. “_totalScore” will be the actual score. “_displayScore” will be the number that is currently
being shown on the LED display. If I add 50 to the score, the _totalScore will immediately be 50, but the _displayScore will be 1, then 2, then 3, until it reaches 50.

If you ever need to know the actual score (like to send to your high score boards) you’ll use _totalScore since _displayScore might not be accurate.

I’m using underscores at the beginning of the variable names to denote that these are private variables.

Step 16: Add the totalScore Accessor Method

So if _totalScore is a private variable, how will we access from outside the Score class? We’ll use an “accessor” or “getter” method.

Add this method below the constructor function:

// public accessor for totalScore
public function get totalScore():int {
	return _totalScore;
}

This method simply returns the value of the _totalScore variable. It gives us a way to access that value without having to expose it as a public variable.

Step 17: Add the add Method

We’ll need a way to add points to the score. Add this method:

// add an amount to the score
public function add(amount:int):void {
	_totalScore += amount;
	addEventListener(Event.ENTER_FRAME, updateScoreDisplay); // start the display counting up
}

This method accepts an integer “amount” which it adds to the _totalScore variable. The second line starts an ENTER_FRAME event that calls a method called updateScoreDisplay every frame. We’ll add that next.

Step 18: Add the updateScoreDisplay Method

Now add a the updateScoreDisplay method. This is where all of the cool counting-up functionality will happen. It needs to accept an Event since it’s getting called from an ENTER_FRAME event.

// this runs every frame to update the score
private function updateScoreDisplay(e:Event):void {

}

Now let’s add some functionality. The first thing this method will do is to increment the _displayScore variable by the amount we set in our SPEED constant:

// increment the display score by the speed amount
_displayScore += SPEED;

There’s a potential problem here though. What if our speed is set to 10 and we try to add 5 to the score? The displayScore will be higher than the totalScore. Let’s add a couple lines to fix that:

// make sure the display score is not higher than the actual score
if(_displayScore > _totalScore){
	_displayScore = _totalScore;
}

That checks if the displayScore is higher than the totalScore and if so, sets the displayScore to be equal to the totalScore.

Next we need to add the leading zeros to the score. We’ll do this by converting the displayScore to a String and adding zeros until the length equals the number of digits defined by the NUM_DIGITS constant:

var scoreStr:String = String(_displayScore); // cast displayScore as a String

// add leading zeros
while(scoreStr.length < NUM_DIGITS){
	scoreStr = "0" + scoreStr;
}

Now to actually display the score we’re going to loop through each of our digit clips (remember we named then “digit1″, “digit2″, etc.) and use the corresponding number from the score string to set the frame number of the clip:

// loop through and update each digit
for (var i:int = 0; i < NUM_DIGITS; i++) {
	var num = int(scoreStr.charAt(i));
	this["digit"+(i+1)].gotoAndStop(num+1);// set the digit mc to the right frame
}

The charAt method retrieves the character from a String at the specified position. This lets us go character by character through the score string.

The brackets in the next line allow us to dynamically create the clip name. The code, this["digit"+(i+1)] accesses the clip with the name “digit1″ or “digit2″, etc., depending on the value of i.

We’re using “num+1″ as the frame number because the frame numbers are offset by 1 from the digits they contain (frame 1 shows 0, frame 2 shows 1, etc.)

The last thing we need to do in this method is check to see if the displayScore and the totalScore are equal. If so, we can remove the listener and stop calling this method for now.

// if the display score is equal to the total score remove the enterframe event
if(_totalScore == _displayScore){
	removeEventListener(Event.ENTER_FRAME, updateScoreDisplay);
}

If you got lost anywhere in that step you can check out the source files to see the completed class.

Step 19: The Score Class in Use

To use this class drag the Score movie clip from the Library onto the Stage and give it the instance name “myScore”. You can add points to your score by using this line in your Document Class:

myScore.add(50);

You can see an example of this in the source files. I’m adding to the score when the bumper buttons are clicked, but you’ll more likely be calling add() when events in your game occur.

If you need to know the player’s score you can get the totalScore by using:

myScore.totalScore

This will call the accessor method and return the value of _totalScore.

Conclusion

You now have a reusable counting Score class that you can use in any of your games.

I think the LED look is cool, but you should definitely alter the design to fit the look of your game. Here are a couple ideas for different designs to get you started:

TK

Thanks for reading this tutorial. Let me know what you think!

]]>
http://active.tutsplus.com/tutorials/effects/create-a-pinball-style-rolling-score-counter-class/feed/ 20
Create a Drag and Drop Puzzle in ActionScript 3.0 http://active.tutsplus.com/tutorials/games/create-a-drag-and-drop-game-in-actionscript-3-0/ http://active.tutsplus.com/tutorials/games/create-a-drag-and-drop-game-in-actionscript-3-0/#comments Mon, 11 Jan 2010 12:00:38 +0000 Carlos Yanez http://active.tutsplus.com/?p=2920 ]]> Drag-and-drop is the action of clicking on a virtual object and dragging it to a different location or onto another virtual object. In general, it can be used to invoke many kinds of actions, or create various types of associations between two objects.

In this tutorial we will create a Drag and Drop Matching game using ActionScript 3.

Step 1: Brief Overview

Using ActionScript 3, we will make draggable MovieClips that will be dropped in the MovieClip targets, we’ll check if the target its correct by using the hitTestObject method.

Step 2: Starting

Open Flash and create a new Flash File (ActionScript 3).

Set the stage size to 450×300 and add a black background (#1B1B1B).

””

Step 3: Draggable Clips

We’ll need some MovieClips to drag, I’ve used some of the Envato Marketplace logos.

Convert them to MovieClips and set their instance names:

Step 4: Drop Target

A MovieClip will be used as a drop target for each draggable clip, a simple rectangle will do the job.

Convert the rectangle to MovieClip and duplicate it (Cmd + D) to match the number of draggable objects.

The instance names will be the name of the draggable clip, plus Target, leaving us with denTarget, oceanTarget, etc.

Step 5: Guides

Let’s add some guides to help the user figure out what to do.

A title that will tell the user what to do with the elements in the screen.

An icon to tell the user how to do it.

Keywords to tell the user where to match the objects.

Step 6: ActionScript Time

Create a new ActionScript Document and save it as "Main.as".

Step 7: Required Classes

This time we’ll need just a few classes.

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

Step 8: Extending the Class

We’re going to use Sprite specific methods and properties so we extend using the Sprite Class.

public class Main extends Sprite
{

Step 9: Variables

These are the variables we will use, explained in the comments.

var xPos:int; //Stores the initial x position
var yPos:int; //Stores the initial y position

Step 10: Main Function

This function is executed when the class is loaded.

public function Main():void
{
	addListeners(den, ocean, jungle, river, forest); //A function to add the listeners to the clips in the parameters
}

Step 11: Position Function

A function to get the position of the MovieClips, this will help us return the MC to its original position when the drop target its incorrect or no drop target was hit.

private function getPosition(target:Object):void
{
	xPos = target.x;
	yPos = target.y;
}

Step 12: Start Drag

This function enables the dragging to the clip with the listener.

private function dragObject(e:MouseEvent):void
{
	getPosition(e.target);

	e.target.startDrag(true);
}

Step 13: Stop Drag

The next function stops the dragging when the mouse button is released, it also checks if the object is in the correct drop target.

private function stopDragObject(e:MouseEvent):void
{
	if (e.target.hitTestObject(getChildByName(e.target.name + "Target"))) //Checks the correct drop target
	{
		e.target.x = getChildByName(e.target.name + "Target").x; //If its correct, place the clip in the same position as the target
		e.target.y = getChildByName(e.target.name + "Target").y;
	}
	else
	{
		e.target.x = xPos; //If not, return the clip to its original position
		e.target.y = yPos;
	}

	e.target.stopDrag(); //Stop drag
}       

Step 14: Listeners

Adds the listeners to the clips in the parameters using the …rest argument.

private function addListeners(... objects):void
{
	for (var i:int = 0; i < objects.length; i++)
	{
		objects[i].addEventListener(MouseEvent.MOUSE_DOWN, dragObject);
		objects[i].addEventListener(MouseEvent.MOUSE_UP, stopDragObject);
	}
}

Step 15: Document Class

Go back to the .Fla file and in the Properties Panel add "Main" in the Class field to make this the Document Class.

Conclusion

Now you know how to easily make a drag target, this can be very useful for games and applications. Make your own drag app and take this concept further!

Thanks for reading!

]]>
http://active.tutsplus.com/tutorials/games/create-a-drag-and-drop-game-in-actionscript-3-0/feed/ 27
Build a Classic Snake Game in ActionScript 2.0 http://active.tutsplus.com/tutorials/games/build-a-classic-snake-game-in-actionscript-2-0/ http://active.tutsplus.com/tutorials/games/build-a-classic-snake-game-in-actionscript-2-0/#comments Wed, 30 Dec 2009 09:00:13 +0000 Joost Rijneveld http://active.tutsplus.com/?p=2637 ]]> Well, we’re approaching the end of 2009, and as ever, these final days in December are a time for reflection, memories and nostalgia. At Activetuts+ we endeavor to be forward-thinking, moving with the times and embracing technology. Saying that, it’s also important we appeal to all our readers, so for those of you who haven’t made the jump yet, let’s finish off the year with a bit of ActionScript 2.0!

In this tutorial, you’ll create a simple AS2 snake game.

Step 1: Setting up the File

Start Flash, and click Create New: Flash File (Actionscript 2.0). You can set the stage size to whatever pleases you – I’ve set mine to 500 x 350. I do advise you to set the framerate to 30fps.

Now, you can either decide to follow this step and draw the required movieclips yourself, or you can skip ahead and use the ones I created. If you want to use the ones I created you can find them in the source file. Just add them to the library and drag them onto the stage. If you are going to draw your own: you don’t have to literally draw the same as I did, but do make sure you stick to the sizes.

Step 2: Create the Snake Pit

The first thing we’ll create is the pit for the snake to roam around. Create a rectangle of 400 x 250 and convert it to a movieclip. Make sure you pay attention to the registration point; make it top-left.

Make sure you give the snake pit the instance name snakepit and align it slightly above the center of the stage (so that we can add text below it later, without messing up the aesthetics).

Step 3: Create the Snake’s Head

Draw a square (perhaps with rounded corners) without a border (size 25 x 25) and copy it so you have 2 exactly the same. Convert one of them to a movieclip. Again, pay attention to the top-left registration.

Make sure you give it the instance name bodypart0 and double check that its size is 25 x 25.

Step 4: Snake’s Head ActionScript

Enter the “head” movieclip. Once inside it, add this line of code to the first and only frame:

stop();

Now, draw a little face on the snake’s head, so you can tell it apart from other bodyparts.

Step 5: Additional Face

Add a second frame to the “head” movieclip, and on this frame, draw a sad or dead face.

Step 6: Create the Other Bodyparts

Remember the duplicate square you created in step 3? We’re going to convert it to a movieclip now. Pay attention to the registration point again.

Make sure you give it the instance name bodypart.

Step 7: Create the Score Field

Create a dynamic text field below the right corner of the snake pit. Make sure you set the variable to score. Also, make sure the field is wide enough for several characters, and aligned to the right. The rest of the settings can be set according to your own style – I used Tahoma 24 Bold.

Step 8: Embedding the Font

Since it’s a dynamic text field, we’ll have to embed the font. Click the embed button in the properties panel (where you just defined the variable and text style).

You’ll see the popup shown below. Make sure you select Numerals [0..9].

Step 9: Create the Food

Draw a blue circle without a border, size 25 x 25, and then erase the center using the eraser tool. Now select the circle that remains and convert it to a movieclip. Alternatively, you can design a piece of food for yourself, as long as it remains 25 x 25.

Step 10: Food Instance Name

Give the food a suprising instance name of food. Double check that its size is 25 x 25.

Step 11: Adding Instructions

Left-below the snake pit seemed like a good place to put some pointers. Simply type the static text “Use the arrow keys to move” here. I know, arrow keys don’t function yet, but we’ll get to that shortly. I promise!

Step 12: Creating the Popup

Draw a white square (somewhere outside the stage, for now) and add the text “You died!” to it. This is the popup we’ll display after someone has lost a game.

Convert it to a movieclip, and give it the instance name popup as well. For alignment purposes, I’ve set the registration point to the center.

Step 13: Score

Double click on the popup you just created to enter it. Once inside it, create a dynamic text field (variable = score) and a button reading the text Play Again.

The button should be given the instance name againbutton.

Inside the button, you can create a slightly darker “Over” and even darker “Down” state.

Congratulations, you have created all the visual objects required for the snake game. Now over to the code!

Step 14: newgame Function

Before I throw a chunk of code at you, I’ll tell you what to do with it. All the code in this tutorial goes on the first and only frame of your movie. No need to attach it to buttons or anything. Now, have a look at this piece of ActionScript.

Editor’s Note: Sorry about this folks, the code syntax highlighter doesn’t want to display this particular chunk of ActionScript in FireFox. Take a look at the code here.

In a nutshell, this bit of code does the following. First, the original bodypart is made invisible – we’ll use duplicates soon. Some default values are (re)set, any duplicates left from the previous game are deleted. In the do { } while() loop, the food and bodypart0 (the head) are given an x and y coordinate. If this matches (so they are at the exact same spot), they are given a new coordinate, until we no longer have a match. Then, the arrays are set up in which we’ll contain the bodyparts’ x and y coordinates.

Step 15: Grabbing the Direction

In this bit of code, we create a keyListener object, which we attach to Key, which represents the keyboard. Whenever a key is pressed, the onKeyDown function is triggered. From the getCode() function, we’ll derive which key was pressed and then define the direction according to this. We’ll store this in the direction variable for later use. Remember: all the code of this tutorial goes on the first and only frame of your movie.

		keyListener = new Object();
		keyListener.onKeyDown = function () {
			keycode = Key.getCode()
			if (keycode == 37) {
				direction = 'left'
			}
			else if (keycode == 38) {
				direction = 'up'
			}
			else if (keycode == 39) {
				direction = 'right'
			}
			else if (keycode == 40) {
				direction = 'down'
			}
		};
		Key.addListener(keyListener);
		

Step 16: onEnterFrame Function

The function down here is a big one. It’s executed on every frame, but only really does something every 5 frames. It checks if the snake isn’t moving outside the box, and then moves all the bodyparts along to their new position. It then checks if the food is there too, and then eats it using the eat() function we’ll discuss later. Then, it checks if the snake is smashing into a wall, and lastly it checks if the snake bites itself in the tail. All these functions will be added soon (insideSnake(), dead(), etcetera). Remember: all the code of this tutorial goes on the first and only frame of your movie.

		this.onEnterFrame = function () {
			if (game) {
				if (framecount/5 == Math.ceil(framecount/5)) {
					if (!(bodypart0._y <= snakepit._y && direction == 'up') && !(bodypart0._y+bodypart0._height >= snakepit._y+snakepit._height-2 && direction=='down') && !(bodypart0._x+bodypart0._width >= snakepit._x+snakepit._width - 2 && direction =='right') && !(bodypart0._x <= snakepit._x && direction == 'left'))
					{
						bodypartsy[0] = bodypart0._y;
						bodypartsx[0] = bodypart0._x;
						for (i=bodypartsy.length-1;i > 0; i--) {
							eval('bodypart'+i)._y = bodypartsy[(i-1)]
							eval('bodypart'+i)._x = bodypartsx[(i-1)]
							bodypartsy[i] = eval('bodypart'+i)._y
							bodypartsx[i] = eval('bodypart'+i)._x
						}
					}
					if (food._x == bodypart0._x && food._y == bodypart0._y) {
						eat()
					}
					if (direction) {
						if (direction == 'up') {
							if (bodypart0._y <= snakepit._y) {
								dead()
							}
							else {
								bodypart0._y-= bodypart0._width
							}
						}
						else if (direction == 'down') {
							if (bodypart0._y+bodypart0._height >= snakepit._y+snakepit._height-2) {
								dead()
							}
							else {
								bodypart0._y+= bodypart0._width
							}
						}
						else if (direction == 'right') {
							if (bodypart0._x+bodypart0._width >= snakepit._x+snakepit._width - 2) {
								dead()
							}
							else {
								bodypart0._x+= bodypart0._width
							}
						}
						else if (direction == 'left') {
							if (bodypart0._x <= snakepit._x) {
								dead()
							}
							else {
								bodypart0._x-= bodypart0._width
							}
						}
					}
					if (game) {
						if (insideSnake(bodypart0._x,bodypart0._y,true)) {
							dead()
						}
					}
				}
				framecount++
			}
		}
		

Step 17: dead() Function

This one’s nice and easy. It executes the actions that need to be taken when the snake dies: the score needs to be defined in the popup, the popup shown, the snake’s dead-face shown and the game-variable set to false (as the game ended).

		function dead() {
			popup.score = 'score: '+(bodypartsx.length-1)
			popup.swapDepths(this.getNextHighestDepth())
			popup._visible = true
			bodypart0.gotoAndStop(2)
			game = false;
		}
		

Step 18: eat() Function

The function below is triggered in the onEnterFrame function we discussed earlier, when the food is picked up. It first duplicates a part of the snake’s body, then positions it at the spot of the last bodypart (so that it’ll join in the line on the next frame). Its coordinates are added to the coordinate containing arrays, and the food is repositioned (somewhere not inside the snake!). Also, the score is updated.

		function eat() {
			duplicateMovieClip(bodypart,'bodypart'+bodypartsy.length,this.getNextHighestDepth())
			bodypart0.swapDepths(this.getNextHighestDepth())
			eval('bodypart'+bodypartsy.length)._y = eval('bodypart'+(bodypartsy.length-1))._y
			eval('bodypart'+bodypartsx.length)._x = eval('bodypart'+(bodypartsy.length-1))._x
			bodypartsy.push(eval('bodypart'+bodypartsy.length)._y)
			bodypartsx.push(eval('bodypart'+bodypartsx.length)._x)
			do {
				food._x = snakepit._x + Math.floor(((snakepit._width-food._width)/food._width)*Math.random())*food._width
				food._y = snakepit._y + Math.floor(((snakepit._height-food._height)/food._height)*Math.random())*food._height
			} while(insideSnake(food._x,food._y));
			score = bodypartsx.length-1
		}
		

Step 19: insideSnake() Function

This function merely checks if the inputted coordinates match any of the coordinates of the snake’s bodyparts. If skiphead is set to true, it is allowed to match the head’s coordinates (when checking if the head bites its tail, this comes in handy).

				function insideSnake(xneedle, yneedle,skiphead) {
			if (skiphead) { startat = 1; }
			else { startat = 0; }
			for (q=startat; q<bodypartsx.length; q++) {
				if (bodypartsx[q] == xneedle) {
					if (bodypartsy[q] == yneedle) {
						return true;
					}
				}
			}
			return false;
		}
		

Step 20: The Popup Button

As a last bit of code; we still have to add an on-release function to the button inside the popup! It’ll trigger the newgame() function we discussed earlier, when clicked. Remember: all the code of this tutorial goes on the first and only frame of your movie – including this bit about the button!

		popup.againbutton.onRelease = function() {
			newgame()
		}
		

Step 21: Wrapping it All Up

I’m sure you’ve done this before, but now is the time: hit Ctrl+Enter (or Cmd+Enter on a Mac) to compile the swf file and test your game. It should now be fully functional. Enjoy!

Conclusion

If you made it all the way here; congratulations! You created quite an addictive game, in AS2! Of course, a game is never finished. There is always room for creative improvement.

If you have any questions or suggestions, feel free to comment below. Also, I’m quite curious to see files produced as a result of this tutorial – I’d love to see a link in the comments!

Thanks for reading my tutorial. I hope you enjoyed it as much as I did.

]]>
http://active.tutsplus.com/tutorials/games/build-a-classic-snake-game-in-actionscript-2-0/feed/ 49
Send Season’s Greetings With a Flash Piano Christmas Card http://active.tutsplus.com/tutorials/games/send-seasons-greetings-with-a-flash-piano-christmas-card/ http://active.tutsplus.com/tutorials/games/send-seasons-greetings-with-a-flash-piano-christmas-card/#comments Mon, 14 Dec 2009 12:00:59 +0000 Bruno Crociquia http://active.tutsplus.com/?p=2697 ]]> Tired of those old-fashioned animated Christmas cards with Santa Claus laughing, a sleigh flying in the sky with a bunch of trees and sparkling lights?

This year you have no excuse. I’m going to show you how to build a virtual keyboard that plays Jingle Bells. You’ll even be able to extend the concept and add more songs and notes to the piano.

For this tut I’ll be using a combination of Tweenlite, the Flex SDK, the Flash IDE and Flash Develop.

Step 1: New File

Start Adobe Flash and create an ActionScript 3 Flash file.

Step 2: Properties

Open the properties and set the FPS to 30 and the stage size to 600 x 380px.

Step 3: Layer 1

Rename layer 1 as “background” and create a white rectangle of 580×360. Convert it to a movieclip symbol named “frame_mc” and set its position to x:10 y:10.

Step 4: Drop Shadow

Add a drop shadow filter with the following parameters:

Step 5: Keyboard Layer

Add a new layer called “keyboard”, create a primitive rectangle 60×190 with a 5 pixel corner radius and with no stroke. Make it a movieclip symbol and call it “keybase_mc”.

Step 6: Reflection and Glow Layers

Hit F8 and create a new movieclip called “key_mc”. Create two more layers inside key_mc (besides the one already present with keybase_mc). Name them: “reflection” and “glow”. Copy the frame to the newly created layers.

Step 7: glow_mc

NOTE: I’ve changed the frame_mc colors for a while to allow me to see the modifications on the keys. Select the movieclip in the glow layer, name it “glow_mc”, open the filters and edit them according to the image below:

Step 8: reflection_mc

Select the movieclip in the reflection layer, name it “reflection_mc”, open the filters and edit them to match the image below:

Step 9: base_mc

now select the movieclip in the base layer, name it “base_mc”, open the filters and edit them to match the image below:

Step 10: Key

Copy and paste the key until you end up with 7 instances. Arrange them evenly across the stage.

Step 11: Align

Open the align tool and click the “horizontal spacing” icon.

Step 12: Notes

Create a new layer, call it “notes”. Then write down C D E F G A B on the keys add the text to a new movieclip. Open the movieclip filters and edit them such as the image below:

Step 13: Numbers

Create a new layer, call it “numbers”. Write the numbers from 1 to 7, this will represent the numbers that you will press on your keyboard to make the ecard key highlight.

Step 14: Flex SDK Path

Go to edit > preferences > ActionScript > ActionScript 3.0 settings and locate your Flex SDK path (you will need this to embed files directly through code).

Step 15: Flash Develop

Open Flash Develop (I’m just using this cause I like it so much more than the usual ActionScript editor from the flash IDE when writing packages) and create 2 blank as3 files. Name them “Main.as” and “Background.as”, save them in the same folder as your .fla.

Step 16: Document Class

Inside Flash IDE set Main.as as your Document class.

Step 17: Autoplay Button

Create an autoplay movieClip and name it “autoplay_mc”. This will be an autoplay button.

Step 18: Snowflakes

To create some snow flakes falling create a new movieclip, draw a smalll white circle inside and add a linkage identifier of “SnowFlake”.

Step 19: Main.as

In Flash Develop open Main.as, define your Main class extending a movieclip and create a function called Main.

Start by importing these Classes inside your package:

	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.TimerEvent;
	import flash.display.StageScaleMode;
	import flash.events.KeyboardEvent;
	import flash.utils.Timer;
	import com.greensock.TweenLite;
	import com.greensock.easing.*;
	import Background; //we will create a background class based on perlin noise and a transform matrix along with a few filled shapes so we can have a nice smooth transition
	import Snow; //i just grabbed kirupas snow and packaged it -> http://troyworks.com/blog/2008/11/26/flash-kirupa-snow-in-as30/
	import flash.media.Sound;

Step 20: Variables

Inside your class define these variables:

		//this will be our background
		private var _background:Background;

		//this will be our snow storm
		private var snow:Snow;

		//->notes
		//Embeding this way requires FLEX SDK -> alternative method is to add these sounds to your library and add a linkage ID to them.
		[Embed(source="assets/A.mp3")]
		private var A:Class;
		private var a:Sound = new A() as Sound;

		[Embed(source="assets/B.mp3")]
		private var B:Class;
		private var b:Sound = new B() as Sound;

		[Embed(source="assets/C.mp3")]
		private var C:Class;
		private var c:Sound = new C() as Sound;

		[Embed(source="assets/D.mp3")]
		private var D:Class;
		private var d:Sound = new D() as Sound;

		[Embed(source = "assets/E.mp3")]
		private var E:Class;
		private var e:Sound = new E() as Sound;

		[Embed(source="assets/F.mp3")]
		private var F:Class;
		private var f:Sound = new F() as Sound;

		[Embed(source="assets/G.mp3")]
		private var G:Class;
		private var g:Sound = new G() as Sound;

		//Storing the notes in an array will make it easier to link to the keyboard
		private var notes:Array = [c,d,e,f,g,a,b]

		//Note sequence for the music
		private var noteSequence:Array = [f, f, f,
										f, f, f,
										f, a, d, e, f,
										g, g, g,
										g, g, e, e,
										d, b, a, f, d, c]
		//Current note that is playing
		private var curnote:Number = 0

		//Sequence of delay that the music needs to have between the notes
		private var noteDelay:Array = [100, 100 , 300,
									100, 100 ,300,
									100, 100, 100,100,200,
									100, 100, 200,
									90, 100, 90,100,
									100, 120, 120, 120, 120, 300]

		//Timer to play the music
		private var tunetimer:Timer = new Timer(noteDelay[0]);

Step 21: Main()

The Main function

		//Main function waits for the the maintimeline to be added to stage
		public function Main():void{
			addEventListener(Event.ADDED_TO_STAGE, addedToStage);
		}

Step 22: Initialize

After being added to stage we’ll initialize the background and the virtual keyboard:

		//when added to stage we can set a stage scale mode, the background and the start the virtual piano
		private function addedToStage(e:Event):void {
			stage.scaleMode = StageScaleMode.NO_SCALE;
			addBackground();
			startMachine();
		}

Step 23: Background Effects

Let’s sort out the moving background and the Snow:

//adds the background
		private function addBackground():void {

			_background = new Background(150,150); //the reason the size is smaller is because it's very CPU intensive
			_background.x = 5; //to give a white margin for the frame
			_background.y = 5;
			_background.width = 570 //scale it up to the size right size
			_background.height = 350
			frame_mc.addChild(_background); //adds background to the frame

			snow = new Snow(570, 350)	//creates a snow storm instance

Step 24: Virtual Keyboard

The initialization of the virtual keyboard

private function startMachine(e:MouseEvent = null):void {

			//associates the keyboard events
			stage.addEventListener(KeyboardEvent.KEY_DOWN, onkeyDown)
			stage.addEventListener(KeyboardEvent.KEY_UP, onkeyUp)

			//associates an autoplay method to the autoplay button
			autoplay_mc.addEventListener(MouseEvent.CLICK, startAutoPlay);
			autoplay_mc.buttonMode = true;

			//associates the notes to the keys
			var i:int = 0
			while (i < 7) {
				this["key"+i].note = notes[i]
				i++
			}
			//makes the highlight of the keys disappear
			lowlightKeys();
		}

Step 25: Highlight

We need to create a function to remove the highlight from the keys:

private function lowlightKeys() {
			var i:int = 0
			while (i < 7) {
				TweenLite.to(this["key" + i].glow_mc, 0.5,{alpha:0} );
				i++
			}
		}

Step 26: Key Events

Let's now handle the Key up and Key down events:

private function onkeyDown(e:KeyboardEvent):void {
			var i:int=0
			switch(e.keyCode) {
				case 49: //keycode for 1
					i=0
				break;
				case 50: //keycode for 2
					i=1
				break;
				case 51: //keycode for 3
					i=2
				break;
				case 52: //keycode for 4
					i=3
				break;
				case 53: //keycode for 5
					i=4
				break;
				case 54: //keycode for 6
					i=5
				break;
				case 55: //keycode for 7
					i=6
				break;
			}
			notes[i].play();
			TweenLite.to(this["key" + i].glow_mc, 0.5,{alpha:1} ); //highlights the key
		}

		private function onkeyUp(e:KeyboardEvent):void {
			var i:int=0
			switch(e.keyCode) {
				case 49:
					i=0
				break;
				case 50:
					i=1
				break;
				case 51:
					i=2
				break;
				case 52:
					i=3
				break;
				case 53:
					i=4
				break;
				case 54:
					i=5
				break;
				case 55:
					i=6
				break;
			}
			TweenLite.to(this["key" + i].glow_mc, 0.5,{alpha:0} ); //lowlights the key
		}

Step 27: Autoplay Functions

How to start and stop the autoplay

private function startAutoPlay(e:MouseEvent = null) {
			curnote = 0; //everytime i start autoplay i reset the current playing note
			tunetimer.delay = noteDelay[curnote] * 3 //this increases the delay set previously
			tunetimer.addEventListener(TimerEvent.TIMER, autoPlayTune) //adds a listener to the timer event for everytime the timer is triggered
			tunetimer.start() //starts the timer
		}
private function stopAutoPlay(e:MouseEvent = null) {
			tunetimer.stop() //stops timer
			tunetimer.removeEventListener(TimerEvent.TIMER, autoPlayTune) //removes the event
		}

Step 28: Update

Updating the virtual keyboard along with the music

private function updateMachine():void {
			lowlightKeys(); // resets the keys highlights
			var i:int = 0
			while (i < 7) {
				if (this["key" + i].note == noteSequence[curnote]) {
					TweenLite.to(this["key" + i].glow_mc, 0.5,{alpha:1} ); //if current note is the one associeated with the key then highlights it
				}
				i++
			}
			curnote++ //goes to next note
			if (curnote > noteSequence.length) {
				curnote = 0; //resets current note
				stopAutoPlay(); //stops autoplay
			}
		}

Step 29: The Complete Code

Here's the full Main.as code

package{

	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.TimerEvent;
	import flash.display.StageScaleMode;
	import flash.events.KeyboardEvent;
	import flash.utils.Timer;
	import com.greensock.TweenLite;
	import com.greensock.easing.*;
	import Background;
	import Snow
	import flash.media.Sound;

	public class Main extends MovieClip{

		private var _background:Background;

		private var snow:Snow;

		[Embed(source="assets/A.mp3")]
		private var A:Class;
		private var a:Sound = new A() as Sound;

		[Embed(source="assets/B.mp3")]
		private var B:Class;
		private var b:Sound = new B() as Sound;

		[Embed(source="assets/C.mp3")]
		private var C:Class;
		private var c:Sound = new C() as Sound;

		[Embed(source="assets/D.mp3")]
		private var D:Class;
		private var d:Sound = new D() as Sound;

		[Embed(source = "assets/E.mp3")]
		private var E:Class;
		private var e:Sound = new E() as Sound;

		[Embed(source="assets/F.mp3")]
		private var F:Class;
		private var f:Sound = new F() as Sound;

		[Embed(source="assets/G.mp3")]
		private var G:Class;
		private var g:Sound = new G() as Sound;

		private var notes:Array = [c,d,e,f,g,a,b]

		private var noteSequence:Array = [f, f, f,
						  f, f, f,
						  f, a, d, e, f,
						  g, g, g,
						  g, g, e, e,
						  d, b, a, f, d, c]
		private var curnote:Number = 0

		private var noteDelay:Array = [100, 100 , 300,
					       100, 100 ,300,
					       100, 100, 100,100,200,
					       100, 100, 200,
					       90, 100, 90,100,
					       100, 120, 120, 120, 120, 300]

		private var tunetimer:Timer = new Timer(noteDelay[0]);

		public function Main():void{
			addEventListener(Event.ADDED_TO_STAGE, addedToStage);
		}

		private function addedToStage(e:Event):void {
			stage.scaleMode = StageScaleMode.NO_SCALE;
			addBackground();
			startMachine();
		}

		//adds the background
		private function addBackground():void {

			_background = new Background(150,150);
			_background.x = 5;
			_background.y = 5;
			_background.width = 570
			_background.height = 350
			frame_mc.addChild(_background);

			snow = new Snow(570, 350);
			frame_mc.addChild(snow);
		}

		private function startMachine(e:MouseEvent = null):void {

			stage.addEventListener(KeyboardEvent.KEY_DOWN, onkeyDown)
			stage.addEventListener(KeyboardEvent.KEY_UP, onkeyUp)

			autoplay_mc.addEventListener(MouseEvent.CLICK, startAutoPlay);
			autoplay_mc.buttonMode = true;

			var i:int = 0
			while (i < 7) {
				this["key"+i].note = notes[i]
				i++
			}
			lowlightKeys();
		}

		private function lowlightKeys() {
			var i:int = 0
			while (i < 7) {
				TweenLite.to(this["key" + i].glow_mc, 0.5,{alpha:0} );
				i++
			}
		}

		private function onkeyDown(e:KeyboardEvent):void {
			var i:int=0
			switch(e.keyCode) {
				case 49:
					i=0
				break;
				case 50:
					i=1
				break;
				case 51:
					i=2
				break;
				case 52:
					i=3
				break;
				case 53:
					i=4
				break;
				case 54:
					i=5
				break;
				case 55:
					i=6
				break;
			}
			notes[i].play();
			TweenLite.to(this["key" + i].glow_mc, 0.5,{alpha:1} );
		}

		private function onkeyUp(e:KeyboardEvent):void {
			var i:int=0
			switch(e.keyCode) {
				case 49:
					i=0
				break;
				case 50:
					i=1
				break;
				case 51:
					i=2
				break;
				case 52:
					i=3
				break;
				case 53:
					i=4
				break;
				case 54:
					i=5
				break;
				case 55:
					i=6
				break;
			}
			TweenLite.to(this["key" + i].glow_mc, 0.5,{alpha:0} );
		}

		//AUTO PLAY FUNCTIONS
	    private function startAutoPlay(e:MouseEvent = null) {
			curnote = 0;
			tunetimer.delay = noteDelay[curnote] * 3
			tunetimer.addEventListener(TimerEvent.TIMER, autoPlayTune)
			tunetimer.start()
		}

		private function stopAutoPlay(e:MouseEvent = null) {
			tunetimer.stop()
			tunetimer.removeEventListener(TimerEvent.TIMER, autoPlayTune)
		}

		private function autoPlayTune(e:TimerEvent) {
			if(curnote<noteSequence.length){
				noteSequence[curnote].play(); //plays the note
				tunetimer.delay = noteDelay[curnote] * 3
			}
			updateMachine()
		}

		private function updateMachine():void {
			lowlightKeys();
			var i:int = 0
			while (i < 7) {
				if (this["key" + i].note == noteSequence[curnote]) {
					TweenLite.to(this["key" + i].glow_mc, 0.5,{alpha:1} );
				}
				i++
			}
			curnote++
			if (curnote > noteSequence.length) {
				curnote = 0;
				stopAutoPlay();
			}
		}
	}

}

Step 30: Background Class

Now on to the background class. We'll begin by importing these classes..

	import flash.display.Shape;
	import flash.events.Event;
	import flash.display.Sprite;
	import flash.display.MovieClip;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.BlendMode;
	import flash.geom.ColorTransform;
	import flash.geom.Rectangle;
	import flash.geom.Point;
    import flash.geom.Matrix;
   	import flash.filters.ColorMatrixFilter;
	import flash.display.GradientType;
   	import flash.display.Graphics;
   	import flash.display.InterpolationMethod;
	import flash.display.SpreadMethod;

Step 31: Variables

..then defining the variables:

		//Noise
		private var dir:Array
		private var point:Point
		private var bd:BitmapData;
		private var bmp:Bitmap;
		private var bdmultiply:BitmapData;
		private var bms:Sprite;
		private var rect:Rectangle
		private var cmf:ColorMatrixFilter;
		private var w:Number
		private var h:Number

		//Linear Gradient Fill
		private var gshape:Shape
        private var gmetrics:Rectangle
        private var gmatrix:Matrix
		private var gtype:String
		private var gspread:String
		private var ginterpolation:String
		private var gcolours:Array
		private var galphas:Array
		private var gratios:Array

		//Solid Fill
		private var sshape:Shape

Step 32: Initial Function

Here's the initial function:

public function Background($width:Number=100, $height:Number=100)
		{
			w = $width
			h = $height
			rect = new Rectangle(0, 0, w, h);
			point = new Point(0, 0);
			dir = [new Point(1, 262), new Point(400, 262)];

			//this one is just to give a solid background to the whole stage
			initBackgroundSolid();

			//this will control the bightness contrast and saturation of the noise
			initColorMatrix();

			//there will be two noise backgrounds this will initiate them
			initBackgroundNoise();

			//a gradient is added so we dont get noisiated.. (get it? too much noise gets you noisiated? haha...hmmm)
			initBackgroundGradient();
		}

Step 33: initColorMatrix()

This function will control the bightness contrast and saturation of the noise, it's a very powerfull filter.

	private function initColorMatrix():void {
		cmf = new ColorMatrixFilter([2, 0, 0, 0, -20, //red
									 0, 2, 0, 0, -20, //green
									 0, 0, 2, 0, -20, //blue
									 0, 0, 0, 1, -20 ]); //alpha
		}

Step 34: Solid Background Color

This one is just to give a solid background to the whole stage.

	private function initBackgroundSolid():void {
			sshape = new Shape();
			sshape.graphics.beginFill(0x170a02,1)
			sshape.graphics.drawRect( 0, 0, w, h );
			sshape.graphics.endFill();
			addChild(sshape)
		}

Step 35: Noises

The noises:

	private function initBackgroundNoise():void {
		//first noise
		bd = new BitmapData(w, h, false, 0 );
		bmp = new Bitmap(bd);
		bmp.smoothing = true;
		addChild(bmp);

		//second noise that overlaps the first noise through an overlay blend mode
		bdmultiply = new BitmapData(w, h, false, 0 );
		bms = new Sprite();
		bms.addChild(new Bitmap(bdmultiply))
		addChild(bms)
		bms.blendMode = "overlay";

		//renders the background so that the noise seems to be moving
		addEventListener(Event.ENTER_FRAME, renderBG);
	}

Step 36: Mask

Here's the gradient mask:

	private function initBackgroundGradient() {

			//this is a basic gradient box with alpha and rotated 90º so that it starts from top-bottom instead of left-right
			gshape = new Shape();
			gmetrics = new Rectangle();
			gmatrix = new Matrix();

			gtype = GradientType.LINEAR;
			gspread = SpreadMethod.PAD;
			ginterpolation = InterpolationMethod.LINEAR_RGB;

			gcolours = [ 0x170a02, 0x170a02 ];
			galphas = [ 0, 1 ];
			gratios = [ 0, 255 ];

			gmatrix.createGradientBox(w, h,(Math.PI/180)*90 );

			gshape.graphics.clear();
			gshape.graphics.beginGradientFill(gtype, gcolours, galphas, gratios, gmatrix, gspread, ginterpolation );

			gshape.graphics.drawRect( 0, 0, w, h );
			gshape.graphics.endFill();
			addChild(gshape)
	}

Step 37: Render

It's render time!

	private function renderBG(event:Event):void {
		//updates noise direction
		dir[0].x-= 1.5
		dir[0].y-= 0 //these are here for you to play with
		dir[1].x-= 0 //these are here for you to play with
		dir[1].y -= 0 //these are here for you to play with

		//defines the first background bitmap to have a perlin noise
		bd.perlinNoise(w, h, 2, 10, false, true, 7, true, dir);
		//coloring time (play with these values)
		bd.colorTransform(rect, new ColorTransform(1, 0.7, 0.5));
		//aplies the brigthness contrast and saturation modifications made earlier
		bd.applyFilter(bd, rect, point, cmf)

		//the other perlin noise
		bdmultiply.perlinNoise(w, h, 3, 21, false, true, 7, true, dir)
		//the other perlin noise collors
		bdmultiply.colorTransform(rect, new ColorTransform(1, 0.6, 0.4));
	}

Step 38: Complete Background Class

Here's the whole background Class:

	package
{
	import flash.display.Shape;
	import flash.events.Event;
	import flash.display.Sprite;
	import flash.display.MovieClip;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.BlendMode;
	import flash.geom.ColorTransform;
	import flash.geom.Rectangle;
	import flash.geom.Point;
   	import flash.geom.Matrix;
    	import flash.filters.ColorMatrixFilter;
	import flash.display.GradientType;
    	import flash.display.Graphics;
   	import flash.display.InterpolationMethod;
	import flash.display.SpreadMethod;

	public class Background extends MovieClip
	{
		private var dir:Array
		private var point:Point
		private var bd:BitmapData;
		private var bmp:Bitmap;
		private var bdmultiply:BitmapData;
		private var bms:Sprite;
		private var rect:Rectangle
		private var cmf:ColorMatrixFilter;
		private var w:Number
		private var h:Number

		private var gshape:Shape
        	private var gmetrics:Rectangle
       		private var gmatrix:Matrix
		private var gtype:String
		private var gspread:String
		private var ginterpolation:String
		private var gcolours:Array
		private var galphas:Array
		private var gratios:Array

		private var sshape:Shape

		public function Background($width:Number=100, $height:Number=100)
		{
			w = $width
			h = $height
			rect = new Rectangle(0, 0, w, h);
			point = new Point(0, 0);
			dir = [new Point(1, 262), new Point(400, 262)];

			initBackgroundSolid();
			initColorMatrix();
			initBackgroundNoise();
			initBackgroundGradient();
		}

		private function initColorMatrix():void {
			cmf = new ColorMatrixFilter([2, 0, 0, 0, -20, //red
						     0, 2, 0, 0, -20, //green
						     0, 0, 2, 0, -20, //blue
						     0, 0, 0, 1, -20 ]); //alpha
		}

		private function initBackgroundSolid():void {
			sshape = new Shape();
			sshape.graphics.beginFill(0x170a02,1)
			sshape.graphics.drawRect( 0, 0, w, h );
			sshape.graphics.endFill();
			addChild(sshape)
		}

		private function initBackgroundNoise():void {
			bd = new BitmapData(w, h, false, 0 );
          		bmp = new Bitmap(bd);
			bmp.smoothing = true;
			addChild(bmp);

			bdmultiply = new BitmapData(w, h, false, 0 );
			bms = new Sprite();
			bms.addChild(new Bitmap(bdmultiply))
			addChild(bms)
			bms.blendMode = "overlay";

			addEventListener(Event.ENTER_FRAME, renderBG);
		}

		private function initBackgroundGradient() {

			gshape = new Shape();
			gmetrics = new Rectangle();
			gmatrix = new Matrix();

			gtype = GradientType.LINEAR;
			gspread = SpreadMethod.PAD;
			ginterpolation = InterpolationMethod.LINEAR_RGB;

			gcolours = [ 0x170a02, 0x170a02 ];
			galphas = [ 0, 1 ];
			gratios = [ 0, 255 ];

			gmatrix.createGradientBox(w, h,(Math.PI/180)*90 );

			gshape.graphics.clear();
			gshape.graphics.beginGradientFill(gtype, gcolours, galphas, gratios, gmatrix, gspread, ginterpolation );

			gshape.graphics.drawRect( 0, 0, w, h );
			gshape.graphics.endFill();
			addChild(gshape)
		}

		private function renderBG(event:Event):void {
			dir[0].x-= 1.5
			dir[0].y-= 0
			dir[1].x-= 0
			dir[1].y -= 0

			bd.perlinNoise(w, h, 2, 10, false, true, 7, true, dir);
			bd.colorTransform(rect, new ColorTransform(1, 0.7, 0.5));
			bd.applyFilter(bd, rect, point, cmf)

			bdmultiply.perlinNoise(w, h, 3, 21, false, true, 7, true, dir)
			bdmultiply.colorTransform(rect, new ColorTransform(1, 0.6, 0.4));
		}

 	}
 }

Step 39: Snow

The snow class is not mine, it was written by Troy Gardner, I just adapted it from the timeline to a package this is why I'm not commenting on the code. Create a "Snow.as" and copy this code inside.

	package
{
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.utils.Dictionary;

	public class Snow extends MovieClip
	{
		var snowflakes:Array = new Array();
		var snowflakeProps:Dictionary= new Dictionary(true);
		var max_snowsize:Number = .04;
		// pixels
		var snowflakesCnt:Number = 150;
		var oheight:Number;
		var owidth:Number;

		public function Snow($width,$height):void {

			owidth = $width;
			oheight = $height;
			// quantity
			for (var i:int=0; i<snowflakesCnt; i++) {

				var t:MovieClip = new SnowFlake();//
				t.name = "snowflake"+i;

				t.alpha = 20+Math.random()*60;
				t.x = -(owidth/2)+Math.random()*(1.5*owidth);
				t.y = -(oheight/2)+Math.random()*(1.5*oheight);
				t.scaleX = t.scaleY=.5+Math.random()*(max_snowsize*10);
				var o:Object = new Object();
				o.k = 1+Math.random()*2;
				o.wind = -1.5+Math.random()*(1.4*3);

				snowflakeProps[t] = o;

				addChild(t);
				snowflakes.push(t);
			}
			addEventListener(Event.ENTER_FRAME,snowFlakeMover)
		}
		private function shakeUp():void{
			for (var i:int=0; i<snowflakes.length; i++) {
				var t:MovieClip = snowflakes[i] as MovieClip;
				t.x = -(owidth/2)+Math.random()*(1.5*owidth);
				t.y = -(oheight/2)+Math.random()*(1.5*oheight);
			}
		}
		private function snowFlakeMover(evt:Event):void {
			var dO:MovieClip;
			var o :Object;
			if(visible && parent.visible){
			for (var i:int = 0; i < snowflakes.length; i++) {
				dO = snowflakes[i] as MovieClip;
				o = snowflakeProps[dO];
				dO.y += o.k;
				dO.x += o.wind;
				if (dO.y>oheight+10) {

					dO.y = -20;

				}
				if (dO.x>owidth+20) {

					dO.x = -(owidth/2)+Math.random()*(1.5*owidth);
					dO.y = -20;

				} else if (dO.x<-20) {

					dO.x= -(owidth/2)+Math.random()*(1.5*owidth);
					dO.y = -20;
				}

			}
			}
		}
	}

}

Conclusion

My music skills aren't the greatest, the music may sound a bit strange. Oh well, with this tutorial you should now be able to create your own songs with more notes and different tones :) . I hope you liked this tutorial, you'll find the commented code and both cs4 and cs3 versions up on the zip file. Thanks for reading!

]]>
http://active.tutsplus.com/tutorials/games/send-seasons-greetings-with-a-flash-piano-christmas-card/feed/ 29
Conquer the Universe With ActionScript 3.0 and the Flixel Game Engine: Part 2 http://active.tutsplus.com/tutorials/screencasts/conquer-the-universe-with-actionscript-3-0-and-the-flixel-game-engine-part-2/ http://active.tutsplus.com/tutorials/screencasts/conquer-the-universe-with-actionscript-3-0-and-the-flixel-game-engine-part-2/#comments Mon, 30 Nov 2009 12:00:09 +0000 Shane Johnson http://active.tutsplus.com/?p=2595 ]]> The Flixel game engine aims is to take the donkey work out of building AS3 games, leaving you with all the fun stuff and saving heaps of time!

During Part 1 of this screencast, Shane covered the basics of Flixel and began work on his brilliant 8-bit style shoot ‘em up. In this second part he takes things further, creating enemies, backgrounds, explosions, the health monitor, the playing arena, other assets and more. The end result is well worth following along for, even for the non-gamers amongst you..

View Screencast: Section One

Enemy Creation

Get the Flash Player to see this player.

View Screencast: Section Two

Background and Extra Assets

Get the Flash Player to see this player.

View Screencast: Section Three

Creating the Main Play Arena

Get the Flash Player to see this player.

View Screencast: Section Four

Finishing Off

Get the Flash Player to see this player.

]]>
http://active.tutsplus.com/tutorials/screencasts/conquer-the-universe-with-actionscript-3-0-and-the-flixel-game-engine-part-2/feed/ 8
Build Your Own OOP Connect 4 Game http://active.tutsplus.com/tutorials/games/build-your-own-oop-connect-4-game/ http://active.tutsplus.com/tutorials/games/build-your-own-oop-connect-4-game/#comments Wed, 25 Nov 2009 12:00:43 +0000 Steven Devooght http://active.tutsplus.com/?p=2386 ]]> OOP can make development faster, and your applications run faster. During this tutorial I’ll demonstrate how to build an ActionScript Connect 4 game, using an organized OOP approach.

Let’s get started!

Step 1: Start Off

In the source files I’ve included a connect4_start.fla file. Start from this file; it contains all the movieclips needed to finish the tutorial. It contains 4 movieclips. A red chip movieclip, a yellow chip movieclip, a boardpiece movieclip and a winner dialog movieclip.

Step 2: Create the Connect4 Class File

Create a new ActionScript file and save it with the name Connect4. Then add the following code:

package
{
	import flash.display.Sprite
	import flash.events.MouseEvent;
	import flash.events.Event;
	import flash.geom.Point;
	import caurina.transitions.Tweener;

	public class Connect4 extends Sprite
	{
		private var columns:uint;
		private var rows:uint;
		private var board:Array = new Array();
		private var columnWidth:uint;
		private var rowHeight:uint;
		private var currentPlayer:uint = 1;
		private var currentChip;

		public function Connect4(columns:uint,rows:uint):void
		{
			this.columns = columns
			this.rows = rows

			columnWidth = new BoardPiece().width
			rowHeight = new BoardPiece().height
		}
	}
}

In this code block we import the classes (I also used the Tweener class which you can find here or in my source files). We define the field variables and we create the constructor. The constructor has two arguments, the number of columns and rows you want for your game. We put these values in our field variables.

Finally we init the columnWidth and rowHeight variable. One column has the same width as the width of one boardPiece (same principle for rowHeight).

Step 3: drawBoard Function

This function will draw the playboard of the game depending on how many columns and rows you want. Add this function to the constructor of the Connect4 class:

private function drawboard():void
{
	for(var i:uint = 0; i < rows; i++)
	{
		for(var j:uint = 0; j < columns; j++)
		{
			var boardpiece:BoardPiece = new BoardPiece();
			boardpiece.x = j * boardpiece.width;
			boardpiece.y = i * boardpiece.height;
			this.addChild(boardpiece);
		}
	}
}

Step 4: createBoardArray Function

Here we create a 2-dimensional array which will contain all the locations of the board.

We fill the array with 0’s. A zero means there hasn’t been a chip placed on that position. This function also needs to be added to the constructor.

private function createboardArray():void
{
	for(var i:uint = 0; i < rows; i++)
	{
		board[i] = []
		for(var j:uint=0; j < columns; j++)
		{
			board[i][j] = 0;
		}
	}
}

Step 5: putChipReady Function

In this function we identify who the current player is and add the matching chip to the display list. We give the chip a negative y value so it shows up above our playboard. Once again, add this function to the constructor.

private function putChipReady():void
{
	if(currentPlayer == 1)
	{
		currentChip = new RedChip();
	}
	else
	{
		currentChip = new YellowChip();
	}
	currentChip.y = -50;
	this.addChildAt(currentChip,0);
}

Step 6: enterFrameHander

On every frame this function calculates the column where the current chip should fall if you click at that moment. That way it’s easier for the user to make a move. The function calculateColumn(explained in the next step) returns the column you are hovering.

Then we calculate the new x position of the chip. Therefore we multiply the currentColumn with the column width. Because the registration point of the chips in centered we have to add the column width divided by 2.

Finally we tween the current chip to the postion we just calculated.

private function enterFrameHandler(e:Event):void
{
	var currentcolumn:uint = calculateColumn(this.mouseX);
	var xPosChip:uint = currentcolumn * columnWidth + columnWidth/2
	Tweener.addTween(currentChip, {x:xPosChip, time:0.3, transition:"lineair"});
}

Add the enter frame Event Listener to the constructor.

this.addEventListener(Event.ENTER_FRAME, enterFrameHandler);

Step 7: calculateColumn Function

This helpfunction gets the x position of the mouse and returns whichever column matches.

If your mouse x position is less then zero then we return the first column(0 because array is zero-indexed). If your mouse x position is more then the width of your board we return the last column. If your mouse x position is on the board we devide the x postion by the width of one column.

private function calculateColumn(mouseXPos):uint
{
	if(mouseXPos < 0)
	{
		return 0;
	}
	else if(mouseXPos > this.width)
	{
		return columns - 1;
	}
	else
	{
		return mouseXPos/columnWidth;
	}
}

Step 8: boardClick Function

This function first checks which column you clicked (by using the calculate column function which I explained in the previous step).

The for loops through all the rows and as soon as board[row][columnclicked] == 0 we know the chip has to be placed on that location. Remember 0 in the BOARD array means the location is empty.

If we find the location where the chip has to fall, we fill that postion in the board array with the number of the current player (we need these numbers later on to check for the winner) and we place that chip with the placeChip function (explained in the next step).

Finally we toggle the player (explained 2 steps further) and we put another chip ready. The return makes sure we exit the for loop.

private function boardClick(e:MouseEvent):void
{
	var columnclicked:uint = calculateColumn(e.currentTarget.mouseX);

	for(var row:int=rows-1; row>=0; row--)
	{

		if(board[row][columnclicked]==0)
		{
			board[row][columnclicked] = currentPlayer;
			placeChip(new Point(row,columnclicked))
			togglePlayer();
			putChipReady();
			return
		}
	}
}

Add the click Event Listener to the constructor.

this.addEventListener(MouseEvent.CLICK, boardClick)

Step 9: placeChip Function

This function gets the row(position.x) and column(position.y) where the chip has to enter, then calculates the y- and x distance.

distanceY: you multiply the row you clicked with the height of one row. Because the registration point of the chips is centered we have to add the row height divided by 2.

distanceX uses the same principle.

Then we use tweener to tween the current chip to the right position.

private function placeChip(position:Point):void
{
	var distanceY:int = position.x * rowHeight + rowHeight/2;
	var distanceX:int = position.y * columnWidth + columnWidth/2;
	Tweener.addTween(currentChip, {x: distanceX, y:distanceY, time:0.7, transition:"easeOutBounce"});
}

Step 10: togglePlayer Function

This function is pretty straight-forward. If the current player is 1 switch to player 2, else switch back to player 1.

private function togglePlayer():void
{
	if(currentPlayer == 1)
	{
		currentPlayer = 2
	}
	else
	{
		currentPlayer = 1
	}
}

At this point you can already place the chips, but there’s currently no check to see if a player managed to connect 4 chips. The next step is to code this check.

Step 11: checkForWinner Function

This function has 1 argument, the position of the last placed chip and return true or false. The function uses 4 subfunctions each of which checks for a winner.

We pass through the position for each subfunction. If one of the 4 returns true we have a winner.

private function checkForWinner(position:Point):Boolean
{
	if(verticalCheck(position))return true;
	if(horizontalCheck(position))return true;
  	if(leftUpDiagonalCheck(position))return true;
    if(rightUpDiagonalCheck(position))return true;
    return false;
}

Step 12: verticalCheck

To check for a vertical connect 4 we only have to look at the chips beneath the current chip. (There can’t be a chip above the current chip at this point).

First we check if there are 3 locations beneath the current chip. If not, there’s no way you can connect 4 and we return false.

If there are 3 or more locations beneath, we start a loop which goes through the 3 rows beneath. If one of the chips beneath is from the other player we did not connect 4 chips (return false).
If the loop can be finished we know there are 4 connected (return true).

function verticalCheck(position:Point):Boolean
{
	var row:uint = position.x;
	var column:uint = position.y;
	var player:uint = board[row][column];

	if (row >= rows - 3)
	{
		return false;
	}

	for (var i:uint = row+1; i <= row + 3; i++)
	{
		if (board[i][column] != player)
		{
			return false;
		}
	}
	return true;
}

Step 13: horizontalCheck

What we do here is first check the chips on the left of the current chip and then the chips on the right side.

Therefore we init a counter with 1 (the chip you just placed is the first one in the row). Then we go to the left until we reach a chip of an other player and meanwhile we count the chips of the current player.

We do the same for the right side. So if our counter is 4 or more we have connected 4 chips (return true).

function horizontalCheck(position:Point):Boolean
{
	var row:uint = position.x;
	var column:uint = position.y;
	var player:uint = board[row][column];
	var counter:uint = 1;

	for(var i:uint = column-1; i>=0; i--)
	{
		if(board[row][i] != player)
		{
			break;
		}
		counter++;
	}

	for(var j:uint = column+1; j<columns; j++)
	{
		if(board[row][j] != player)
		{
			break;
		}
		counter++;
	}

	if(counter >=4)
	{
		return true;
	}
	else
	{
		return false;
	}
}

Step 14: leftUpDiagonalCheck

This functions is similar to the horizontal check. The only difference is that we now check diagonally.

First we go left upwards: we count the chips of the current player and if we come across a chip of the other player we break the loop. To make sure we don't go outside our board array the while loop has to stop when our row or our column is less then 0.

We use the same principle to check the positions right downwards.

function leftUpDiagonalCheck(position:Point):Boolean
{
	var player:uint = board[position.x][position.y];

	var row:Number = position.x - 1;
	var column:Number = position.y - 1;

	var counter:uint = 1;

	while (row >= 0 && column >= 0)
	{
		if (board[row][column] == player)
		{
			counter++;
			row--;
			column--;
		}
		else
		{
			break;
		}
	}

	row = position.x + 1;
	column = position.y + 1;

	while (row < rows && column < columns)
	{
		if (board[row][column] == player)
		{
			counter++;
			row++;
			column++;
		}
		else
		{
			 break;
		}
	}
	if(counter >=4)
	{
		return true;
	}
	else
	{
		return false;
	}
}

Step 15: rightUpDiagonalCheck

This function is almost identical to the previous function. The only difference is the direction of the diagonal we check.

private function rightUpDiagonalCheck(position:Point):Boolean
{
	var player:uint = board[position.x][position.y];

	var row:Number = position.x + 1;
	var column:Number = position.y - 1;

	var counter:uint = 1;

	while (row < rows && column >= 0)
	{
		if (board[row][column] == player)
		{
			counter++;
			row++;
			column--;
		}
		else
		{
			break;
		}
	}

	row = position.x - 1;
	column = position.y + 1;

	while (row >= 0 && column < columns)
	{
		if (board[row][column] == player)
		{
			counter++;
			row--;
			column++;
		}
		else
		{
			 break;
		}
	}
	if(counter >=4)
	{
		return true;
	}
	else
	{
		return false;
	}
}

Step 16: Updating the boardClick Function

Now we have to implement the code we just wrote. After we place the chip we check for a winner. If we have a winner we remove our EventListeners so you can't place a new chip and we show who's won (explained in the next step). If we don't have a winner we togglePlayer and put a new chip ready.

private function boardClick(e:MouseEvent):void
{
	var columnclicked:uint = calculateColumn(e.currentTarget.mouseX);

	for(var row:int=rows-1; row>=0; row--)
	{

		if(board[row][columnclicked]==0)
		{
			board[row][columnclicked] = currentPlayer;
			placeChip(new Point(row,columnclicked))
			if(checkForWinner(new Point(row,columnclicked)))
			{
				this.removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
				this.removeEventListener(MouseEvent.CLICK, boardClick);
				showWinnerDialog();
			}
			else
			{
				togglePlayer();
				putChipReady();
			}
			return
		}
	}
}

Step 17: showWinnerDialog Function

This function simply adds a new instance of the WinnerDialog which you can find in the library. If the current player is 1 red wins, else yellow wins.

private function showWinnerDialog():void
{
	var dialog:WinnerDialog = new WinnerDialog();
	var winner:String = (currentPlayer == 1)? "red" : "yellow"
	dialog.txtWinner.text = winner + " wins!!!";
	dialog.x = (this.width - dialog.width)/2;
	dialog.y = 100;
	this.addChild(dialog);
}

Step 18: Create the Document Class

Create a new ActionScript file and save it with the name "Application".

In this class we add an instance of the Connect4 Class we wrote to the display list. Don't forget the constructor arguments.

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

	public class Application extends MovieClip
	{
		public function Application():void
		{
			var connect4:Connect4 = new Connect4(7,6);
			connect4.x = (stage.stageWidth - connect4.width)/2;
			connect4.y = (stage.stageHeight - connect4.height)/2 + 50;
			this.addChild(connect4);
		}
	}
}

Finally, click on the stage and set the Document Class to "Application".

Conclusion

You just learned how to create a connect 4 game and how multiple dimensions can make life a lot easier! I hope you enjoyed the tutorial and thank you for reading.

]]>
http://active.tutsplus.com/tutorials/games/build-your-own-oop-connect-4-game/feed/ 25
Conquer the Universe With ActionScript 3.0 and the Flixel Game Engine: Part 1 http://active.tutsplus.com/tutorials/screencasts/conquer-the-universe-with-actionscript-3-0-and-the-flixel-game-engine-part-1/ http://active.tutsplus.com/tutorials/screencasts/conquer-the-universe-with-actionscript-3-0-and-the-flixel-game-engine-part-1/#comments Fri, 20 Nov 2009 12:00:11 +0000 Shane Johnson http://active.tutsplus.com/?p=2530 ]]> The Flixel game engine is an opensource ActionScript 3.0 framework written by Adam Atomic. Its aim is to take the donkey work out of building AS3 games, leaving you with all the fun stuff and saving heaps of time!

In this, the first in a series of two screencasts, Shane Johnson takes you through the basics of Flixel, explaining how to build an awesome shoot ‘em up game from scratch.

View Screencast: Section One

Introduction

Get the Flash Player to see this player.

View Screencast: Section Two

Begin Coding

Get the Flash Player to see this player.

View Screencast: Section Three

Moving on; Sprites and Sounds

Get the Flash Player to see this player.

View Screencast: Section Four

Shooting, Killing and Compiling

Get the Flash Player to see this player.

]]>
http://active.tutsplus.com/tutorials/screencasts/conquer-the-universe-with-actionscript-3-0-and-the-flixel-game-engine-part-1/feed/ 20
Add Depth to Your Game with Parallax Scrolling http://active.tutsplus.com/tutorials/games/add-depth-to-your-game-with-parallax-scrolling/ http://active.tutsplus.com/tutorials/games/add-depth-to-your-game-with-parallax-scrolling/#comments Wed, 21 Oct 2009 12:00:17 +0000 Michael Williams http://flash.tutsplus.com/?p=2134 ]]> This is a really simple technique which you can use to add the illusion of depth to any side-scroller game. Whilst I’m explaining how it’s achieved, we’ll also take a look at making infinitely repeating backgrounds.

Introduction

You can see the basic parallax scrolling effect at work in the demo. By adjusting the speeds at which certain objects scroll, we can change how near or far away they appear to be.

This tutorial will explain how to code the effect, as well as how to make the game’s camera appear to follow the car. Lastly, it’ll explain how to create infinitely repeating backgrounds, just like in Scooby Doo.

Step 1: The Setup

If you’re using Flash, create a new ActionScript 3.0 Flash file. Set the size of the stage to be whatever you like; I’ve chosen the default 550 by 400 pixels.

If you’re not using the Flash IDE, don’t worry; just create a new AS3 project. The zip file contains a SWC with all my graphics, so you can use those just by adding it to your library. Skip all the drawing steps if you do so.

If you are using the Flash IDE but you don’t want to draw anything, the zip also contains a FLA file containing all my MovieClips :)

Step 2: Draw a Car

Create a new MovieClip symbol and draw a car. You can animate it if you like. Here’s mine:

Car symbol

Try to centre the car so that its registration point (the little cross) is about half-way along. This will make it easier for the camera to follow it later.

Step 3: Export Your Car for ActionScript

Right-click your car symbol in the Library and select Properties:

Car properties

Give your car a Class (Car will do) and check the Export for ActionScript box (this lets us access the car using code). Also, check the Export in first frame box (otherwise, we’ll have to make a preloader).

Step 4: Draw a Road

Create another new symbol, but this time draw a road:

Road symbol

Make it wider than the stage, but unlike the car, align the registration point with the left edge of the road. This will help later on, when we need to turn it into a repeating pattern.

Just like you did for the car, give the road a Class, export it for ActionScript and export it in the first frame.

Step 5: Create the Document Class

Create a new AS file and paste the following code into it:

package
{
	import flash.display.MovieClip;
	public class ParallaxDemo extends MovieClip
	{
		public function ParallaxDemo()
		{

		}
	}
}

Save this as ParallaxDemo.as, in the same folder as your FLA (or as your project, if you’re not using the IDE).

If you’re using the IDE, be sure to set this as your document class in the Properties Panel of your document:

Setting the Document Class

Step 6: Set Up the Car and the Road

Create new instances of your car and road in the AS file: (lines 6, 7, 11, 12)

package
{
	import flash.display.MovieClip;
	public class ParallaxDemo extends MovieClip
	{
		public var car:Car;
		public var road:Road;

		public function ParallaxDemo()
		{
			car = new Car();
			road = new Road();
		}
	}
}

Step 7: Position the Car and the Road

If you’re using my graphics, you can just copy the following code (lines 14-17:

package
{
	import flash.display.MovieClip;
	public class ParallaxDemo extends MovieClip
	{
		public var car:Car;
		public var road:Road;

		public function ParallaxDemo()
		{
			car = new Car();
			road = new Road();

			car.x = 275.0;
			car.y = 235.0;
			road.x = 0.0;
			road.y = 294.0;
		}
	}
}

Otherwise, you’ll need to figure out where your car and road should be placed at the start. Create a new Layer in your FLA’s timeline, then make it into a Guide Layer by right-clicking it and selecting Guide. Flash will ignore anything you do in this layer when it creates a SWF, so drag your car and road symbols here.

Make sure the left edge of your road is aligned with the left edge of the stage, and that the car is roughly in the centre (horizontally). Then adjust them so that they fit together:

Positioning the car and the road

Now take my code from above and adjust it to match the x- and y- coordinates of your car and road. By clicking on your car or road, you’ll be able to see these values in the Properties panel.

Step 8: Add your Symbols to the Stage

If you test your movie now, you’ll see nothing. We need to addChild() the car and road to the stage:

public function ParallaxDemo()
{
	car = new Car();
	road = new Road();

	car.x = 275.0;
	car.y = 235.0;
	road.x = 0.0;
	road.y = 294.0;

	stage.addChild( road );
	stage.addChild( car );
}

(Make sure you add the road first, or it’ll cover the car!)

Test your movie now, and it should look like this:

Great! Well, OK, it’s nothing spectacular yet. But now the setup’s out of the way, we can make this more interesting. For starters, let’s get this car moving…

Step 9: Add an ENTER_FRAME Event Listener

Alter your document class to add an event listener to be triggered on each frame: (lines 4, 23, 26-29)

package
{
	import flash.display.MovieClip;
	import flash.events.Event;
	public class ParallaxDemo extends MovieClip
	{
		public var car:Car;
		public var road:Road;

		public function ParallaxDemo()
		{
			car = new Car();
			road = new Road();

			car.x = 275.0;
			car.y = 235.0;
			road.x = 0.0;
			road.y = 294.0;

			stage.addChild( road );
			stage.addChild( car );

			addEventListener( Event.ENTER_FRAME, onEnterFrame );
		}

		public function onEnterFrame( evt:Event ):void
		{

		}
	}
}

If you kept the default frame rate of 12fps, the onEnterFrame() function will be called every 1/12th of a second.

Step 10: Move That Car!

If we keep increasing the car’s x-position…

public function onEnterFrame( evt:Event ):void
{
	car.x = car.x + 10;
	//if you're up for a challenge, try adding basic
	//keyboard controls to let the player accelerate
	//and decelerate.
}

…we can make the car move forwards…

Exit, stage right

…right off the edge of the screen!

Step 11: Follow That Car!

This is hardly ideal; after a few seconds we can’t even see the car any more. So let’s make the “camera” appear to follow the car.

What exactly does this mean? Well, basically we need the car to stay in the same place, while the road appears to move backwards.

That means we could do something like this:

public function onEnterFrame( evt:Event ):void
{
	road.x = road.x - 10;
}

…but this would just complicate things later. For example, imagine if we wanted to add other cars to the road, or powerups, or oil slicks, or anything at all; we’d have to move every single one of them backwards in the onEnterFrame() function.

No, there is a much simpler technique we can use. Instead of addChild()-ing the car and road to the stage, we create a new object, addChild() them to that, and then move this object backwards in the onEnterFrame() function.

It sounds more complicated than it is. Let me show you the actual code:

package
{
	import flash.display.MovieClip;
	import flash.events.Event;
	public class ParallaxDemo extends MovieClip
	{
		public var car:Car;
		public var road:Road;
		public var roadContainer:MovieClip;

		public function ParallaxDemo()
		{
			car = new Car();
			road = new Road();

			car.x = 275.0;
			car.y = 235.0;
			road.x = 0.0;
			road.y = 294.0;

			roadContainer = new MovieClip();
			roadContainer.addChild( road );
			roadContainer.addChild( car );

			stage.addChild( roadContainer );
			addEventListener( Event.ENTER_FRAME, onEnterFrame );
		}

		public function onEnterFrame( evt:Event ):void
		{
			car.x = car.x + 10;
			roadContainer.x = roadContainer.x - 10;
		}
	}
}

In lines 9 and 21 we create a new, blank MovieClip called roadContainer. Flash automatically sets its x and y values to 0.

In lines 22 and 23 we add the road and the car to the roadContainer, instead of the stage. In line 25 we add the roadContainer itself to the stage – and since the car and road are now added to the roadContainer, this lets us see them on the stage.

Line 32 is the most important part. Here, we move the roadContainer backwards by the same amount that we just moved the car forwards. This means that everything inside the roadContainer gets moved left by 10 pixels, but since the car has just been moved 10 pixels to the right, it stays in the centre of the screen.

It’s a bit like running up the down escalator. If you walk up at the same speed that it’s moving down, then to a person standing on the stairs next to you, you don’t seem to be moving.

The overall effect:

It's the end of the world as we know it, and I feel fine.

The car stays in the centre! Great. Well, great apart from the gaping white hole. But we’ll get to that. Now if you want to add more cars to the road, all you have to do is addChild() them to the roadContainer.

Step 12: Improve the Following

The trouble with just moving the whole container backwards a little bit every frame is that it’s not very flexible. What if the player uses a powerup to teleport the car 100 pixels forwards instead of 10? What if we want to make the camera centre on a different car?

When the SWF first loads, the car’s x-position is 275, and the roadContainer’s x-position is 0. How do they each change over time?

  • Start: car.x is 275, roadContainer.x is 0
  • Frame 1: car.x is 285, roadContainer.x is -10
  • Frame 2: car.x is 295, roadContainer.x is -20
  • Frame 3: car.x is 305, roadContainer.x is -30

Do you see a general rule connecting the two? If not, check this out:

  • Start: car.x is 275, roadContainer.x is 275 – 275
  • Frame 1: car.x is 285, roadContainer.x is 275 – 285
  • Frame 2: car.x is 295, roadContainer.x is 275 – 295
  • Frame 3: car.x is 305, roadContainer.x is 275 – 305

The connection is a little more obvious now! Let’s put it into code:

public function onEnterFrame( evt:Event ):void
{
	car.x = car.x + 10;
	roadContainer.x = 275 - car.x;
}

You can do what you like with the car now. Speed it up, teleport it forwards a random number of pixels, stop it moving – whatever! The camera will still follow it.

And if you want to make the camera follow a different object, just replace car.x with otherObject.x in the line we just changed.

Step 13: Extend the Road

Time to fix the endless white void of nothingness at the end of the road.

The simplest way to make the road longer is just to add another instance of the road symbol to the right of our existing symbol, like so: (lines 9, 22, 23, 27)

package
{
	import flash.display.MovieClip;
	import flash.events.Event;
	public class ParallaxDemo extends MovieClip
	{
		public var car:Car;
		public var road:Road;
		public var road2:Road;
		public var roadContainer:MovieClip;

		public function ParallaxDemo()
		{
			car = new Car();
			road = new Road();
			road2 = new Road();

			car.x = 275.0;
			car.y = 235.0;
			road.x = 0.0;
			road.y = 294.0;
			road2.x = road.x + road.width;
			road2.y = road.y;

			roadContainer = new MovieClip();
			roadContainer.addChild( road );
			roadContainer.addChild( road2 );
			roadContainer.addChild( car );

			stage.addChild( roadContainer );
			addEventListener( Event.ENTER_FRAME, onEnterFrame );
		}

		public function onEnterFrame( evt:Event ):void
		{
			car.x = car.x + 10;
			roadContainer.x = 275 - car.x;
		}
	}
}

Here’s how mine looks when it’s run:

Road gap

Oh dear. Better fix that gap.

Step 14: Mind the Gap

(If you’re not drawing your own graphics, skip to Step 17.)

The problem is in line 22 of the above code, road2.x = road.x + road.width. The road’s width value must be slightly larger than my road actually appears to be.

Even if your road doesn’t have the same problem, it still might not fit together perfectly. So head back to your FLA and drag another Road symbol from the library to your Guide layer.

Make sure it’s got the same y-position as the first road segment, then move it along horizontally until there’s no gap:

No gap

Step 15: Tweak the Join

If the edges of your two symbols don’t quite join together neatly, double-click one of them. You’ll be able to edit it and immediately see how the changes you make affect the other:

Use this trick to adjust the edges of the symbol so that the join is clean.

Step 16: Work Out the Breadth

Instead of using road.width to figure out where the second road segment should be placed, we’ll use a number I call the breadth.

To find this number for your road, just take the x-position of your right-most road symbol (in your Guide layer), and subtract the x-position of your left-most road symbol.

All you’re doing here is figuring out how many pixels apart your two road segments have to be in order to get the same perfect join you just created in Flash.

Step 17: Add a Breadth Variable

Create a new Number variable, roadBreadth, and set its value to the number you worked out in the previous step: (If you’re using my graphics, that number is 653.7.)

public class ParallaxDemo extends MovieClip
{
	public var car:Car;
	public var road:Road;
	public var road2:Road;
	public var roadContainer:MovieClip;
	public var roadBreadth:Number;

	public function ParallaxDemo()
	{
		car = new Car();
		road = new Road();
		road2 = new Road();
		roadBreadth = 653.7;

Step 18: Replace Width with Breadth

Replace the line:

road2.x = road.x + road.width;

with:

road2.x = road.x + roadBreadth;

Now test it out. There should be no gap:

Look Ma, no gap!

Great! We still run into the endless white void, though…

Step 19: Make the Background Repeat Infinitely

We could create a road3 and a road4 and a road5 and so on, positioning them each to the right of the one before them, but no matter how many segments we created, the car would reach the end of them eventually.

For a better solution, think back to when you were a child. Did you ever play that game where you pretend the floor is made of lava, but you have to get to the other end of the room somehow? (If not, heck, go play it now, it’s great fun.)

I don’t know about you, but in my house, sofa cushions were considered to be lava-resistant, able to be used as stepping-stones. We only had a couple, which wasn’t enough to reach the end of the room – but eventually I figured out how to make them reach further.

I’d lay the two cushions down to make a short path, and walk over to the second one. Then, I’d pick up the one behind me, drop it in front of me, and step across to it. By repeatedly picking up the one behind me and moving it in front of me, I could get to anywhere I pleased.

We can use the same technique to make the road last forever, without having to use more than two segments. All we have to do is detect when a road segment is “behind” the camera, and move it in front of it.

What do I mean by “behind” the camera? I mean that the right-hand edge of the segment is off the left-hand edge of the stage. We can use this if-statement to check that:

if ( road.x + roadBreadth + roadContainer.x < 0 )
{
	//road is behind the camera
}

Curious as to why this works? If not, skip to the next step. Otherwise, let me explain:

  • road.x is how many pixels to the right the left-hand edge of road is from the left-hand edge of roadContainer
  • road.x + roadBreadth is how many pixels to the right the right-hand edge of road is from the left-hand edge of roadContainer
  • roadContainer.x is how many pixels to the right the left-hand edge of roadContainer is from the left-hand edge of the stage (since roadContainer is constantly moving to the left, this will usually be negative)
  • So, ( road.x + roadBreadth + roadContainer.x ) is how many pixels to the right the right-hand edge of road is from the left-hand edge of the stage.

Phew! OK, I'll admit, that's pretty confusing. If you'd like a deeper explanation, feel free to ask in the comments :)

Step 20: Move the Road in Front of the Camera

Now that we can tell when the road segment is behind the camera, we need to move it in front of the camera again.

If we moved the road to the right by roadBreadth number of pixels, it would be in the exact same place as the other road segment. So, we need to move it to the right by twice that amount:

if ( road.x + roadBreadth + roadContainer.x < 0 )
{
	road.x = road.x + (2 * roadBreadth);
}

Put that in your onEnterFrame() function, and test it out:

As you can see, one road segment is repeating, but the other isn't yet.

Step 21: Move the Other Road Segment

We can just copy the above code for our other road segment, road2:

public function onEnterFrame( evt:Event ):void
{
	car.x = car.x + 25;
	roadContainer.x = 275 - car.x;
	if ( road.x + roadBreadth + roadContainer.x < 0 )
	{
		road.x = road.x + (2 * roadBreadth);
	}
	if ( road2.x + roadBreadth + roadContainer.x < 0 )
	{
		road2.x = road2.x + (2 * roadBreadth);
	}
}

Test it out again:

Fantastic! An infinitely looping, side-scrolling background :) Now to create the actual parallax effect...

Step 22: Create Rolling Hills

(Skip this step if you're using my graphics.)

We're going to need a repeating background to show off the parallax scrolling. I've chosen hills, but you could make buildings, forests, alien sculptures - anything you like!

There are a few tricks you can use to make what you draw look like it's further away than the car:

  1. Use duller colours (for instance, a darker shade of green for your grass)
  2. Draw less detail (no individual tufts of grass)
  3. Add a "blurred" effect to the edges (because the camera is focused on the car)

Follow the same basic steps we used for drawing the road:

  • Make the symbol wider than the stage
  • Align the symbol so that the registration point is at the left-hand edge
  • Give it a class name, export it for ActionScript, and export it in the first frame
  • Tweak the join to make sure two symbols fit together neatly
  • Figure out the "breadth" of the symbol

Here's mine:

Green green hills

Step 23: Code the Hills

The code regarding the hills is almost exactly the same as the code we just wrote regarding the roads. Have a go at writing it yourself. I've pasted my AS file with all the new additions below, so you can refer to it if you like:

package
{
	import flash.display.MovieClip;
	import flash.events.Event;
	public class ParallaxDemo extends MovieClip
	{
		public var car:Car;
		public var road:Road;
		public var road2:Road;
		public var roadContainer:MovieClip;
		public var roadBreadth:Number;
		public var hills:Hills;
		public var hills2:Hills;
		public var hillsBreadth:Number;
		public var hillsContainer:MovieClip;

		public function ParallaxDemo()
		{
			car = new Car();
			road = new Road();
			road2 = new Road();
			roadBreadth = 653.7;

			hills = new Hills();
			hills2 = new Hills();
			hillsBreadth = 890.5;

			car.x = 275.0;
			car.y = 235.0;
			road.x = 0.0;
			road.y = 294.0;
			road2.x = road.x + roadBreadth;
			road2.y = road.y;

			hills.x = 0;
			hills.y = 14.5;
			hills2.x = hills.x + hillsBreadth;
			hills2.y = hills.y;

			roadContainer = new MovieClip();
			roadContainer.addChild( road );
			roadContainer.addChild( road2 );
			roadContainer.addChild( car );

			hillsContainer = new MovieClip();
			hillsContainer.addChild( hills );
			hillsContainer.addChild( hills2 );

			stage.addChild( hillsContainer );
			stage.addChild( roadContainer );
			addEventListener( Event.ENTER_FRAME, onEnterFrame );
		}

		public function onEnterFrame( evt:Event ):void
		{
			car.x = car.x + 10;
			roadContainer.x = 275 - car.x;
			if ( road.x + roadBreadth + roadContainer.x < 0 )
			{
				road.x = road.x + (2 * roadBreadth);
			}
			if ( road2.x + roadBreadth + roadContainer.x < 0 )
			{
				road2.x = road2.x + (2 * roadBreadth);
			}

			hillsContainer.x = 275 - car.x;
			if ( hills.x + hillsBreadth + hillsContainer.x < 0 )
			{
				hills.x = hills.x + (2 * hillsBreadth);
			}
			if ( hills2.x + hillsBreadth + hillsContainer.x < 0 )
			{
				hills2.x = hills2.x + (2 * hillsBreadth);
			}
		}
	}
}

(The new lines are 12-15, 24-26, 35-38, 45-47, 49, and 67-75. How did you do?)

Here is the result:

You might be wondering why I've bothered creating a hillsContainer. If so, then nicely spotted! We could just addChild() the hills to the roadContainer - but creating a new container for the background is what lets us create the actual parallax effect.

Step 24: The Actual Parallax Effect

The effect only requires changing one line of code:

hillsContainer.x = 275 - car.x;

into this:

hillsContainer.x = (275 - car.x) * 1/5;

This makes the hills scroll at 1/5th the speed of the road and car.

It looks like this:

You don't have to use 1/5; make this value larger or smaller until the speed feels right to you.

Why does this work? Well, remember that we see things in a cone of vision; the further away something is the more of it we can see. So if we walk past two objects of the same size, but one is further away, the closer of the two will appear to move faster, like so:

Let's add another background layer, even further away than the hills.

Step 25: Create Mountains

This is exactly the same as creating the road and the hills, so I'm not even going to paste the code this time! All I'll do is post a picture of my mountains..

Blue moutains

..tell you that my mountains' breadth is 751.5, x is 0 and y is 63.0; remind you to create a new mountainContainer MovieClip; and let you know that my mountains scroll at 1/16th the speed of my road.

Oh, and show you the result:

Step 26: Create the Sky

The sky is nice and easy. Since it's really far away, it scrolls so slowly that it looks like it's barely scrolling at all. Clouds and birds move, of course, and the Sun rises and sets, but none of this is due to any parallax scrolling effect. This means we don't have to make anything in the sky scroll!

(The exception to this is if the camera is travelling really, really fast - like, the speed of an aeroplane or rocket. Even then, be sure to make it scroll very slowly.)

So, no need to worry about breadth here, or creating an infinitely repeating image. It's still a good idea to make a skyContainer, though, just to keep things consistent. My sky is just a blue rectangle:

Sky so blue

If you place it at x=0, y=0 it'll cover the whole stage. Here's what it looks like in the SWF:

Step 27: Create a Big Tree in the Foreground

We've created a lot of background objects, but nothing closer to the camera than the car. As I'm sure you realise, such an object would have to scroll faster than the roadContainer, so let's try this.

For my foreground object, I've drawn a tree:

Big tree

The tree's a little different from the other objects we've made so far because it's not made to loop - it stands alone, it won't join to another tree standing next to it. This means that we only ever need one tree on screen at any one time (especially since it's so big!)

So we only need one Tree object in the code as well. Write the code for this object. If you're using my graphics, the starting x-position will be 780.0 and the y-position will be 175.0.

Since the tree will scroll, we still need a treeContainer, and we still need a treeBreadth. However, this time, the treeBreadth just controls the number of pixels between each tree. I've used a nice round 1000.0 for mine.

Step 28: Scroll the Tree

Since there's only one tree, the scrolling code is much simpler:

treeContainer.x = (275 - car.x) * 3;
if ( tree.x + treeBreadth + treeContainer.x < 0 )
{
	tree.x = tree.x + (2 * treeBreadth);
}

Nothing complicated :) Just note that it scrolls three times faster than the road. Here is the final result:

Congratulations! You've created a dynamically scrolling camera, infinitely repeating backgrounds, and a pseudo-3D parallax effect :)

Further Ideas to Try

Here are few more things you can do with the same code:

If you're creating a shoot-'em-up and you'd like all your explosions to appear closer to the camera than your enemies, simply create a new explosionsContainer, addChild() any explosions to that, and make it scroll at the same speed as the enemiesContainer.

Put the player's score, their lives counter, the mute and pause buttons, and any other parts of your game's interface into a single container. Place this container in front of all other containers, but don't make it scroll. This is an easy way to keep a game's camera and assets separate from its interface.

Try having one container stay still while making the containers in front of it and behind it scroll in opposite directions. This creates a cool rotation effect, as seen about five minutes into this clip from Disney's Snow White!

Conclusion

Thanks for reading this tutorial; I hope you enjoyed it. If anything was unclear at all, or if you'd like to ask any questions about the effect, please post a comment below.

Speaking of comments, if you create anything using this tutorial, I'd love it if you posted a link so I could see it :)

]]>
http://active.tutsplus.com/tutorials/games/add-depth-to-your-game-with-parallax-scrolling/feed/ 45
Improve Your Memory With an Away3D Game http://active.tutsplus.com/tutorials/games/improve-your-memory-with-an-away3d-game/ http://active.tutsplus.com/tutorials/games/improve-your-memory-with-an-away3d-game/#comments Fri, 25 Sep 2009 12:00:00 +0000 Yagiz Gurgul http://flash.tutsplus.com/?p=1909 ]]> In this tutorial we’ll build a 3D memory game. Along the way we’ll look at adding variables and mouseEvents to Away3D objects. Let’s get started..

Step 1: ActionScript File

Create a new ActionScript 3.0 file.

Step 2: Edit Profile

In the Properties panel press the Edit button.

Step 3: Choose Flash Player Version

Choose the Flash Player 9 profile and click OK.

Step 4: Get the Away3D Engine!

Save your file as "Memory Game.fla" in the Memory Game Tutorial folder. Now download the Away3D engine from Away3D Downloads. We’ll use version 2.3.3 for Flash Player 9. Unpack the archive and copy all folders into the Memory Game Tutorial folder.

Step 5: Get the Tweener!

Download Tweener from here. Unpack the class files to your Memory Game Tutorial folder so it all looks like this:

Step 6: Import Textures

We’ll use 5 different cards for this game (you can find them in the source files). Import them into Flash by going File > Import > Import to Library.

Step 7: Export Textures for ActionScript

To use these textures in runtime we need to attach a Class name to them. Select the images one by one and go Right-Click > Properties > Export for ActionScript. Just remove the ".png" parts of their names.

Step 8: Start Coding

After all that we’re ready to start coding. Let’s fill our first lines by importing the classes:

import away3d.cameras.*;
import away3d.containers.*;
import away3d.materials.*;
import away3d.primitives.Plane
import away3d.primitives.Cube
import away3d.containers.ObjectContainer3D;
import away3d.core.math.Number3D;
import caurina.transitions.*
 

Step 9: Setup Variables

After importing our classes we should define our variables to use in the following steps.

var scene:Scene3D;
var camera:Camera3D;
var view:View3D;
var totalchildren:int=10
var cards:Array
var textures:Array = [ new texture0(0,0),
new texture1(0,0),
new texture2(0,0),
new texture3(0,0),
new texture4(0,0)]
var backtexture:BitmapData = new textureback(0,0)
var woodtexture:BitmapData = new texturewood(0,0)
var cardwidth:Number = 110
var cardheight:Number = 150
var xoffset:Number = 10
var yoffset:Number = 10
var cardsholder:ObjectContainer3D
var selectedCard1:Plane
var selectedCard2:Plane
var disableMouseEvents:Boolean=false

The textures array holds our texture images. To attach images to our stage from the library we use this method:
var imageData:BitmapData = LibraryLinkageName(0,0). We use the same approach for our table and back face of the cards. xoffset and yoffset define the distance between cards.

Step 10: Setup Away3D

First of all we need to build Away3D.

function initAway3D():void {
 scene = new Scene3D();

 camera = new Camera3D();
 camera.y = 700
 camera.z = 500
 camera.lookAt(new Number3D(0,0,0))

 view = new View3D({scene:scene, camera:camera});
 view.x = stage.stageWidth/2
 view.y = stage.stageHeight/2
 addChild(view);
 }

the first line in our function creates the 3D scene. We’ll add 3D objects into it. After that we create the camera. We’ll move it backward and upward a bit. By doing this, we’ll able to see cards better when we’re playing the game. Then we’ll center it. Finally we create the View and set it in the middle of the scene.

Step 11: Creating the Table

In this step we’ll create the table:

function createGround():void {
 var cube:Cube = new Cube({width:680,depth:400,height:20,pushback:true,ownCanvas:true,material: new BitmapMaterial(woodtexture)})
 cube.y=-20
 scene.addChild(cube)
 }

To make it look more realistic we’re using a Cube instead of a Plane. The most important point here is to use pushback properties in Cube to make it visible under the cards. The material we use for Cube is BitmapMaterial. This is the best way to use bitmaps as textures.

Step 12: Creating One Card

First we’ll create a holder. There’ll be two planes in this holder. One of these planes is the front face of the cards and the other one is the back. We’ll use the holder to rotate or move the cards.

function createCard(texture:BitmapData,id:int):ObjectContainer3D {
	var card:ObjectContainer3D = new ObjectContainer3D()

	var front:Plane = new Plane({width:cardwidth,height:cardheight, material: new BitmapMaterial(texture,{smooth:true})})
	var back:Plane = new Plane({width:cardwidth,height:cardheight, material: new BitmapMaterial(backtexture,{smooth:true})})
	front.rotationY=180
	back.rotationZ=180
	back.rotationY=180

	back.extra = {}
	back.extra.id = id
	back.extra.targetCard = card
	back.addOnMouseDown(onBackClicked)

	card.rotationZ=180
	card.addChild(front)
	card.addChild(back)
	card.ownCanvas = true
	return card
}

In this function we are recreating what the diagram illustrates. We should use rotation to place the cards face down. We aren’t adding an event to the holder, because we only click the back face of the card. For this reason we add a mouseDown event to the back Plane only.

Every 3D object in Away3D can have extra variables and every color in our game has a unique id. We’ll add this id variable to the "extra" propertie of back Plane. We’ll use ids to check if the selected two cards are the same color or not.

Step 13: Creating All Cards

After the card creating function, we’re ready to create all of them.

function initCards():void {
 cards = new Array()
 for(var i:int = 0; i<textures.length; i++ ) {

 var card1:ObjectContainer3D = createCard(textures[i],i)
 var card2:ObjectContainer3D = createCard(textures[i],i)

 cards.push( card1 )
 cards.push( card2 )

 }
 }

We’ll push all our cards to a cards array. There’ll be two cards of each color (two blue, two red and two green). Because of this we create two cards with the same color then push them to the array.

Step 14: Randomize Cards

Next step is to randomize the cards array.

function randomizeCards():void{
 var newArray:Array = new Array();
 while(cards.length > 0){
 newArray.push(cards.splice(Math.floor(Math.random()*cards.length), 1)[0]);
 }
 cards = newArray
 }

It’s all so simple. First we are creating a new array. Then we are picking a random item from the cards array, pushing it to the new array and removing it from cards array. After the while loop has completed we equalize the cards array to our new array. Now we have a randomized array.

Step 15: Adding Cards to the Scene

Now, we’ve randomized our cards, so we can add them to the scene. We’ll use a grid system for their positions

function addCardsToScene():void {
	cardsholder = new ObjectContainer3D()
	var currentindex:int = 0

	for(var i:int=0; i<2; i++) {
		for(var b:int=0; b<5; b++) {
			cards[currentindex].x=b*(cardwidth+xoffset)+cardwidth/2
			cards[currentindex].z=i*(cardheight+yoffset)+cardheight/2
			cardsholder.addChild(cards[currentindex])
			currentindex++
		}
	}

	var cardswidth:Number = (5*cardwidth) + (4*xoffset)
	var cardsheight:Number = (2*cardheight) + (1*yoffset)

	cardsholder.x=-cardswidth/2
	cardsholder.z=-cardsheight/2

	scene.addChild(cardsholder)
}

The first “for” loop is for the x axis and the second one is for the y axis. We’re adding cards to a new main holder, so when we want to rotate or move the cards we can only use the main holder. Then we set the cards by using the grid system. For that we are using cardwidth, cardheight, xoffset and yoffset variables. The cards need to be in the middle of table. To do this we need to get width and height values of the main cards holder. This diagram shows the way we are getting them.

After we get them we move the main holder into the middle of the table.

Step 16: Mouse Down Event

We’ve added cards to scene. Our next step will be creating the mouseDown event function.

function onBackClicked(e:Event) {
	if(disableMouseEvents==false) {
		if(selectedCard1==null) {
			selectedCard1 = e.currentTarget as Plane
		}else {
			if(selectedCard2==null) {
				selectedCard2 = e.currentTarget as Plane
				waitForDecision()
				disableMouseEvents = true
			}
		}
		Tweener.addTween(e.currentTarget.extra.targetCard,{y:50,rotationZ:0,time:1})
	}
}

First we check disableMouseEvents. That means if we have permission to click cards we continue but if we don’t nothing happens. If the first card isn’t selected, clicked card is our first card. If first card isn’t null then this clicked card is our second card.

Our game must make a desicion after we select the two cards as to whether they’re same or not. For this reason our “waitForDecision” function is running and we set disableMouseEvents to true. So while the game is waiting for a decision, nothing will happen if we click a card.

The rotationZ property of our clicked card will be 180 degree with Tweener, so we can see the color of the card.

Step 17: Wait for a Decision

When the two cards are selected, the game waits a little (this is just for fun).

function waitForDecision():void {
	var timer:Timer = new Timer(1000,1)
	timer.addEventListener(TimerEvent.TIMER,makeDecision)
	timer.start()
}

As you can see, this is a simple Timer usege. It waits 1000 miliseconds (1 second). After that, TimerEvent triggers the makeDecision function to run.

Step 18: Make a Decision

We waited 1 second so now it’s time to make a decision. If the id values of the cards are the same they will disappear, if not they will turn face down again

function makeDecision(e:Event):void {
	if(selectedCard1.extra.id == selectedCard2.extra.id) {
		Tweener.addTween(selectedCard1.extra.targetCard,{alpha:0,time:0.2,onComplete:removeCard,onCompleteParams:[selectedCard1.extra.targetCard]})
		Tweener.addTween(selectedCard2.extra.targetCard,{alpha:0,time:0.2,onComplete:removeCard,onCompleteParams:[selectedCard2.extra.targetCard]})
	}else {
		Tweener.addTween(selectedCard1.extra.targetCard,{y:0,rotationZ:180,time:1})
		Tweener.addTween(selectedCard2.extra.targetCard,{y:0,rotationZ:180,time:1})
	}
	disableMouseEvents = false
	selectedCard1 = null
	selectedCard2 = null
}

We are doing exactly that in this function. We are checking the id values of two selected cards. If they are same, the alpha values of them will change 0 with Tweener (we make them disappear). When this tween is finished , the removeCard function is called. The parameter of the removeCard function is the cards themselves. We do this to the two cards at the same time. If they are not the same, we send them to their old positions and make them face down. Whatever the desicion is, selectedCard1 and selectedCard2 will be set to null.

Step 19: Removing Cards

We need to remove the two cards from our main cards holder when they disappear, because we don’t need them anymore.

function removeCard(e:ObjectContainer3D):void {
	cardsholder.removeChild(e)
	totalchildren--
	if(totalchildren==0) {
		trace("WIN")
	}

}

After they are kicked out of the scene, the value of totalchildren decreases one by one. When it reaches 0 this means you’ve won the game!

Step 20: Rendering

The final step is to write a loop function to render Away3D in runtime.

function startToRender():void {
   addEventListener(Event.ENTER_FRAME, render);
   }
function render( e:Event ):void{
   view.render();
}

Step 21: Call All Functions

We’re ready to call all the functions we’ve written.

initAway3D()
createGround()
initCards()
randomizeCards()
addCardsToScene()
startToRender()

Now test it and play your game :)

Conclusion

In this lesson we learned how to add variables and mouseEvents to Away3D objects. With these skills we made a game and as you can see it wasn’t so hard :)

I hope you liked this tutorial, thanks for reading!

]]>
http://active.tutsplus.com/tutorials/games/improve-your-memory-with-an-away3d-game/feed/ 13
Build a Mini Golf Game with ActionScript 3.0 http://active.tutsplus.com/tutorials/games/build-a-mini-golf-game-with-actionscript-3-0/ http://active.tutsplus.com/tutorials/games/build-a-mini-golf-game-with-actionscript-3-0/#comments Wed, 16 Sep 2009 12:00:48 +0000 George Loffelmann http://flash.tutsplus.com/?p=1820 ]]> In this tutorial I´ll show you how to create nice Mini Golf Game with ActionScript 3.0. We´ll work with Event Handlers, Functions and also the Timer Object which is a really cool feature in AS3.

Introduction

You’ll find there’s a lot of drawing involved with this project. If you’d rather not do all that yourself, just download the file programmeGolf.fla and skip to Step 19 where the programming part begins.

Step 1: New File

Let´s start by opening a new ActionScript 3.0 Flash File.

Step 2: Document Settings

Change the dimensions in the properties panel (600px by 400px) and set the frame rate to 30 fps.

Step 3: Creating the Background

Now we´ll prepare some graphics. Create a rectangle (600px by 400px) and place it to coordinates 0, 0. Fill the rectangle with a gradient (#0066FF to #FFFFFF).

Select the object and press F8. Choose Movie Clip and name it “background”.

Step 4: Adding Clouds and Trees

Go into the background object (double-click on it) and add some graphics. I added clouds and trees by just picking the brush tool and drawing some simple shapes. Do this on a new layer to keep things organized. For the clouds I used a light blue color (#8FBBE8), for trees a light violet color (#8DA5C7). Finally I cropped the clouds and trees along my stage.

Clouds

Trees

Final result

Step 5: Ground

On the main scene create a rectangle (600px by 50px) and place it to coordinates 0, 350. Fill the rectangle with a gradient (#543201 to #985B01).

Select the object and press F8. Choose Movie Clip and name it “ground”. Change its registration to the left and export object for ActionScript. Click OK.

Step 6: Grass

Go into the ground object (double-click on it) and create a new layer. Pick the Rectangle Primitive Tool (R), draw a shape (10px by 20px) and place it at coordinates 0, -35. Set the corner radius to 4px.

Copy these rectangles across the entire scene length. Now select all of them, make them editable (Ctrl + B) and fill them with a gradient (#009900 to #35FD35).

Step 7: Hole

Now we’ll create a shape which will mask our ground and grass. Color therefore doesn´t matter. Create a rectangle (900px by 70px) and place it at coordinates -300, -40.

Create a “hole” shape (20px by 40px) using the Oval and the Rectangle Tool then place it at coordinates 300, -40. Now make sure that your hole shape is on the same layer with our blue rectangle created before. If so, select it and delete. You should get the final shape of the mask.

Press F8 and convert it into a Movie Clip. Change the registration point to the left and name it “grassMask”. Once your Movie Clip is created, give it an instance name “grassMask”.

Now we should set the masking. Double-click on the layer where your ground object is and select “masked” option. Do the same with the grass layer. In the layer containing your “grassMask” object choose the “mask” option. Now you can leave the “ground” object and go back to the main stage.

Step 8: Flag

Select the Brush Tool (B) and draw a stake object (#C25C00). Now pick the Rectangle Tool (R) and draw a Flag object (#FFDCDC). Select the both objects and press F8. Choose Movie Clip and name it “flag”. Change the registration point and export it for ActionScript.

Step 9: Character

Now the worst part begins :) drawing a character. I usually draw something by hand and then redraw it in Flash, so let´s use my hand-drawn scanned picture. Download it here and import it into the library (File > Import > Import to Library …). Create a new Movie Clip (Ctrl + F8) and name it “bearMC”. Drag the bear from the library onto the stage, lock the layer containing the bitmap and create a new layer. We need to separate body parts so each part will be in another layer.

Step 10: Body parts

Let´s start with a head. Pick the Brush Tool (B). Select the largest circle shape and brown color (#432801). Start drawing the outlines of the head. Finally, you can pick the Paint Bucket Tool (K) and fill the head with colors. Good isn´t it? :) Do the same thing with the arms, body, legs and golf stick. Don´t forget to place every part into separate layers.

Step 11: Adjusting Body Parts

Now we should make some changes. First of all delete the layer with your bear bitmap (we should also delete the bear bitmap from the library). Now select the whole character throughout all layers. Because we’ll load the character dynamically using ActionScript, we need to have the correct scale. Pick the Free Transform Tool (Q) and make the bear smaller (about 80px wide).

Step 12: Converting Body Parts into Movie Clips

Click on the first frame of your head layer. The whole head should be now selected. Press F8 and name it “head”. Export it for ActionScript as well. Do exactly the same thing with body (“body”), arms (“arms”) and stick (“stick”).

Step 13: Adjusting Pivot Points

Every object in Flash rotates around a pivot point, so we have to adjust the pivot points of our stick and arms. Hide your head and body layer and select the stick and arms layer. You should see the pivot points of our objects.

Create some sort of mark on a separate layer where the pivots should really be (between the shoulders) to allow proper rotation.

Now take one of the objects and drag it onto the mark (pivot point should be on the mark). Do the same with the second object.

Now unhide all layers and delete the mark we made before. Go into the arms object (double-click on it) so you can place the arms in the proper position.

Leave the arms object and do the same thing with the stick.

Step 14: Positioning the Bear

Go to the main scene and create a new layer, name it “bear”. Take “bearMC” from the library and drag it onto the main stage. With our “bearMC” selected press Ctrl+B. Now you can delete “bearMC” from the library.

Step 15: Apple

Press Ctrl+F8 and create new Movie Clip. Name it “apple” and export it for ActionScript. Use the Brush Tool (B) and draw an apple shape. You can also add leaf and a stalk if you want. Now select the whole object and scale it down using the Free Transform Tool (Q), a maximum of 20px wide.

Step 16: Apple Animation

Select the whole object again and press F8. Name it “appleFall”. The registration point should be in the middle. Give it an instance name “appleFall” in the properties panel.

Go into the “appleFall” object (double-click on it). Create a new layer and name it “action”. Go to frame 7 and create a new keyframe (F6). Then go to the actions panel (F9) and type:

stop();

Select the first frame and do the same thing.

Now add keyframes (F6) into the second, fourth, sixth and seventh frames of the “apple” layer. Select the second to sixth frame and create a motion tween (Rightclick > Create Motion Tween).

Now position apples in the keyframes roughly at the positions shown on the picture below.

If everything is OK you can go back to the main scene and drag an “apple” object from the library to the stage.

Step 17: Text field

Create a new layer and name it “messages”. Pick a Text Tool (T) and create a new text field in the middle of the stage (I chose a blue Arial font). Set the field type to Dynamic Text and assign an instance name “myMessage”. You can lock the layer now.

Step 18: Score Counter

Create a new layer and name it “counter”. Again drag an apple object from the library onto the stage and position it at 30, 30. Press Ctrl + B twice (because we have two movie clips to break – apple and appleFall) and fill the object with a white color. Make five instances and place them in a line. Select all of them and press F8. Name it “counterBcg”. Go to the properties panel and change the color from none to alpha at 40%.

Drag an apple from the library onto the stage, position it at 30, 30 and press Ctrl + B twice again. Press F8 and name the Movie Clip “counter”. Go to this object (double-click on it) and add one more keyframe. In this keyframe fill the apple with a blue color (this apple will be displayed in the counter if you miss the hole).

Go to the actions panel of every keyframe (F9) and type:

stop();

Go to the main scene, create five instances of the apple, again forming a line. Each apple will have an instance name (“a1″, “a2″, “a3″ etc.).

Step 19: Preparing for Programming

So here we are. The drawing part is over. Take a piece of paper and note the coordinates of the head, arms, body, stick, apple, ground and flag so we can attach them dynamically.

Now you can delete them from the scene.

Here’s a quick break-down of how we’ll approach the programming:

  • Step 20 Attaching and positioning objects
    All objects are properly attached and positioned on the scene.
  • Step 21 Counter
    Counter.
  • Step 22 Programming the Stick
    Making the stick rotate.
  • Step 23 Programming the Apple
    The apple moves when the stick hits it.
  • Step 24 Hit
    If the apple’s close enough, it falls into the hole.
  • Step 25 Programming a Counter
    If it was a successful hit, the counter adds one.
  • Step 26 Creating Next Try Using Timer Object
    Once the attempt is over, we can try again after two seconds.
  • Step 27 Adding Text Messages
    We add some messages for the player.
  • Step 28 Game Over
    If the game is finished, we can play again.

Step 20: Attaching and Positioning Objects

All of the programming is placed in the first frame of the actions layer. So let´s create a new layer and name it “actions”. Firstly, we’ll add three blocks of comments to our code:

//Variables

//EventListeners

//Functions

When the comments are ready we can add following code which will attach our objects from the library to the stage.

(Additional lines 2-8, 13-23)

//Variables
var myHead = new head();
var myArms = new arms();
var myBody = new body();
var myStick = new stick();
var myGround = new ground();
var myFlag = new flag();
var myApple = new apple();

//EventListeners

//Functions
function resetScene():void {
	addChild(myHead);
	addChild(myArms);
	addChild(myBody);
	addChild(myStick);
	addChild(myGround);
	addChild(myFlag);
	addChild(myApple);
	}

resetScene();

Now we have all our objects on the scene at coordinates 0, 0. Objects are attached on the scene everytime the resetScene function is called. Let´s refer back to our piece of paper where we noted the coordinates. We’ll also use a random function for generating the x position of our hole and flag.

(Additional lines 9, 22-39)

//Variables
var myHead = new head();
var myArms = new arms();
var myBody = new body();
var myStick = new stick();
var myGround = new ground();
var myFlag = new flag();
var myApple = new apple();
var grassMask:Number;

//EventListeners

//Functions
function resetScene():void {
	addChild(myBody);
	addChild(myArms);
	addChild(myHead);
	addChild(myStick);
	addChild(myGround);
	addChild(myFlag);
	addChild(myApple);
	grassMask = (320 + Math.random()*260);
	myBody.x = 70;
	myBody.y = 296;
	myArms.x = 68;
	myArms.y = 276;
	myArms.rotation = 0;
	myHead.x = 70;
	myHead.y = 215;
	myStick.x = 68;
	myStick.y = 276;
	myStick.rotation = 0;
	myApple.x = 90;
	myApple.y = 331;
	myGround.x = 0;
	myGround.y = 375;
	myGround.grassMask.x = grassMask - 610;
	myFlag.x = grassMask;
	myFlag.y = 375;
	}

resetScene();

*grassMask: variable description

  1. Our stage is 600px long
  2. Math.random function generates numbers from 0 to 1
  3. If we multiply this value by 260 we’ll get a number between 0 and 260.
  4. If we then add 320 to this result, we’ll get the range we’re after: 320 to 580. This is the region along the x axis where we can place our hole and flag.

Step 21: Counter

All objects should be positioned properly and our mask and flag should be at a random x position. Add the following lines under the resetScene function. It will hide the apples for counting our score.

(Additional lines 6-15)


...

resetScene();

function invisibleApples():void {
	a1.visible = false;
	a2.visible = false;
	a3.visible = false;
	a4.visible = false;
	a5.visible = false;
	a6.visible = false;
	}

invisibleApples();

Step 22: Programming the Stick

Now we’ll add listeners for our keyboard. If we press any key, the onDown function will be called. If we then release any key, the onUp function will be called. We also need to add an enter frame listener.

The principle is very simple; if you press any key, the variable checker will contain the value 1. If you release the key, the variable checker will contain a value of 0. The Hit function, which shows up in every frame of our program (30 times per second in our case) will act along those values (affirmative or negative rotation). Variables speedUp and speedDown are just numbers which tell us how big the rotation will be. Variables minPower and maxPower tell us the minimum and maximum degree of stick rotation. Type following lines:

(Additional lines 12,13,18-37)

//EventListeners
stage.addEventListener(KeyboardEvent.KEY_DOWN, onDown);
stage.addEventListener(KeyboardEvent.KEY_UP, onUp);
stage.addEventListener(Event.ENTER_FRAME, hit);

//Functions

        ...

        myFlag.x = grassMask;
	myFlag.y = 375;
        checker = 0;
        minPower = 0;
	}

resetScene();

function onDown(e:KeyboardEvent):void {
	checker = 1;
	minPower = -20;
	}

function onUp(e:KeyboardEvent):void {
	checker = 0;
	}

function hit(e:Event):void {
	if((checker == 1) && (myStick.rotation < maxPower)) {
		myStick.rotation += speedUp;
		myArms.rotation += speedUp;
		} else {
			if(myStick.rotation > minPower) {
				myStick.rotation -= speedDown;
				myArms.rotation -= speedDown;
				}
			}
	}

We’re using a few new variables in the code, so we have to initialize them. Add the following lines to the top. Variables checker and minPower are declared in resetScene function because they’ll be changed during the program and we’ll need to reset them later.

(Additional lines 7-17)

//Variables

...

var myApple = new apple();
var grassMask:Number;
var speedUp:int = 2;
var speedDown:int = speedUp * 8;
var checker:int;
var minPower:int;
var maxPower:int = 110;

Step 23: Programming an Apple

We’ll use method hitTestObject to retrieve information about the collision between our stick and apple. If a collision happens, the shot function will be called.

(Additional lines 7,8,16,30-32,36-40)

//Variables

...

var minPower:int;
var maxPower:int = 110;
var strength:Number;
var applePower:Number;

//Functions

...

function onUp(e:KeyboardEvent):void {
	checker = 0;
	applePower = (myApple.x + myStick.rotation) * 3.2;
	}

...

function hit(e:Event):void {
	if((checker == 1) && (myStick.rotation < maxPower)) {
		myStick.rotation += speedUp;
		myArms.rotation += speedUp;
		} else {
			if(myStick.rotation > minPower) {
				myStick.rotation -= speedDown;
				myArms.rotation -= speedDown;
				}
			if(myStick.hitTestObject(myApple)) {
				stage.addEventListener(Event.ENTER_FRAME, shot);
				}
			}
	}

function shot(e:Event):void {
	strength = (applePower - myApple.x);
	myApple.x += strength / 30;
	myApple.rotation += strength / 4;
	}

Step 24: Hit

Shooting should work. Let´s now code an apple falling into the hole. We’ll be testing in our shot function whether the distance from the hole is small enough and the strength is low enough. Should this be the case, we can play our clip where an apple falls into the hole. My rules (number values) are very strict. You can define your own lighter rules if you prefer.

(Additional lines 17,27-34,37,38)

//Variables

...

var strength:Number;
var applePower:Number;
var distance:Number;
var target:int;

//Functions
function resetScene():void {	

        ...

	checker = 0;
	minPower = 0;
	target = 0;
	}

resetScene();

...

function shot(e:Event):void {
	strength = (applePower - myApple.x);
	distance = grassMask - myApple.x;
	if((Math.round(distance) <= 10) && (Math.round(distance) >= 3) && (strength <= 65)) {
		if(target == 0) {
			myApple.appleFall.play();
			myApple.rotation = 0;
			target = 1;
			}
		} else {
			if(strength > 5) {
				myApple.x += strength / 30;
				myApple.rotation += strength / 4;
				}
			}
	}

Here is a breakdown of how the shot function really works. It´s very simple. We’ll add some lines into it later, but this is the core.

Shot function scheme

  • Set strength variable
  • Set distance variable

If distance and strength is low and the target hasn’t yet been hit

  • Play clip with apple falling into hole
  • Set apple rotation to zero
  • Tell our variable that the target was hit

If distance and strength isn’t alongside our values and if strength is still large enough – lower than 5 and the apple stops moving

  • Apple keeps moving along the x axis
  • Apple keeps rotating

Step 25: Programming the Counter

Now we’ll make a score counter. Firstly we have to initialize a new variable called tryShot; it counts the attempts we have remaining by incrementing one every time the resetScene funcion is called. If we hit the hole an apple will appear. If we miss the hole an apple will also appear, but it will jump to the second frame of the clip (displaying a blue apple).

(Additional lines 6,7,15,16,31,37-40)

//Variables

...

var distance:Number;
var target:int;
var tryShot:int = 0;

//Functions
function resetScene():void {	

        ...

	minPower = 3;
	target = 0;
	tryShot ++;
	}

resetScene();

...

function shot(e:Event):void {
	strength = (applePower - myApple.x);
	distance = grassMask - myApple.x;
	if((Math.round(distance) <= 10) && (Math.round(distance) >= 3) && (strength <= 65)) {
		if(target == 0) {
			myApple.appleFall.play();
			myApple.rotation = 0;
			target = 1;
			this["a" + tryShot].visible = true;
			}
		} else {
			if(strength > 5) {
				myApple.x += strength / 30;
				myApple.rotation += strength / 4;
				} else {
					this["a" + tryShot].visible = true;
					this["a" + tryShot].gotoAndStop(2);
					}
			}
	}

Step 26: Creating Next Try Using Timer Object

Add the following lines into the code. They will ensure that the countdown function (which resets the scene and removes the listener for the shot function) will be called two seconds after any given shot is over (it doesn´t matter if the hole was or wasn´t hit). We also have to add a condition to our hit function which restricts the number of attempts to six.

(Additional lines 7,13,22,30,44,55,64,69-72)

//Variables

...

var target:int;
var tryShot:int = 0;
var myTimer = new Timer(2000, 1);

//EventListeners
stage.addEventListener(KeyboardEvent.KEY_DOWN, onDown);
stage.addEventListener(KeyboardEvent.KEY_UP, onUp);
stage.addEventListener(Event.ENTER_FRAME, hit);
myTimer.addEventListener(TimerEvent.TIMER, countDown);

//Functions
function resetScene():void {
;
        ...

	target = 0;
	tryShot ++;
	myApple.appleFall.gotoAndStop(1);
	}

resetScene();

...

function hit(e:Event):void {
	if(tryShot < 7) {
		if((checker == 1) && (myStick.rotation < maxPower)) {
			myStick.rotation += speedUp;
			myArms.rotation += speedUp;
			} else {
				if(myStick.rotation > minPower) {
					myStick.rotation -= speedDown;
					myArms.rotation -= speedDown;
					}
				if(myStick.hitTestObject(myApple)) {
					stage.addEventListener(Event.ENTER_FRAME, shot);
					}
				}
			}
	}	

function shot(e:Event):void {
	strength = (applePower - myApple.x);
	distance = grassMask - myApple.x;
	if((Math.round(distance) <= 10) && (Math.round(distance) >= 3) && (strength <= 65)) {
		if(target == 0) {
			myApple.appleFall.play();
			myApple.rotation = 0;
			target = 1;
			this["a" + tryShot].visible = true;
			myTimer.start();
			}
		} else {
			if(strength > 5) {
				myApple.x += strength / 30;
				myApple.rotation += strength / 4;
				} else {
					this["a" + tryShot].visible = true;
					this["a" + tryShot].gotoAndStop(2);
					myTimer.start();
					}
			}
	}

function countDown(e:TimerEvent):void {
	resetScene();
	stage.removeEventListener(Event.ENTER_FRAME, shot);
	}

Step 27: Adding Text Messages

Now we should create some text messages, to let the player know what’s going on.

(Additional lines 8,29-31,43,53)

//Functions
function resetScene():void {	

        ...

	tryShot ++;
	myApple.appleFall.gotoAndStop(1);
	myMessage.text = "Press any key!";
	}

resetScene();

...

function hit(e:Event):void {
	if(tryShot < 7) {
		if((checker == 1) && (myStick.rotation < maxPower)) {
			myStick.rotation += speedUp;
			myArms.rotation += speedUp;
			} else {
				if(myStick.rotation > minPower) {
					myStick.rotation -= speedDown;
					myArms.rotation -= speedDown;
					}
				if(myStick.hitTestObject(myApple)) {
					stage.addEventListener(Event.ENTER_FRAME, shot);
					}
				}
			} else {
				myMessage.text = "Game Over";
		}
	}

function shot(e:Event):void {
	strength = (applePower - myApple.x);
	distance = grassMask - myApple.x;
	if((Math.round(distance) <= 10) && (Math.round(distance) >= 3) && (strength <= 65)) {
		if(target == 0) {
			myApple.appleFall.play();
			myApple.rotation = 0;
			target = 1;
			this["a" + tryShot].visible = true;
			myMessage.text = "Cooool!";
			myTimer.start();
			}
		} else {
			if(strength > 5) {
				myApple.x += strength / 30;
				myApple.rotation += strength / 4;
				} else {
					this["a" + tryShot].visible = true;
					this["a" + tryShot].gotoAndStop(2);
					myMessage.text = "Bad Luck!";
					myTimer.start();
					}
			}
		}

Step 28: Game Over

As we’ve built such a gorgeous game, there’s a good chance that our player will want to play again :-) so let´s make that possible. If the player reaches six attempts (the block of code where we write “Game Over”) we should add a final listener to our stage. It’s a mouse listener and calls a function if you click anywhere.

(Additional lines 12-17,37,38,42,47)

//Functions

...

function invisibleApples():void {
	a1.visible = false;
	a2.visible = false;
	a3.visible = false;
	a4.visible = false;
	a5.visible = false;
	a6.visible = false;
	a1.gotoAndStop(1);
	a2.gotoAndStop(1);
	a3.gotoAndStop(1);
	a4.gotoAndStop(1);
	a5.gotoAndStop(1);
	a6.gotoAndStop(1);
	}

...

function hit(e:Event):void {
	if(tryShot < 7) {
		if((checker == 1) && (myStick.rotation < maxPower)) {
			myStick.rotation += speedUp;
			myArms.rotation += speedUp;
			} else {
				if(myStick.rotation > minPower) {
					myStick.rotation -= speedDown;
					myArms.rotation -= speedDown;
					}
				if(myStick.hitTestObject(myApple)) {
					stage.addEventListener(Event.ENTER_FRAME, shot);
					}
				}
			} else {
				myMessage.text = "Game Over\nClick to play again";
				stage.addEventListener(MouseEvent.MOUSE_DOWN, playAgain);
		}
	}

function playAgain(e:MouseEvent):void {
	resetScene();
	invisibleApples();
	tryShot = 1;
	stage.removeEventListener(MouseEvent.MOUSE_DOWN, playAgain);
	}

Final Code

//Variables
var myHead = new head();
var myArms = new arms();
var myBody = new body();
var myStick = new stick();
var myGround = new ground();
var myFlag = new flag();
var myApple = new apple();
var grassMask:Number;
var speedUp:int = 2;
var speedDown:int = speedUp * 8;
var checker:int;
var minPower:int;
var maxPower:int = 110;
var strength:Number;
var applePower:Number;
var distance:Number;
var target:int;
var tryShot:int = 0;
var myTimer = new Timer(2000, 1);

//EventListeners
stage.addEventListener(KeyboardEvent.KEY_DOWN, onDown);
stage.addEventListener(KeyboardEvent.KEY_UP, onUp);
stage.addEventListener(Event.ENTER_FRAME, hit);
myTimer.addEventListener(TimerEvent.TIMER, countDown);

//Functions
function resetScene():void {
	addChild(myBody);
	addChild(myArms);
	addChild(myHead);
	addChild(myStick);
	addChild(myGround);
	addChild(myFlag);
	addChild(myApple);
	grassMask = (320 + Math.random()*260);
	myBody.x = 70;
	myBody.y = 296;
	myArms.x = 68;
	myArms.y = 276;
	myArms.rotation = 0;
	myHead.x = 70;
	myHead.y = 215;
	myStick.x = 68;
	myStick.y = 276;
	myStick.rotation = 0;
	myApple.x = 90;
	myApple.y = 331;
	myGround.x = 0;
	myGround.y = 375;
	myGround.grassMask.x = grassMask - 610;
	myFlag.x = grassMask;
	myFlag.y = 375;
	checker = 0;
	minPower = 0;
	target = 0;
	tryShot ++;
	myApple.appleFall.gotoAndStop(1);
	myMessage.text = "Press any key!";
	}

resetScene();

function invisibleApples():void {
	a1.visible = false;
	a2.visible = false;
	a3.visible = false;
	a4.visible = false;
	a5.visible = false;
	a6.visible = false;
	a1.gotoAndStop(1);
	a2.gotoAndStop(1);
	a3.gotoAndStop(1);
	a4.gotoAndStop(1);
	a5.gotoAndStop(1);
	a6.gotoAndStop(1);
	}

invisibleApples();

function onDown(e:KeyboardEvent):void {
	checker = 1;
	minPower = -20;
	}

function onUp(e:KeyboardEvent):void {
	checker = 0;
	applePower = (myApple.x + myStick.rotation) * 3.2;
	}

function hit(e:Event):void {
	if(tryShot < 7) {
		if((checker == 1) && (myStick.rotation < maxPower)) {
			myStick.rotation += speedUp;
			myArms.rotation += speedUp;
			} else {
				if(myStick.rotation > minPower) {
					myStick.rotation -= speedDown;
					myArms.rotation -= speedDown;
					}
				if(myStick.hitTestObject(myApple)) {
					stage.addEventListener(Event.ENTER_FRAME, shot);
					}
				}
			} else {
				myMessage.text = "Game Over\nClick to play again";
				stage.addEventListener(MouseEvent.MOUSE_DOWN, playAgain);
		}
	}

function playAgain(e:MouseEvent):void {
	resetScene();
	invisibleApples();
	tryShot = 1;
	stage.removeEventListener(MouseEvent.MOUSE_DOWN, playAgain);
	}

function shot(e:Event):void {
	strength = (applePower - myApple.x);
	distance = grassMask - myApple.x;
	if((Math.round(distance) <= 10) && (Math.round(distance) >= 3) && (strength <= 65)) {
		if(target == 0) {
			myApple.appleFall.play();
			myApple.rotation = 0;
			target = 1;
			this["a" + tryShot].visible = true;
			myMessage.text = "Cooool!";
			myTimer.start();
			}
		} else {
			if(strength > 5) {
				myApple.x += strength / 30;
				myApple.rotation += strength / 4;
				} else {
					this["a" + tryShot].visible = true;
					this["a" + tryShot].gotoAndStop(2);
					myMessage.text = "Bad Luck!";
					myTimer.start();
					}
			}
		}

function countDown(e:TimerEvent):void {
	resetScene();
	stage.removeEventListener(Event.ENTER_FRAME, shot);
	}

Conclusion

Pretty simple wasn´t it? Now you can fool around with all sorts of things; you can add other levels, terrains, you can change the character, add cheats into the game, add a game menu or intro and so on. There are unlimited possibilities, so I hope you found it useful and thanks for reading!

]]>
http://active.tutsplus.com/tutorials/games/build-a-mini-golf-game-with-actionscript-3-0/feed/ 39