Do you like my tutorials?

Then consider supporting me on Ko-fi

Talking about Racing game, Actionscript 2, Flash, Game development and Users contributions.

If you’re interested in gaming and blogging, YouSayToo lets you earn from your flash games and allows you to make money blogging.

In this tutorial we’ll cover the creation of a flash racing game.

I got the permission from Remus C. from gameSheep to publish this awesome tutorial.

Before we start, I recommend you all to give a look at gameSheep. You will find a lot of games with cute graphics.

Moreover, this tutorial will give the start to another series of tutorials about creating a flash racing game.

Let’s start!

Moving the car

1. Car movement is not the most difficult part of a racing game, but if you want to simulate realistic (almost realistic) movement you have to take in consideration some of the aspects described below.

2. Download and open racing_part1_step1.fla file.

3. Click on the first frame (the only frame) of the “defs” layer and press F9 to display the Actions Window for this frame. Now let’s see what these variables do:

car1.code = "player"; 
//this variable will decide if the specified car is controlled by a human player or by the computer (in Part II of this tutorial you will learn how to add opponents and this variable will come in handy)
acceleration = 0.4; 
//the acceleration variable will add to the speed variable on every enterFrame event (in this case 24 times per second); a higher value translates in a faster acceleration of the car
speedDecay = 0.96; 
//when the car is not accelerated (the UP Key is released), the car will have to slow down smoothly; the speed will multiply with this value (less than 1); the lower this value is, the faster the car will slow down
rotationStep = 10;
//this is the number of degrees that will increase or decrease the car's rotation (angle) when the left or right keys are pressed
maxSpeed = 10;
//this is the speed limit on our track; increase it if you want the car to go faster
backSpeed = 1;
//this is the speed limit when going backwards

4. OK! Now let’s get back to school :) and see what we can do with these variables.

Click on the first frame of the second layer (“actions”) and if the Actions windows is not open, press F9 to display it.

We will discuss this script in a few moments, but first let’s see how Flash “understands” movement and coordinates.

Just a bit of trigonometry and Flash

Flash is using the classic Cartesian Coordinate System (a grid based system with a horizontal axis OX and a vertical axis OY).

vertical

You notice in the attached picture that in Flash the Y axis is inverted meaning that the negative side of the Y axis is positioned higher than the positive side. So the lower a coordinate is, the higher it’s value will be.

Because Flash understand only horizontal and vertical vectors we will have to calculate the horizontal and the vertical components of the actual “speed”.

So, from trigonometry we know (in this case) that:

sin(angle) = speedx/speed and

cos(angle) = speedy/speed

so… we know the angle (angle=car._rotation) and we know the speed. That’s all we need know. Is it? No. You need to know one more thing:

The Math class implemented in Macromedia Flash does not work with angles measured in degrees. Instead we will have to provide angles measured in radians (an alternative unit measurement for angles).

The only case in which you will use degrees is when actually rotating the movieclips.

Using the simple equation below you will be able to transform degrees into radians:

angle_radians = angle_degrees * (PI/180)

Now we can easily calculate the X and Y components of the car’s speed:

speedx = Math.sin(_rotation*(Math.PI/180))*speed;
speedy = Math.cos(_rotation*(Math.PI/180))*speed*-1;

Well, you already figured out why the sign of the Y component has to be inverted ;)

And now let’s get back to Flash and our Actions Window. Next I will explain what the “step” function is all about. The “step” function will be executed on every enterFrame event (on the “stepper” layer you will find an empty movieclip the executes the onClipEvent (enterFrame) routine).

function step(who) {
	//check to see if the car in question is controlled by the player or by the computer
	if (_root["car"+who].code == "player") {
		//we will constantly decrease speed by multiplying it with a number below 1, but only if speed if higher than 0.3; a lower value will only consume resources and movement will not even be noticed so we will set the speed variable to 0
		if (this["speed"+who]>0.3) {
			this["speed"+who] *= _root.speedDecay;
		} else {
			this["speed"+who] = 0;
		}
		//the car will react to certain key presses that we will capture using the Key.isDown method as follows
		//accelerate - we add a certain value to the speed variable if the UP key is pressed and the speed is lower than it's maximum alowed value
		if (Key.isDown(Key.UP) && this["speed"+who]<_root.maxSpeed) {
			this["speed"+who] += _root.acceleration;
		}
		//brake (reverse) - same thing, but here we subtract 
		if (Key.isDown(Key.DOWN)) {
			this["speed"+who] -= _root.backSpeed;
		}
		//steer left - well, we could simply add or subtract a fixed angle (in degrees) to/from the car's rotation, but that's not good enough. In order to simulate a natural movement, steering must depend on speed, otherwise you will be able to rotate your car even if it's almost stopped and it will look like a propeller :) 
		if (Key.isDown(Key.LEFT) && this["speed"+who]>0.3) {
			_root["car"+who]._rotation -= _root.rotationStep*(this["speed"+who]/_root.maxSpeed);
		}
		//steer right - you already know what happens here 
		if (Key.isDown(Key.RIGHT) && this["speed"+who]>0.3) {
			_root["car"+who]._rotation += _root.rotationStep*(this["speed"+who]/_root.maxSpeed);
		}
		this["rotation"+who] = _root["car"+who]._rotation;
		//we calculate the two components of speed (X axis and Y axis) - we have already discussed this part of the function above
		this["speedx"+who] = Math.sin(this["rotation"+who]*(Math.PI/180))*this["speed"+who];
		this["speedy"+who] = Math.cos(this["rotation"+who]*(Math.PI/180))*this["speed"+who]*-1;
		//apply the components on the actual position of the car - we add the X component of the speed to the X coordinate of the car and the Y component of the speed to the Y coordinate
		_root["car"+who]._x += this["speedx"+who];
		_root["car"+who]._y += this["speedy"+who];
		//position the shadow of the car - when the car steers, we want the shadow to keep it X and Y coordinates and always stay on one side of the car (whatever side that would be)
		_root["shadow"+who]._x = _root["car"+who]._x-4;
		_root["shadow"+who]._y = _root["car"+who]._y+2;
		_root["shadow"+who]._rotation = _root["car"+who]._rotation;
	}
	if (_root["car"+who].code == "computer") {
		//in our next tutorial "Racing Game Part II" we will add opponents to make the game more dynamic and interactive
	}
}
 

That’s it! We already have a moving car. Now we can move on to collisions.

Collisions

1. We all know why collisions are important in a Racing Game… Because we don’t want the car to leave the track, because we want to force the player to use a specific way, because we want him/her to avoid collisions in order to get the best time (or win the race).

Collisions are a very important part of a racing game and 70% of the game feeling and success depends on good collisions.

We don’t want the car to get stuck in the non accessible areas (NAA), we don’t want it to lose all speed although it hardly touches those areas and we definitely don’t want it to bounce back (by reversing the speed).

In other words we don’t want to give the player a hard time, but on the contrary, an enjoyable game. So when the car touches the NAA we must try to correct it’s trajectory and of course apply a speed penalty depending on the angle of collision and collision duration.

2. Download and open the racing_part1_step2.fla file.

3. Before we go back to the “step” function, I will explain how the collisions will work.

Using four points to detect collisions

As you can see in the attached picture, we will pick four points, one on every side of the car and check to see if any of them “touches” the NAA.

Collision

For example if the Left Side Point is inside the NAA (hits the NAA) then we will have to apply a speed penalty and increase the angle (_rotation) of the car. Why do we do that? Because of what we discussed earlier: we must try to correct the car’s trajectory. So what we do here is force the car to steer right.

OK, I guess everything is clear up to this point. And since we are speaking of points, let’s see how we calculate their coordinates. To simplify things we will take the Left Side Point as an example.

When the car’s rotation is 0 our job is very simple: the LSP coordinates are x=car._x-20 (20 pixels to the left of the car’s center point) and y=car._y

But the car will not always have an angle of 0. Well, here comes the tricky part. There are a few ways to calculate the four points even if the angle is not 0 (for example you can use the sine and the cosine functions) and for this tutorial I chose the simple way (I don’t know if it’s the optimum way, but it’s very simple):

We define the Left Side Point as if the car’s rotation was 0:

car.pointLeft = {x:-20, y:0}; //this is an Object

and then we transform the point’s coordinated from local (related to the car’s clip) to global (related to the _root clip where we will test the collisions):

car.localToGlobal(car.pointLeft);

Now we have our Left Side Point coordinates that we can use to check the collision:

car.pointLeft.x and car.pointLeft.y

Can it get any simpler? :)

4. And again back to our Actions Window. Click on the first frame of the “actions” layer and if the Actions Window is not open press F9 to display it.

You will notice that I added a few lines to the “step” function and you probably already know what those lines do :) but still I will go into a few details.

//the collisions
//define the four collision points
_root["car"+who].pointLeft = {x:-20, y:0};
_root["car"+who].localToGlobal(_root["car"+who].pointLeft);
_root["car"+who].pointRight = {x:20, y:0};
_root["car"+who].localToGlobal(_root["car"+who].pointRight);
_root["car"+who].pointFront = {x:0, y:-25};
_root["car"+who].localToGlobal(_root["car"+who].pointFront);
_root["car"+who].pointBack = {x:0, y:25};
_root["car"+who].localToGlobal(_root["car"+who].pointBack);
//let's use some shorter variable names :) - this is just for better understanding and to simplify the code
this["lpx"+who] = _root["car"+who].pointLeft.x;
this["lpy"+who] = _root["car"+who].pointLeft.y;
this["rpx"+who] = _root["car"+who].pointRight.x;
this["rpy"+who] = _root["car"+who].pointRight.y;
this["fpx"+who] = _root["car"+who].pointFront.x;
this["fpy"+who] = _root["car"+who].pointFront.y;
this["bpx"+who] = _root["car"+who].pointBack.x;
this["bpy"+who] = _root["car"+who].pointBack.y;
//check for collisions - we check the collisions using the Flash built in hitTest method. This methods can be used to compare the x and y coordinates to the shape or bounding box of the specified instance (in our case _root.terrain), according to the shape flag (the third parameter: true-shape / false - bounding box) setting or to check if the bounding boxes of the target and the specified instance overlap. We use the first one.
if (_root.terrain.hitTest(this["lpx"+who], this["lpy"+who], true)) {
	//steer right
	_root["car"+who]._rotation += 5;
	//speed penalty
	this["speed"+who] *= 0.85;
}
if (_root.terrain.hitTest(this["rpx"+who], this["rpy"+who], true)) {
	//steer left
	_root["car"+who]._rotation -= 5;
	//speed penalty
	this["speed"+who] *= 0.85;
}
if (_root.terrain.hitTest(this["fpx"+who], this["fpy"+who], true)) {
	//stop the car
	this["speed"+who] = -1;
}
if (_root.terrain.hitTest(this["bpx"+who], this["bpy"+who], true)) {
	//stop the car
	this["speed"+who] = 1;
}

Hard? Not so hard :) And the next two steps are even simpler.

Where are we running?

1. Up to now we have a 100% functional game engine but no game. We don’t have a goal. So we’ll have to add one and because this tutorial is about a time trial racing game we will add laps and timers.

2. Download and open the racing_part1_step3.fla file.

3. You notice that I added two frames on every layer and I labeled them “ready set”, “go” and “finish”. In the first frame a movie clip will play saying “ready, set, go”. When “go” is displayed _root will move to the frame labeled “go” where the car can move.

Why the car will not move in the first frame? Well, that’s because the “stepper” movieClip only exists in the second frame, so that’s where the “step” function will be executed.

On the second frame of the “actions” layer you will also find two new variables. Those variables will be used to store the exact time when the race started (initialTime|) and the exact time when the current lap started (lapTime).

When the game is over, after the player finishes ten laps, _root will move to the “finish” frame where an information movieClip will be displayed.

4. OK! What we need to do next is determine whether the player has finished a lap or not, so we will add two movieClip (the red line on the right side) and check if the car “touched” this movieClip, and if it did, than you know that a lap is finished… hmm… not really :)

First of all the car will “touch” this movieClip for more than one frame. Maybe for two, maybe for ten, maybe for one hundred frames, you cannot determine this number because it depends on the car’s speed. And instead of increasing the number of laps with one, you will increase it with two, ten or one hundred laps, so the race will be ready quite fast.

The second problem, assuming that you solved the first one is that one player will go past the finish line (the red line on the right) and then return immediately to the same line and “touch” it again, increasing the number of laps even though the lap is not completed. This problem can be solved in a few ways but we will chose the solution that fixes both our problems: we will add a checkpoint (the red line to the left). This checkpoint will be placed somewhere around the middle of the race so that the player will lose more time returning to the finish line than he will lose by completing the lap. Of course if you want a more secured race you will add more than one checkpoint. You will be able to easily add more checkpoints after finishing this tutorial.

5. Open the actions window for the first frame of the “actions” layer. We have to new functions both related to timing the race – setTimes (calculates and sets the total race time) and setBestLap (calculates and sets the best lap time). We’ll take them one at a time and see what they do.

function setTimes() {
	//we calculate the time elapsed from the moment the race started in millisecond
	timeElapsed = getTimer()-_root.initialTime;
	//we calculate the minutes, seconds and tens of seconds and set them to their respective variables
	milliseconds = timeElapsed;
	seconds = Math.floor(milliseconds/1000);
	minutes = Math.floor(seconds/60);
	minutesTXT = minutes;
	secondsTXT = seconds-minutes*60;
	tensTXT = Math.round((milliseconds-seconds*1000)/10);
	//if the minutes, seconds or the tens of seconds number has only one character we add a "0" before it - that's just because we want the time to look good ;)
	if (minutesTXT<10) {
		minutesTXT = "0"+minutesTXT;
	}
	if (secondsTXT<10) {
		secondsTXT = "0"+secondsTXT;
	}
	if (tensTXT<10) {
		tensTXT = "0"+tensTXT;
	}
	//we put all three variables in one that will be used in the timers tables 
	_root.totalTimeTXT = minutesTXT+"."+secondsTXT+"."+tensTXT;
}
//and the second function
function setBestLap() {
	//this function does the exact same thing as the first one, only here we will use the time elapsed from the last time the car has passed the finish line
	bestTime = getTimer()-_root.lapTime;
	milliseconds = bestTime;
	//we don't calculate the lap time if the car passes the finish/start line for the first time
	if (oldMilliseconds>milliseconds || oldMilliseconds == null) {
		oldMilliseconds = milliseconds;
		seconds = Math.floor(milliseconds/1000);
		minutes = Math.floor(seconds/60);
		minutesTXT = minutes;
		secondsTXT = seconds-minutes*60;
		tensTXT = Math.round((milliseconds-seconds*1000)/10);
		if (minutesTXT<10) {
			minutesTXT = "0"+minutesTXT;
		}
		if (secondsTXT<10) {
			secondsTXT = "0"+secondsTXT;
		}
		if (tensTXT<10) {
			tensTXT = "0"+tensTXT;
		}
		_root.bestLapTXT = minutesTXT+"."+secondsTXT+"."+tensTXT;
	}
	//we set the initial time to the moment the car passed the finish line 
	_root.lapTime = getTimer();
}

6. And now one more time (I promise this is the last time) back to the "step" function and let's analyze the new lines.

//checkpoints
//we check to see if the car "touches" the current checkpoint (the current checkpoint, in this case, can be checkpoint1 or checkpoint2 because we only have two checkpoints; checkpoint1 is the start/finish line)
if (_root["car"+who].hitTest(_root["checkpoint"+_root["currentCheckpoint"+who]])) {
	//if the current checkpoint is the start line - increase the lap number
	if (_root["currentCheckpoint"+who] == 1) {
		//if the current lap is not 0 we check if this is the best lap
		if (_root["currentLap"+who] != 0) {
			_root.setBestLap();
		}
		//if this is the final lap, _root will move to the "finish" frame 
		if (_root["currentLap"+who] == _root.totalLaps) {
			_root.gotoAndStop("finish");
		} else {
			_root["currentLap"+who]++;
		}
		_root.currentLapTXT = _root["currentLap"+who]+"/10";
	}
	//we set to checkpoint to be checked to the next checkpoint 
	_root["currentCheckpoint"+who]++;
	//if the current checkpoint is the last checkpoint - set the next checkpoint to the start line
	if (_root["currentCheckpoint"+who]>_root.checkpoints) {
		_root["currentCheckpoint"+who] = 1;
	}
}

7. That's it :) Now let's move on to the final graphic touches and see our completed game.

Finishing the game

1. The final graphic touches... Well, there's nothing to explain here. You can express yourself in anyway and create graphics after your own taste.

Just remember to set the alpha of the green movie clip and the red movie clips to 0.

2. This is racing_part1_step4.fla file provided with this tutorial. Download it!

3. Remember that this is just a tutorial. You must work on it in order to build a really addicting game. In the next tutorial we will add opponents in the game. Until then, have fun building your Time Trial Racing Game ;)

And by the way my best lap time is 9.84 :D

And here ends Remus tutorial. I learned a lot from this tut and I will use it to improve the gameplay with some additions.

Meanwhile, give feedback to Remus and try to beat that n00b laptime!!

Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.