字幕列表 影片播放
[BELL RINGS]
Hello and welcome to chapter two of the Nature of Code, forces.
So I want to start with this discussion
by talking about Newton's laws of motion.
So by no means is anything I'm going
to do be a really accurate, robust physics simulation.
But at a minimum, I want to examine
what the basic laws of motion are, the basic principles
behind how things move, and just make sure
that the code I'm writing fits with that for the most part.
So let's start with those laws of motion.
Law number one.
This is how it is often referred to most informally--
an object at rest stays at rest.
However, a more accurate statement of this
might be, an object at rest stays at rest
and an object in motion stays in motion at a constant speed
and direction unless acted upon by an unbalanced force.
So that's a bit of a mouthful of stuff,
but let's unpack that for a moment.
Let's think about this in the context of our P5 canvas
and the object being a circle that I'm
drawing on that canvas.
If this circle is going to obey Newton's first law,
if it's not moving, there's no reason for it to start moving,
or if it is moving at a particular velocity vector,
there's no reason for that velocity vector
to ever change unless acted upon by a force.
The definition that I offered over here
uses the term unbalanced force.
So something that's very important about this concept
of staying at rest or staying at a constant velocity
is that it's not just whether the force is there or not,
but whether the net force, the sum
of all the forces in the environments,
add up to something non-zero.
For example, with my marker here, there's
a force of gravity pulling it down, presumably
towards the center of the earth, but there's also
some other forces that I'm applying to it to keep it up,
that are pushing it up.
And so if I try my best to balance the force--
balance the force-- then it won't move at all.
This diagram is another example of this.
So all of these forces balance themselves out,
and this object is at a state of equilibrium,
meaning it won't start moving, or if it
were, its velocity would remain constant.
And the reason I'm harping on this
so much is I really want to make sure
when I go to write the next bits of code
that I'm going to write, that the simulation obeys this law.
But in order for it to obey this law in the first place,
I have got to understand--
I'm using the term force all over the place.
It's really important that I think I take a moment now
to define what I mean by force.
A force is a vector that causes an object with mass
to accelerate.
That is the technical definition that I
am going to use for force.
Here's the good news.
Vector-- check.
We've got that.
I just did a whole section of videos about what is a vector,
so we can understand force as a vector.
Here's more good news for you.
Accelerate-- check.
I just did a video tutorial about how to apply acceleration
in a P5 sketch.
So if we can create a vector and somehow
have that vector affect the object's acceleration,
then we are implementing a force.
And if the way that we implement the force in our code
obeys this law, then we are golden.
One thing here-- mass is not something
that I have discussed, or used, or implemented
as a property of any of the things I've done so far.
So let's put a question mark there and return to that.
Which leads me to Newton's second law,
F equals M times A, or Force equals Mass times Acceleration.
I can also describe this law as a restatement
of the definition of force.
Force is a vector that causes an object with mass to accelerate.
We're turning back to the code for a moment.
I have these two lines of code--
vel.add(this.acc), pos.add(this.vel).
And in this example, everything starts with acceleration.
I'm calculating an acceleration based
on a vector that points from the object moving
to the mouse itself.
So if everything starts with acceleration,
maybe I need to rethink the way that I'm
describing this formula.
And a different way that I could consider it is acceleration
equals force divided by mass.
The idea being that if I introduce some force--
like let's say there is a wind force
with a magnitude and direction that I want
to put into this environment.
If I have that force, I could take that force, divide it
by this object's mass, and that would become the object's
acceleration, which would then affect its velocity, which
would then affect its position.
And this is the technique, the strategy by which everything
I do is going to be built upon.
I'm really ready to start implementing this in code.
But before I do so, there's a few things I want to address.
One is you might hear, when looking
at how physics engines are built, the term integration.
And this is actually one way of doing integration.
This specific way is referred to as Euler integration.
Why is it called integration?
I'm getting into the weeds here into the math aspect of this,
but it is important, and I think this will help you
as you move forward and do more of this stuff
when you see the lingo popping up here and there.
In calculus, there's the concept of a derivative.
And you might typically see this as written as like dx
over dt, the change in x with the change in time.
This is actually what velocity is doing.
Velocity is dx dt.
If x is position, velocity is the change
in position over time.
Acceleration is the change in velocity over time.
So velocity can be thought of as the derivative of position,
and acceleration can be thought of as the derivative
of velocity.
But I'm not going in that direction.
I'm not starting with position, and then
figuring out what's the velocity,
and then figuring out what's the acceleration.
I'm going in the other way.
I'm starting with what's the acceleration,
and then changing the velocity, and then taking the velocity
and changing the position.
So the other direction of doing the derivative
is integration, the integral.
So that's why this process is tied to calculus
and the concept of integration.
The big difference here is that calculus,
being the study, in a way, of infinitesimally small time
steps, these time steps are quite large.
This algorithm is just being applied 30 times per second.
But as I move my arm back and forth,
are you seeing this animation at 30 frames per second?
Maybe because you're watching a video, which
is then recording me and playing it back at 30 frames a second.
But the actual physical time space that I'm in
is very continuous.
The time step is infinitesimally small.
And if I were doing an accurate calculation of a physics
simulation, I would need to take that under consideration.
Instead, I'm using a technique called Euler integration, which
allows me to have fixed time steps, which are rather large,
and it's wildly inaccurate.
And I'll maybe back circle back to this
as I look in future videos about other integration techniques.
There's Berlet physics, and there's
Runge Kutta integration.
I probably didn't say that correctly.
And so maybe I could circle back when
we look at other physics libraries that
might have slightly more robust methodologies
underneath the hood.
But the reason why I like this methodology is, number one,
it works just fine.
I just want my particles to spiral around the screen
in pretty ways.
And two, it's really fast and really easy to implement.
So it has its advantages in terms
of the kinds of examples and demonstrations
that I want to show you.
Also, I know there's so much more to this.
Before you start typing your comments in the chat about
the theory of relativity, and space-time continuum,
and Planck time, and also "Plonk" time--
I don't even know how to say it--
my goal here is not to go in-depth
into the physics of this, and I'm
glad to include in this video's description
other resources that do, but to take
these ideas, these core classic concepts,
and then work out a way to implement them in code.
I should also mention there is Newton's third law, which
is often stated as, for every action,
there is an equal and opposite reaction.
That's a little bit misleading, and I've written
some thoughts about that here.
Another way of stating that law is,
forces always occur in pairs.
The two forces are of equal strength
but in opposite directions.
So to be honest, this law is not so meaningful to me right now.
Trust me, it's kind of important.
But in terms of the simulation that I want to start with,
I am not going to bother to simulate
every possible reaction and opposite
interaction of every force within the system.
But this will come up again, and I will look at this law
when, in a future video as part of this section,
I look at gravitational attraction
and how two bodies both attract each other,
and those forces are of equal strength
but in the opposite direction.
To get started implementing Newton's second law in code,
I want to build off of the example
that I finished with in the previous section on vectors.
So here, I have this mover object.
It starts at a position 200, 200,
and two functions are called, update() and show().
So if I run it, the acceleration is
being calculated as a vector pointing towards the mouse.
You could think of it as a force,
a gravitational attraction force pulling into the mouse.
It's not really that.
Of course, it's not really that.
It's just some pixels on a screen.
But I'm not even using the formula
for gravitational attraction.
So I'm going to circle back to that
and make some changes to this kind of idea later.
But right now, what I want to do is change the way the internals
of the mover object works because right now,
the acceleration is being calculated right here
in update(), and I don't want to do that.
Instead, I want to write a function that's
basically Newton's second law.
So I want to write a function--
I'm going to change its name in a moment--
that implements this particular law.
The law, again, is force equals mass times acceleration.
But I've rewritten it to acceleration equals
force divided by mass.
Guess what?
Let's just pretend for a moment that the world we live in,
the world that I'm programming in, there's no concept of mass.
In fact, everything in this entire universe
has a mass of 1.
So in that case, going back to this law of force
equals mass, which is 1, 1 times acceleration
is really just same as force equals acceleration
or acceleration equals force.
So these are the same concepts.
Meaning, I could just say this.acc = force.
There we go.
This is the simplified version of Newton's second law in code.
I'm going to change the name to applyForce(),
the idea being that what I want to do is take a force that
exists in the environment, not something calculated from
within the object.
That's going to be important later in some
of the other videos I'll do.
But right now, imagine a wind force
external to the object, or a gravitational pull
maybe pulling it down along the y-axis external to the object.
For example, let gravity equal createVector(0, 1).
I'm making a vector that points down,
and then I want to say mover.applyForce(gravity).
Let's run this code.
Remember, this is the applyForce() function.
It sets the acceleration equal to the force,
and then that acceleration is added to velocity
and that velocity is added to position.
Ooh.
[BUZZER]
I forgot that I'm programming in a class, and in a class,
I don't use the keyword function to define an instance method
of that class, I just say applyForce().
Ugh.
There we go.
Look at that.
There's that force of gravity pulling it down.
This sketch is a little bit sad because the ball falls
and it never comes back.
So just for the purpose of demonstration,
I'm going to add an artificial constraint
to this particular sketch, where what
I'm going to do is I'm going to look at this bottom edge,
and if this circle reaches the bottom edge,
I'm going to take its velocity and reverse it.
Really, if I were doing a physics simulation,
I would think about this as an object with a different mass,
and what kind of forces are happening
when it hits that object.
But I'm going to just bypass all of that with a quick shortcut.
I can come back and look at that a little later, maybe.
But I'm going to bypass that with a quick shortcut
of just inverting the velocity.
And I'm going to do that by adding a function called edges
to keep track of when the object hits an edge.
So this is really your classic bouncing ball code,
and I have another video that goes through this algorithm
more specifically.
But what I'm looking for is anytime that y position
is at the bottom of the canvas or below--
because testing if it's equal to height
is not going to do me any good.
If it's not precisely equal, it could easily move past--
then I'm going to shove it back up to height
because if it goes too far and it turns around,
it could get stuck.
So I'm going to make sure even if it goes past height,
I set its position to height and then reverse its velocity.
And let's take a look.
Ooh, that didn't work.
Why didn't it work?
It's one thing to write a function.
It's another thing to call it.
So let's say mover.edges().
And there we are.
So now this gravitational force is pulling the object down
and it's bouncing up or down.
Why is it going off to the right a little bit?
It's going off to the right because I gave it
an initial random velocity.
Interestingly, you notice how it's
slowing down and eventually kind of losing its bounce
and stopping?
In a weird sort of way, that's what I wanted to do.
That feels realistic.
But I can't see in the code why it's doing that because really,
I'm reversing its velocity.
There's nothing causing it.
It should go all the way back up to the top,
and then all the way back down there, then all the way
back up to the top.
And this, I think, is because of this Euler integration.
I'm not looking at continuous time.
And there's a lot of inaccuracies here.
Not to mention the fact that I'm violating
all of the principles of time and space
by just resetting its position back to the height
if it got below the height, so it's
losing some time where it might regain its velocity moving back
up.
So there's all sorts of goofy stuff going on here.
And these are the kinds of things
that as a programmer, working with this
in a creative context, ultimately,
I'm going to need to do massaging and tweaking
things to get it to feel right, whatever
the types of application it is that I'm wanting to create.
But right now, I just want to test if Newton's second law is
at play.
And I'm going to test that by doing this.
Only if the mouse is pressed, apply this force of gravity,
meaning the net force is currently 0
so the object is at rest.
Now I am going to apply the force.
I'm going to let go.
Oh.
[FAKE SOBS]
[BELL DINGS]
I'm applying gravity, but when I let go,
I think the gravity should stop being applied because I'm
no longer pressing the mouse.
But the gravity still seems to be there.
Why is that?
Well, there's a flaw in the way that I programmed this.
The flaw is that I'm setting the acceleration equal to force.
If the force goes away, I don't set the acceleration back to 0,
but I should.
So one of the strange things about this Euler integration
and algorithm is that while velocity and position are
accumulating over time, acceleration
is just a function, a calculation based on,
at any given moment in time, what
are the forces in the environment?
So at any given time, we should start
with an acceleration of 0.
And there only is an acceleration
if there is a non-0 force.
So there's a variety of ways that I could handle this.
I'm going to just, I think, for simplicity, say this.acc.set(0,
0).
So this is me just clearing the force out
at the end of every animation cycle.
So now, let's see if Newton's second law--
sorry, first law-- is really at play, and the second one.
Oops.
Cannot read properties set of undefined because I
haven't given acceleration of value until I've applied
the force, so this is just a flaw in how I've written
the constructor.
I also need to say, initialize a starting acceleration of 0.
Now here we go.
The object is staying at rest.
There's no force.
I click the mouse.
Whoop.
I let go of the mouse and it just flew off.
Let's make the gravity less strong.
I'm going to change this to 0.1.
I'm holding down the mouse.
There's gravity.
I let go.
The gravity is gone.
The object keeps its constant velocity
because there's no force acting on it.
Let's try also now adding a second force
into the environment.
So there's one force pointing straight down called gravity.
Let's add another force called wind and point it to the right.
Let's get rid of the if (mouseIsPressed).
I'll come back to that in a little bit.
So now I've got gravity pointing down
and wind pointing to the right.
Of course, I could have just made one force
with this as the x component and this as the y component.
But I'm demonstrating an idea that as you start
to build these kinds of simulations,
there might be a variety of forces that are at play or not
at play, based on any number of factors,
and you can now separate those out into different vectors
and individually apply them to your object.
Let's run this sketch and see what happens.
Wait a second.
This I anticipated-- this error.
Why is gravity not being applied?
I only see wind.
Let's change the order here.
Let's put wind--
I don't know.
Let's move the code around a little bit.
Let's declare wind and gravity there, but now
apply them like this.
I don't know.
Is that going to do anything?
Wait.
Now, only gravity is being applied, but not the wind.
Go back into the mover.
What's wrong?
this.acc = force.
So stepping through the code, mover.applyForce(wind).
this.acc = wind.
move.applyForce(gravity).
this.acc = gravity.
I just overwrote the wind.
The wind is ignored because I'd take gravity and put it
into acceleration.
As I was diagramming this and describing it,
I think a couple of times, I used this term net force,
or the sum of all forces, or this idea
of force accumulation.
Meaning the object's acceleration
is a result of the sum of all of the forces in its environment
at any given time.
Wind and gravity both act on the object,
and both get added into the acceleration.
So this applyForce() function shouldn't actually set
acceleration equal to force, but rather it should call add.
this.acc.add(force).
I can call applyForce() as many times as I want and add up all
the forces into the acceleration.
There we go.
The visual result of that isn't super interesting.
Let's make it a little more interesting by saying--
and I'll make the gravity a little stronger.
So now the wind will only be applied if I click the mouse,
but gravity will always be applied.
Let's see if this works.
There's the ball bouncing.
And now I'm going to click the mouse and the wind-- oh.
Let's add some more edges.
So now I've added a check for the right and left edges,
and make sure the x slides back onto the edge,
and the velocity is reversed.
We have probably more elegant, and efficient, and shorter way
to write this, but I'm just putting it in there
to get it to work.
Let's see how the forces are going.
Gravity.
And now I'm going to click the mouse, and wind is happening.
You can see that.
The wind is pushing it in that direction.
But let's let go of the mouse and now the wind
is no longer there and it's bouncing back and forth.
The x velocity is constant now because there's no x component
to the net force.
But I could add the wind back in and we can see now it's
pushing it to the side.
I could also just increase the relative strength
of these forces, just to see what that looks like.
And we can see that here.
And we see I already have a pretty interesting
and dynamic simulation going.
This is a small detail and not super important,
in terms of what I'm doing, but it
would be nice to actually have the ball reverse
its course when the edge of the circle
hits the edge of the canvas, rather
than the center of the circle.
So in that sense, I could add another variable for r
to keep track of the object's radius.
So I'm going to say this.r = 16, and then draw the ellipsis,
this.r * 2, and then use this.r to determine where the edge is.
Let me have it move a little slower so this is hopefully
a little more visible.
Ooh.
Whoa.
I forgot something.
I've got to set it back to this.r.
There we go.
So that's a little bit more realistic in terms
of where it's actually bouncing in terms of hitting the edge.
So this is a good stopping point for this example,
I think there's of twists you could do on this.
First of all, I've just hardcoded numbers here
for wind and gravity.
There might be other ways you think about calculating those.
For example, you could actually pull from a weather API
and get the actual wind and have that affect this object.
Or you could use Perlin noise.
Maybe these vectors are changing over time
for some other reason, or there's more than two forces.
So many ways you could go in this direction,
but this is a bit of a foundation,
upon which you could build a simple interactive simulation.
There's a couple of different things
that I want to do with this example, which
I'll do in separate videos.
Number one is there's no concept of mass here.
So what would it mean if I added to the mover object
a mass property, and how would I use that mass value
and actually bring that back into the true Newton's
second law, force equals mass times acceleration?
So I want to bring back mass.
Bring back mass.
Then I also want to investigate other forces
that I can look up a real-world formula for
and see if I can implement those.
And the first one I'll do is friction.
For example, there's this thing happening
where the ball comes to a resting point,
but then it just rolls back and forth.
Could I simulate the friction of it against the edge
and have it slow down and reach a stopping point?
So the next video, I'll start with math, and then I'll
look at friction, and keep going from there,
finishing up with looking at the real formula
for gravitational attraction and how
I might implement that in code.
So maybe I'll see you, maybe I won't, and go on with your day.
May the force be with you.
I said it.
I said it.
I said it.
Goodbye.
[MUSIC PLAYING]