Main content
Computer programming - JavaScript and the web
Course: Computer programming - JavaScript and the web > Unit 5
Lesson 4: Vectors- Intro to vectors
- Challenge: Vector walker
- More vector math
- Challenge: Lightsaber
- Vector magnitude & normalization
- Challenge: Magnitude visualizer
- Vector motion
- Challenge: Braking car
- Static functions vs. instance methods
- Challenge: Static functions
- Interactive vector motion
- Challenge: Mouse stalker
- Project: Computational creatures
© 2023 Khan AcademyTerms of usePrivacy PolicyCookie Notice
Interactive vector motion
To finish out this section, let’s try something a bit more complex and a great deal more useful. We’ll dynamically calculate an object’s acceleration according to a rule stated in Algorithm #3 — the object accelerates towards the mouse.
Anytime we want to calculate a vector based on a rule or a formula, we need to compute two things: magnitude and direction. Let’s start with direction. We know the acceleration vector should point from the object’s position towards the mouse position. Let’s say the object is located at the point
(x,y)
and the mouse at (mouseX,mouseY)
.In that diagram, we see that we can get a vector
(dx,dy)
by subtracting the object’s position from the mouse’s position.:dx = mouseX - x
dy = mouseY - y
Let’s rewrite the above using
PVector
syntax. Assuming we are in the Mover
object definition and thus have access to the object’s PVector
position, we then have:var mouse = new PVector(mouseX, mouseY);
// Look! We’re using the static sub() because we want a completely new PVector
var dir = PVector.sub(mouse, position);
We now have a
PVector
that points from the mover’s position all the way to the mouse. If the object were to actually accelerate using that vector, it would appear instantaneously at the mouse position. This does not make for good animation, of course, and what we want to do now is decide how quickly that object should accelerate toward the mouse.In order to set the magnitude (whatever it may be) of our acceleration PVector, we must first ___ that direction vector. That’s right, you said it. Normalize. If we can shrink the vector down to its unit vector (of length one) then we have a vector that tells us the direction and can easily be scaled to any value. One multiplied by anything equals anything.
var anything = ??;
dir.normalize();
dir.mult(anything);
To summarize, we take the following steps:
- Calculate a vector that points from the object to the target position (mouse)
- Normalize that vector (reducing its length to 1)
- Scale that vector to an appropriate value (by multiplying it by some value)
- Assign that vector to acceleration
Here's what the program looks like, with those steps fully implemented:
You may be wondering why the circle doesn’t stop when it reaches the target. It’s important to note that the object moving has no knowledge about trying to stop at a destination; it only knows where the destination is and tries to go there as quickly as possible. Going as quickly as possible means it will inevitably overshoot the position and have to turn around, again going as quickly as possible towards the destination, overshooting it again, and so on and so forth. Stay tuned; in later sections we’ll learn how to program an object to arrive at a position (slow down on approach).
This example is remarkably close to the concept of gravitational attraction (in which the object is attracted to the mouse position). Gravitational attraction will be covered in more detail in the next section. However, one thing missing here is that the strength of gravity (magnitude of acceleration) is inversely proportional to distance. This means that the closer the object is to the mouse, the faster it accelerates.
Let’s see what this example would look like with an array of movers (rather than just one).
This "Natural Simulations" course is a derivative of "The Nature of Code" by Daniel Shiffman, used under a Creative Commons Attribution-NonCommercial 3.0 Unported License.
Want to join the conversation?
- how to find the biggest possible direction vector compared to the current one?(21 votes)
- You may need to clarify your question, particularly wrt "current" and "biggest". Certainly the largest magnitude of a vector that will fit on the picture canvas is
var maxMag = (new PVector(width-1, height-1)).mag();
(22 votes)
- In the mouse stalker I cant continue, I dont know how to answer it, could some one help me.(17 votes)
- I can't move on either, I think there may be something wrong with this exercise.(6 votes)
- OK I think I almost got it. Thank you for your patience with me. I think that what closeness is something like this.
var closeness = (dir.mag()-???)/maxMag;
But I'm not sure what to subtract from dir.mag()? I'm sure it's painfully obvious but I can't see it.(3 votes)- The hint tells you that 0 should indicate that it is the farthest away and 1 should indicate that it's right on top.
maxMag
is a fixed value. So, the only thing that is changing in that equation ((maxMag - dir.mag()) / maxMag)
) isdir.mag()
. Think about howdir.mag()
is calculated. You created the dir vector to be a new PVector that is the difference between the mouse location and the object. The closer that the object is to the mouse location, the smaller the dir vector will be. The smaller that the direction vector is, the smaller its magnitude will be.
As the magnitude of the dir vector gets smaller and smaller, you are subtracting less and less from maxMag. Therefore, the formula will yield a result that is closer and closer to 1. Conversely, as the object is further away from the mouse, the formula will yield a result that gets closer and closer to zero (as the magnitude of the dir vector gets closer and closer to the magnitude (maxMag) of the largest direction vector (maxDir)).(35 votes)
- I'm stuck on the last part of challenge: mouse stalker. Here is the code:
Mover.prototype.update = function() {
var mouse = new PVector(mouseX, mouseY);
var dir = PVector.sub(mouse, this.position);
var closeness = (maxMag-dir.mag())/maxMag;
dir.normalize();
dir.mult(closeness);
this.acceleration = dir;
this.velocity.add(this.acceleration);
this.velocity.limit(5);
this.position.add(this.velocity);
};
When I entered dir.mult(closeness);, the ball just went to the upper left corner. How do I fix this?(8 votes)- Your variables for maxDir and maxMag should be at the top of your function and hen everything should work swimmingly!(4 votes)
- I don't know what to define maxDir as. Can someone tell me what to do and why to do it?(5 votes)
- It's a vector reaches from the origin to the farthest pixel from the origin:
var maxDir = PVector.sub(new PVector(0, 0), new PVector(width-1, height-1));
(3 votes)
- In the challenge Mouse Stalker, I am on Step 2 and, for some reason, I cannot continue. It says that "mouse is undefined", however it use the method after where it is defined. My code looks like this:
var Mover = function() {
this.position = new PVector(width/2, height/2);
this.velocity = new PVector(0, 0);
this.acceleration = new PVector(0, 0);
};
Mover.prototype.update = function() {
var mouse = new PVector(mouseX, mouseY);
var dir = PVector.sub(mouse, this.position);
dir.normalize();
dir.mult(0.5);
this.acceleration = dir;
this.velocity.add(this.acceleration);
this.velocity.limit(5);
this.position.add(this.velocity);
};
Mover.prototype.display = function() {
stroke(0);
strokeWeight(2);
fill(127);
ellipse(this.position.x, this.position.y, 48, 48);
};
var maxDir = PVector.sub(new PVector(0, 0), new PVector (width - 1, height - 1));
var maxMag = maxDir.mag ();
var dir = PVector.sub(mouse, this.position);
var closeness = ((maxMag - dir.mag()) / maxMag);
Mover.prototype.checkEdges = function() {
if (this.position.x > width) {
this.position.x = 0;
} else if (this.position.x < 0) {
this.position.x = width;
}
if (this.position.y > height) {
this.position.y = 0;
} else if (this.position.y < 0) {
this.position.y = height;
}
};
var mover = new Mover();
var draw = function() {
background(255, 255, 255);
mover.update();
mover.checkEdges();
mover.display();
};
Please help me out!(3 votes)mouse
is defined inside of theupdate
method, which means it is local to that function and cannot be used in the global scale.(8 votes)
- In the next Challenge, what is
.mag()
?(5 votes)- It's the magnitude of the vector. Like every vector has a magnitude and a direction. Think of the direction as an angle, and the magnitude as the third side of a triangle. The vector's first parameter would be the first side, and the vector's second parameter would be the second side. Obvious a^2 + b^2 = c^2 so you square both components of the vector, add them, then take the square root of that to find the magnitude.(8 votes)
- I was on step 2 of the Mouse Stalker challenge. For some reason, it wouldn't accept this code as correct:
Mover.prototype.update = function() {
var mouse = new PVector(mouseX, mouseY);
var dir = PVector.sub(mouse, this.position);
var dirMag = dir.mag();
var maxDir = new PVector(width, height);
var maxMag = maxDir.mag();
var closeness = (maxMag-dirMag)/maxMag;
But, when I commented out my variable (dirMag), and just typed dir.mag() in the variable closeness, it was accepted.
var closeness = (maxMag-dir.mag())/maxMag;
Why is this?(4 votes)- Because the program that inspects your program is far from perfect.(4 votes)
- With help, I managed to finish the "Mouse Stalker" challenge. However, I'm having trouble understanding how the math works to make the
Mover
go faster with more closeness to the mouse cursor.(2 votes)- You need to compare the maximum magnitude to the current magnitude, and then scale it down so it's a number between 1 and 0.(3 votes)
- In the Mouse stalker challenge, does the
closeness
variable make the circle accelerate faster when it's closer to the mouse?Mover.prototype.update = function() {
var mouse = new PVector(mouseX, mouseY);
var dir = PVector.sub(mouse, this.position);
var maxDir = new PVector(width, height);
var maxMag = maxDir.mag();
var closeness = (maxMag - dir.mag())/maxMag;
dir.normalize();
dir.mult(closeness);
this.acceleration = dir;
this.velocity.add(this.acceleration);
this.velocity.limit(5);
this.position.add(this.velocity);
};(2 votes)