Main content
Computer programming
Course: Computer programming > Unit 5
Lesson 5: Forces- Newton's laws of motion
- Challenge: Floating balloon
- Motion of many objects
- Challenge: Wall balls
- Modeling gravity and friction
- Challenge: Speed bumps
- Air and fluid resistance
- Challenge: Sinking logs
- Gravitational attraction
- Challenge: Artwork generator
- Mutual attraction
- Challenge: Mutual repulsion
- Project: Creature comforts and critter jitters
© 2023 Khan AcademyTerms of usePrivacy PolicyCookie Notice
Motion of many objects
In the real world, the one we're seeking inspiration from here, we have more than one moving object - we're surrounded by many objects of varying mass and position. Let's look at how we can get our
Mover
to better simulate that aspect of the real world.To do this, we’ll need a quick review of object-oriented programming. Again, we’re not covering all the basics of OO programming here. However, since the idea of creating a world filled with objects is pretty fundamental to all the examples in this course, it’s worth taking a moment to walk through the steps of going from one object to many.
As a reminder, this is what our current
Mover
looks like. It is identical to the Mover
object that we created when we first introduced vectors, but with two additions—mass
and a new applyForce()
method:Now that our object is set, we can choose to create, say, twenty
Mover
instances with an array, initializing them with a loopvar movers = [];
for (var i = 0; i < 20; i++) {
movers[i] = new Mover();
}
But now we have a small issue. If we refer back to the
Mover
object’s constructor…var Mover = function() {
this.mass = 1;
this.position = new PVector(30, 30);
this.velocity = new PVector(0, 0);
this.acceleration = new PVector(0, 0);
};
…we discover that every
Mover
object is made exactly the same way. What we want are Mover
objects with varying mass that start at varying positions. Here is where we need to increase the sophistication of our constructor by adding arguments.var Mover = function(m, x, y) {
this.mass = m;
this.position = new PVector(x, y);
this.velocity = new PVector(0, 0);
this.acceleration = new PVector(0, 0);
};
Notice how the mass and position are no longer set to hardcoded numbers, but rather initialized via arguments passed through the constructor. This means we can create a variety of
Mover
objects: big ones, small ones, ones that start on the left side of the screen, ones that start on the right, etc.// A big Mover on the left side of the window
var m1 = new Mover(10, 0, height/2);
// A small Mover on the right side of the window
var m2 = new Mover(0.1, width, height/2);
With an array, however, we want to initialize all of the objects with a loop.
for (var i = 0; i < 20; i++) {
movers[i] = new Mover(random(0.1, 5), 0, 0);
}
For each mover created, the mass is set to a random value between 0.1 and 5, the starting x-position is set to 0, and the starting y-position is set to 0. Certainly, there are all sorts of ways we might choose to initialize the objects; this is just a demonstration of one possibility.
Once the array of objects is declared, created, and initialized, the rest of the code is simple. We run through every object, hand them each the forces in the environment, and enjoy the show.
draw = function() {
background(50, 50, 50);
for (var i = 0; i < movers.length; i++) {
var wind = new PVector(0.01, 0);
var gravity = new PVector(0, 0.1);
movers[i].applyForce(wind);
movers[i].applyForce(gravity);
movers[i].update();
movers[i].display();
movers[i].checkEdges();
}
};
Here's how the program looks, all together. Note how in the program, the smaller circles reach the right of the window faster than the larger ones. This is because of our formula: acceleration = force divided by mass. The larger the mass, the smaller the acceleration.
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 would we check for collisions with the balls against each other? Is there a way to check: if the balls hit each other, then they bounce away from each other? Thanks!(23 votes)
- (I'd imagine it'd be a bit more complicated for an ellipse which isn't a perfect circle)
Lets just say we circles A and B, with their locations being represented as PVector A and B. Lets just say that they also have radius A and B. We'd check the distance between them (dist(A.x, A.y, B.x, B.y)
) and add their radii. If distance <= A+B, we know they have collided. If not, they have not collided. I hope this helps.(27 votes)
- Challenge: Wall balls. Step 1 - Error...Make sure the balls are bouncing in a sensible direction? What am I missing?(16 votes)
- I had a similar problem. The editor liked this.
if (this.position.x > width) {
x = -1;
} else if (this.position.x < 0) {
x = 1;
}
if (this.position.y > height) {
y = -1;
}
else if (this.position.y < 0) {
y = 1;
}(14 votes)
- On the Walls Balls challenge it gives me: "That's a great way to get the balls to bounce back away from the walls! However, in this case, we actually want to generate a force vector that we can apply, whose effects will be combined with the other forces."
I don't understand what else I'm meant to do... I have it working just fine in my eyes...(5 votes)- That worked for me, though it didn't like returning a smaller number for the value, such as 0.01. (I didn't do the 2nd section first, where I'm sure it would be obvious that this was not a good value, but still.)(1 vote)
- I'm on the 2nd part could some1 help me thanks
here's my original code:
var Ball = function(m, x, y) {
this.mass = m;
this.position = new PVector(x, y);
this.velocity = new PVector(0, 0);
this.acceleration = new PVector(0, 0);
this.color = color(random(255), random(255), random(255), 127);
};
Ball.prototype.applyForce = function(force) {
var f = PVector.div(force, this.mass);
this.acceleration.add(f);
};
Ball.prototype.update = function() {
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
this.acceleration.mult(0);
};
Ball.prototype.checkEdges = function() {
if (this.position.x > width) {
this.position.x = width;
this.velocity.x *= -1;
} else if (this.position.x < 0) {
this.velocity.x *= -1;
this.position.x = 0;
}
if (this.position.y > height) {
this.velocity.y *= -1;
this.position.y = height;
}
};
Ball.prototype.display = function() {
stroke(0);
strokeWeight(2);
fill(this.color);
ellipse(this.position.x, this.position.y, this.mass*16, this.mass*16);
};
Ball.prototype.calculateWallForce = function() {
var x = 0;
var y = 0;
if (this.position.x > width) {
x = -1;
} else if (this.position.x < 0) {
x = 1;
}
if (this.position.y > height) {
y = -1;
} else if (this.position.y < 0) {
y = 1;
}
return new PVector(x, y);
};
var balls = [];
for (var i = 0; i < 20; i++) {
balls[i] = new Ball(random(0.1, 5), 0, 0);
}
var draw = function() {
background(255, 255, 255);
for (var i = 0; i < balls.length; i++) {
var wind = new PVector(0.01, 0);
var gravity = new PVector(0, 0.1);
balls[i].applyForce(wind);
balls[i].applyForce(gravity);
balls[i].update();
balls[i].display();
}
};(6 votes) - Having trouble with the second step of Wall Balls challenge. The hint says to calculate the force within the loop but isn't force calculated in the applyForce prototype? What are we supposed to do in this step?(4 votes)
- balls[i].applyForce(balls[i].calculateWallForce()); try that(8 votes)
- In the example above they re-declare
wind
andgravity
inside a loop, insidedraw
.
This seems pretty redundant since there seems to be no reason for doing so. Am I missing something or is it necessary for some other reason?(4 votes)- You are missing nothing. The act is pretty egregious considering the KA bug described here: https://www.khanacademy.org/computer-programming/leak/5149570618228736(3 votes)
- In the examples above, if gravity is a change in the Y position, and wind is a change in the X position, why do we need them as two separate PVectors? Couldn't the examples above just have one PVector that changes both the X (wind) and the Y (gravity)?(4 votes)
- They could, but it wouldn't make much sense for a viewer to see them together.(2 votes)
- In the wall balls challenge what do I have to do to my first and second variable when the balls hit a wall, ceiling or floor.(3 votes)
- For that challenge I found it easier to do the second part first, where you apply the force you calculated to the balls in the draw function. This gave me immediate feedback as to what the values I was changing in the calculateWallForce function was doing to the balls.(3 votes)
- i am stuck on step 2 of wall balls
Here is my code:var Ball = function(m, x, y) {
this.mass = m;
this.position = new PVector(x, y);
this.velocity = new PVector(0, 0);
this.acceleration = new PVector(0, 0);
this.color = color(random(255), random(255), random(255), 127);
};
Ball.prototype.applyForce = function(force) {
var f = PVector.div(force, this.mass);
this.acceleration.add(f);
};
Ball.prototype.update = function() {
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
this.acceleration.mult(0);
};
Ball.prototype.display = function() {
stroke(0);
strokeWeight(2);
fill(this.color);
ellipse(this.position.x, this.position.y, this.mass * 16, this.mass * 16);
};
Ball.prototype.calculateWallForce = function() {
var x = 0;
var y = 0;
if (this.position.x > width) {
x = -1;
} else if (this.position.x < 0) {
x = 1;
}
if (this.position.y > height) {
y = -1;
} else if (this.position.y < 0) {
y =1;
}
return new PVector(x,y);
};
var balls = [];
for (var i = 0; i < 20; i++) {
balls[i] = new Ball(random(0.1, 5), 0, 0);
}
var wind = new PVector(0.01, 0);
var gravity = new PVector(0, 0.1);
draw = function() {
background(255, 255, 255);
for (var i = 0; i < balls.length; i++) {
balls[i].applyForce(wind);
balls[i].applyForce(gravity);
balls[i].update();
balls[i].display();
}
};(4 votes) - In the code snippets Pamela gives, she uses a for-loop to generate 20 balls. Here is where I get confused. She did this:
movers[i] = new Mover(random(0.1, 5), 0, 0);
Whereas I would've done this:movers.push(new Mover(random(0.1, 5), 0, 0));
When I see Pamela's code, it makes me think: won't that override the previous balls in the array?
Is it because she uses[i]
?
Some answers would be appreciated :D(1 vote)var movers = [];
for (var i = 0; i < 20; i++) {
movers[i] = new Mover(random(0.1, 5), 0, 0);
}
The first time, i = 0.
So we add a new Mover tomovers[0]
. The array is now:movers = [new Mover(...)]
.
There is now a mover at the zeroth index.
The second time, i = 1.
So we add a new Mover tomovers[1]
. The array is now:movers = [new Mover(...), new Mover(...)]
.
There are now movers at the zeroth and first indices.
And so on until i = 19. Then i becomes 20 and the loop stops.(6 votes)