logo
468x60-2-495


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

Generating Ghosts That Follow in Your Footsteps

Path following is a simple concept to grasp: the object moves from point A to point B to point C, and so on. But what if we want our object to follow the path of the player, like ghosts in racing games? In this tutorial, I’ll show you how to achieve this with waypoints in AS3.


Final Result Preview

Click the SWF, then use the arrow keys to move around. Press space to switch to the ghost, which will follow the path you’ve created.


The Logic Behind Path Following

Let’s suppose the player moves 4 units left and 2 units down from our point of origin. For our ghost to end up in the same location it will have to also move 4 units left and 2 units down from the same point of origin. Now let’s say our player is moving at a speed of 2; for the path following to remain accurate our ghost will also have a speed rate of 2.

What if our player decides to take a pause before continuing on? The obvious solution is for the ghost to keep track of the player’s exact position every tick – but this will involve storing a lot of data. Instead, what we’ll do is simply store data every time the player presses different keys – so if the player moves right for ten seconds, we’ll store the same amount of data as if the player moved right for half a second.

For this technique to work our ghost must abide by the following rules:

  • The ghost and player have the same point of origin.
  • The ghost must follow the exact same path as the player.
  • The ghost should move at the same speed as the player.
  • The ghost has to store the current time each time the player’s motion changes.

Step 1: Setting Up

Start by creating a new Flash file (ActionScript 3.0). Set the width to 480, the height to 320 and frames per second to 30. Leave the background color as white and save the file as CreatingGhosts.fla; lastly set its class to CreatingGhosts.

Before we move into the classes we need to create a pair of MovieClips. Start by drawing two separate 20px squares without a stroke. Convert the first fill to a MovieClip, setting its registration to the center, naming it player and exporting it for ActionScript with the class name Player. Now repeat the same process, except replace the name with ghost and the class with Ghost. Remove these MovieClips from the stage.

Create your document class with the following code:


package{
	import flash.display.*;
	import flash.events.*;

	public class CreatingGhosts extends MovieClip{
		public var player:Player = new Player();
		public function CreatingGhosts(){
			addChild(player);
		}
	}
}

Self explanatory; our next step will be to set up the Player class:


package{
	import flash.display.*;
	import flash.events.*;
	import flash.geom.Point;
	import flash.ui.Keyboard;
	import flash.utils.Timer;
	import flash.utils.getTimer;

	public class Player extends MovieClip{
		public var startPos:Point;
		public var startTime:int;
		public var speed:Number = 2;
		public var currentLife:int;
		public var keyPressLeft:Boolean = false;
		public var keyPressRight:Boolean = false;
		public var keyPressUp:Boolean = false;
		public var keyPressDown:Boolean = false;
		public function Player(){

		}
	}
}

The first three variables are used to help meet the rules; startPos is our point of origin, startTime is the time when the Player was added to the stage and speed is our our rate of movement. currentLife is an addition used to check how many times the player has died, accordingly each path is stored and obtainable through that value. The last four variables are used to check key presses.

It’s time to create the Ghost class:


package{
	import flash.display.*;
	import flash.events.*;
	import flash.geom.Point;
	import flash.utils.getTimer;
	import flash.utils.Timer;

	public class Ghost extends MovieClip{
		static public var waypoints:Array = new Array();
		static public var times:Array = new Array();
		public var i:int = 0;
		public var startTime:int;
		public var speed:Number = 2;
		public var selectedLife:int;
		public function Ghost(){

		}
	}
}

The two static variables, waypoints and times, will be used to store arrays; the first will store coordinates of the player’s positions whenever the player changes motion, and the second will store the times at which each change occurred. The other variables match those from the Player class.


Step 2: Initializing the Player

Within the Player’s constructor add the following line:


addEventListener(Event.ADDED_TO_STAGE, init);

Next create the init() function:


public function init(e:Event){

}

First, we need to obtain the startTime and push a new time array to the Ghost’s times array. (This is a little confusing; the ghost has multiple time arrays to allow it to deal with multiple lives in the future.)


startTime = flash.utils.getTimer();
Ghost.times.push(new Array);
currentLife = Ghost.times.length - 1;
Ghost.times[currentLife].push(flash.utils.getTimer() - startTime);

startTime is set to the current time (a value in milliseconds); we add a new child array to the Ghost’s times array; our currentLife is set to the index of this new array; and we push the time that has elapsed during this function to the first element of this new array.

Now we set up the starting position:


startPos = new Point(stage.stageWidth/2, stage.stageHeight/2);
this.x = startPos.x;
this.y = startPos.y;
Ghost.waypoints.push(new Array);
Ghost.waypoints[currentLife].push(startPos);

Our point of origin is set to the center of the stage; we reposition our Player to the origin; a new array is added to the waypoints array in the Ghost class; and the first position is pushed to that array.

So, at the moment, Ghost.times[0][0] contains the number of milliseconds since the SWF was set up (practically zero), and Ghost.waypoints[0][0] contains a Point set to the center of the stage.

Our aim is to code this so that if, after one second, the player presses a key, then Ghost.times[0][1] will be set to 1000, and Ghost.waypoints[0][1] will be another Point, again set to the center (because the player will not have moved yet). When the player lets go of that key (or presses another), Ghost.times[0][2] will be set to the current time, and Ghost.waypoints[0][2] will be a Point that matches the player’s position at that time.

Now, here are the three event listeners:


addEventListener(Event.ENTER_FRAME, enterFrame);
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUp);

Step 3: Key Events

For now let’s ignore the enterFrame and focus on the key presses.


public function keyDown(e:KeyboardEvent){
	if (e.keyCode == Keyboard.LEFT && keyPressLeft == false){
		updateWaypoints();
		keyPressLeft = true;
	}else if (e.keyCode == Keyboard.RIGHT  && keyPressRight == false){
		updateWaypoints();
		keyPressRight = true;
	}

	if (e.keyCode == Keyboard.UP  && keyPressUp == false){
		updateWaypoints();
		keyPressUp = true;
	}else if (e.keyCode == Keyboard.DOWN  && keyPressDown == false){
		updateWaypoints();
		keyPressDown = true;
	}

	if (e.keyCode == Keyboard.SPACE){
		destroy();
	}
}

Just a few simple if-statements to prevent bugs in key presses, and two new functions that are being called. updateWaypoints() will be called every time new points and times are to be pushed to the ghost arrays, and destroy() is used to remove the Player and add the Ghost to the stage. But before we go to those functions let’s finish off the key press functions.


public function keyUp(e:KeyboardEvent){
	if (e.keyCode == Keyboard.LEFT  && keyPressLeft == true){
		updateWaypoints();
		keyPressLeft = false;
	}else if (e.keyCode == Keyboard.RIGHT  && keyPressRight == true){
		updateWaypoints();
		keyPressRight = false;
	}

	if (e.keyCode == Keyboard.UP  && keyPressUp == true){
		updateWaypoints();
		keyPressUp = false;
	}else if (e.keyCode == Keyboard.DOWN  && keyPressDown == true){
		updateWaypoints();
		keyPressDown = false;
	}
}

This time we do the opposite: the variables are set to false when the key is released and the waypoints are updated.

I will elaborate in more detail on what is happening between those functions. Each time you press a key the waypoints and times are updated, so if you press another to cause a change a point and its corresponding time are added to the ghost arrays.

But what happens if the player decides to randomly release a key and cause change again? Well we account for that by updating the waypoints and times again. If this was not done the Ghost would not be able to account for 90 degree turns; instead it would move on an angle towards the next point.


Step 4: Updating and Destroying

Our updateWaypoints() function is fairly simple, seeing as it consists of code that we have already written:


public function updateWaypoints(){
	Ghost.times[currentLife].push(flash.utils.getTimer() - startTime);
	Ghost.waypoints[currentLife].push(new Point(this.x, this.y));
}

The destroy() function is just as simple! Waypoints are updated, a Ghost is added, event listeners are stopped and our Player is removed:


public function destroy(){
	updateWaypoints();
	var ghost:Ghost = new Ghost();
	parent.addChild(ghost);
	removeEventListener(Event.ENTER_FRAME, enterFrame);
	stage.removeEventListener(KeyboardEvent.KEY_DOWN, keyDown);
	stage.removeEventListener(KeyboardEvent.KEY_UP, keyUp);
	parent.removeChild(this);
}

Step 5: The Player’s enterFrame

Begin by creating the function:


public function enterFrame(e:Event){

}

For the purposes of this tutorial we will add some simple collision with borders, to show how the waypoints are updated on this change:


if((this.x-(this.width/2)) > 0){

}
if((this.x+(this.width/2)) < stage.stageWidth){

}
if((this.y-(this.height/2)) > 0){

}
if((this.y+(this.height/2)) < stage.stageHeight){

}

Now are player should only move in the specified direction while it isn’t touching a border. Inside the first if-statement add the following code for moving left:


if(keyPressLeft == true){
	if((this.x-(this.width/2)) <= 0){
		updateWaypoints();
		this.x = this.width/2;
	}else{
		this.x -= speed;
	}
}

First we check if the left key is currently down, then we check to see if the Player’s position is greater than or equal to 0; if so we update our waypoints and reposition the player to the edge of the left side; if not we continue to move the player left.

The exact same thing is done for the other three sides:


if(keyPressRight == true){
	if((this.x+(this.width/2)) >= stage.stageWidth){
		updateWaypoints();
		this.x = (stage.stageWidth - (this.width/2));
	}else{
		this.x += speed;
	}
}

if(keyPressUp == true){
	if((this.y-(this.height/2)) <= 0){
		updateWaypoints();
		this.y = this.height/2;
	}else{
		this.y -= speed;
	}
}

if(keyPressDown == true){
	if((this.y+(this.height/2)) >= stage.stageHeight){
		updateWaypoints();
		this.y = (stage.stageHeight - (this.height/2));
	}else{
		this.y += speed;
	}
}

And with that we are finished with the Player Class!


Step 6: Initializing the Ghost

Add the following line inside the Ghost’s constructor:


addEventListener(Event.ADDED_TO_STAGE, init);

Like before create the init() function:


public function init(e:Event){
	selectedLife = times.length - 1;
	this.x = waypoints[selectedLife][0].x;
	this.y = waypoints[selectedLife][0].y;
	startTime = flash.utils.getTimer();
	addEventListener(Event.ENTER_FRAME, enterFrame);
}

We start by selecting the path we want to use (by default it will choose the last array); we then position the ghost to the origin and set our Ghost’s start time. Then an event listener for the enterFrame is created.


Step 7: The Ghost’s enterFrame

Naturally we create our enterFrame function:


public function enterFrame(e:Event){

}

Now we have to loop through our time array. We do this through the variable i; we check if it is less than the length of the array and we also check if the time elapsed is greater than or equal to the current time in the array:


while (i < times[selectedLife].length - 1 && flash.utils.getTimer() - startTime >= times[selectedLife][i]) {
	i++;
}

The next thing to do is to move the Ghost if the time elapsed is less than the current time from the array:


if (flash.utils.getTimer() - startTime < times[selectedLife][i]) {
	updatePosition();
}

Step 8: Updating the Ghost’s Position

We’ll start this step off by creating the updatePosition() function:


public function updatePosition(){

}

Next add two variables, to represent the difference and the distance between the old and the new position:


var diff:Point = waypoints[selectedLife][i].subtract(new Point(this.x, this.y));
var dist = diff.length;

We subtract the points from each other to find the distance. Now, we must move the ghost:


if (dist <= speed){
	this.x = waypoints[selectedLife][i].x;
	this.y = waypoints[selectedLife][i].y;
}else{
	diff.normalize(1);
	this.x += diff.x * speed;
	this.y += diff.y * speed;
}

First we check whether the distance is less than the speed (i.e. the distance the ghost moves each tick); if so we move the Ghost directly to the point. However, if the distance is less then we normalize the difference (“means making its magnitude be equal to 1, while still preserving the direction and sense of the vector” – Euclidean Vectors in Flash), and we increase the Ghost’s position along the direction of the point.

(more…)

search search search search search
Find an Article
Categories
  • Flash Video Training
  • Hints and Tips
  • Recommended
Please Support Our Sponsors
Recent Posts
  • Tuts+ Community Meetup in New York!
  • HTML5 Canvas Optimization: A Practical Example
  • Recreate the Cover Flow Effect Using Flash and AS3
  • Drawing Activetuts+ to a Close
  • Intro to Dart: Creating a Marquee
Tag Cloud
2011 ActionScript Active Activetuts+ Adobe animation Basic Basix Best Build Button Character Code Create Creating Critique Custom design Effect Effects Files Flash from Game Guide HTML5 Introduction Macromedia Motion Muzzle part Player Premium Professional Quick Silverlight Simple Text Tool Tutorial Tuts+ Using Video website Workshop
About Our Site:

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

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

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

Blogroll

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

Meta

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

Archives

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