As mentioned, acceleration has a value—the force—and a direction, just like velocity. And like velocity, if you start with those two factors, you need to break them down into the component x and y forces. Now, if you are paying attention, you know that the way to do that is by using
Math.cos
andMath.sin
. Here’s how that looks:var force = 10,
angle = 45, //degrees. Need to convert!
ax = Math.cos(angle * Math.PI / 180) * force, ay = Math.sin(angle * Math.PI / 180) * force;
Now that you have acceleration for each axis, you can update the velocity on each axis, and from that, update the object’s position.
Let’s resurrect the mouse follower example from earlier in the chapter and make it work with acceleration instead of just plain velocity. Because that example used the
Arrow
class, find thearrow.js
file to include in this exercise. Remember that in the earlier incarnation, you took the angle from the arrow to the mouse and used that to determinevx
andvy
. This time, you use the same calculations, but employ them to determineax
anday
instead. Then you add the acceleration values to the velocity values and the velocity values to thex
andy
properties. Here’s the code(10-follow-mouse-2.html)
:<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Follow Mouse 2</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<canvas id="canvas" width="400" height="400"></canvas>
<script src="utils.js"></script>
<script src="arrow.js"></script>
<script>
window.onload = function () {
var canvas = document.getElementById('canvas'), context = canvas.getContext('2d'),
window.requestAnimationFrame(drawFrame, canvas);
context.clearRect(0, 0, canvas.width, canvas.height);
var dx = mouse.x - arrow.x, dy = mouse.y - arrow.y,
angle = Math.atan2(dy, dx), //radians ax = Math.cos(angle) * force,
ay = Math.sin(angle) * force;
124
arrow.rotation = angle;
vx += ax;
vy += ay;
arrow.x += vx;
arrow.y += vy;
arrow.draw(context);
}());
};
</script>
</body>
</html>
Notice that we also change the variable
speed
intoforce
and make it much smaller. Because acceleration is additive, you want to start with small amounts because it builds up quickly. Also, nowvx
andvy
are declared at the top of the script; earlier, they were calculated newly on each frame, but now you need to keep track of them and add or subtract from their value each time. Of course, you can do away with theax
anday
variables here altogether and just add the result of the sine and cosine lines directly to the velocities. Here, they are separated for clarity.Run the example and you see the accelerating arrow swinging around a moving cursor and pointing toward the mouse position the entire time. But look back to the first motion example you did at the beginning of the chapter, and see just how far you’ve come. By learning just a couple of basic principles, you’ve now created something a million times more fluid and dynamic—something that almost feels alive.
And you’re not even at the end of the chapter yet!
Let’s pull everything together and see how much further you can go with it.
A spaceship
We’ve been talking a lot about spaceships travelling from here to there. Well, with the ground you’ve covered so far, you should be able to put together a reasonable spaceship simulation.
Here’s the plan. The spaceship will be a class of its own that takes care of drawing itself, much like the
Arrow
orBall
classes you’ve been using. You can use the left and right keys to rotate it left and right. The up key will act to fire the rocket. Of course, the rocket is in the back of the ship and fires straight back.Thus, the force that the rocket applies will cause acceleration in the direction the ship is facing at that time.
Actually, what you’re going to make is like the ship in the old game Asteroids, but without the actual asteroids.
First, you need a ship class. Its
draw
method uses a few lines of canvas drawing API code to render four short, white lines, as an homage to the original that we are copying. Save the following code in the fileship.js
to import into the next example:function Ship () { this.x = 0;
this.y = 0;
this.width = 25;
this.height = 20;
this.rotation = 0;
this.showFlame = false;
125
}Ship.prototype.draw = function (context) { context.save();
context.translate(this.x, this.y);
context.rotate(this.rotation);
context.lineWidth = 1;
context.strokeStyle = "#ffffff";
context.beginPath();
context.moveTo(10, 0);
context.lineTo(-10, 10);
context.lineTo(-5, 0);
context.lineTo(-10, -10);
context.lineTo(10, 0);
context.stroke();
if (this.showFlame) { context.beginPath();
context.moveTo(-7.5, -5);
context.lineTo(-15, 0);
context.lineTo(-7.5, 5);
context.stroke();
}
context.restore();
};
The
draw
method references the ship's propertyshowFlame
, which is a true or false value. This way, you can draw the ship with or without a flame showing. This is useful to show that the engines are firing. You can see how it looks with and without the flame in Figures 5-8 and 5-9.Figure 5-8. The future of space travel.
126
Figure 5-9. All systems go.