CoCraTu 8: Building momentum.
I have a mad scheme for making an object move using applyForce and momentum. )Arrgh, results suggest that we need to file a bug report.)
We have established, more or less, that when we use the applyForce
function on an entity, the force vector provided is applied for an interval equal to one DeltaTime, one draw cycle. There seem to be exceptions to this. I have seen two: one case is that sometimes I see a force of zero applied, and very rarely, I’ve seen double the expected amount.
I plan to ignore these variances, and proceed as if applyForce
applies the force for a period of time DeltaTime.
In real world physics, and fake-world physics as well, objects have momentum. Mathematically,
momentum = mass * velocity
This is often written, for reasons only physicists know, as:
p = m*v
My own theory is:
- Physicist 1
- What shall we call momentum? How about “m”?
- Physicist 2
- Can’t use “m”, that’s mass. How about “n”?
- Physicist 1
- Used in counting one to n. Maybe “o”?
- Physicist 2
- Looks too much like zero. Let’s use “p”.
- Physicist 1
- “p” it is. (They clink glasses and drink.)
Note that momentum is a vector quantity. The momentum of something of mass 2 moving straight up at speed 1 is <0,2,0>
, while if it is moving to the right, momentum is <2,0,0>
.
It turns out, of course, that the momentum of something of mass one, moving at speed two, to the left, is <-2,0,0>
. If those two things collide, the two momentums (momenta?) will cancel out and both objects will stop. The light object can stop the heavy one if it is going fast enough. If it were going even faster, the light object would push the heavy one back and keep on moving, although slowed down.
We saw that in the Michigan-Ohio game as Michigan’s runners pushed through or dragged two or three would-be blockers and tacklers. Go Blue.
Now it turns out, through a bit of math that we won’t explore here, that
impulse = change in momentum
or
F*t = m*Δv
Now applyForce
is an impulse, with time t = DeltaTime (we believe). So it changes the momentum of the object to which it is applied, and we have seen that it mostly changes it by DeltaTime times the force applied. (Our sample ball had mass one, so that we could easily see these figures.)
Netting this all out, suppose we have an object of mass m, moving with vector velocity v
. And suppose we want it to be moving with a new vector velocity, w
. It turns out that if we take the new vector w - v
, and whack the object hard enough with a force in that direction, it will, when all is said and done, be moving with velocity w
.
How hard is hard enough? If I’m not mistaken–which is always a possibility–we need to scale the vector up by the mass, and we need to adjust for the time the force will be applied.
I think we need to hit the object with
force = m*(w-v)/DeltaTime
Let’s try to make that concrete with a Codea program.
Bad News
I’ll spare you the long story of what happened. I’ve spent hours playing with applyForce
and asking questions about it on the forum. I am pretty confident in the program I’ll show below, and I think it shows the following:
- Usually, the amount of force required to reverse the object is 0.5 times the object’s linear velocity, divided by DeltaTime. I believe this is telling me that the physics cycle time is 1/60th of a second, not the draw cycle time, which varies. I think physics time needs not to vary lest things change speed visibly on the screen.
- Sometimes, *on both my fast iPads, every 7 or 8 seconds,
applyForce
, called insidedraw
, has no effect, and for a while thereafter, it has no effect. Then, 7 or 8 seconds later, it again starts to work. - Generally,
applyForce
called indraw
takes effect, subject to the problem #2 above. Generally, usingapplyForce
duringscene:update
does not take effect, but sometimes it does.
Toggling the provided parameter switches the program from applying force in draw
to applying it inside the update
method of the Reporter
, that is, inside scene:update
.
Here’s the program that I plan to submit as a bug report.
-- CoCraTu-008
function setup()
scene = craft.scene()
depth = 0
scene.physics.gravity = vec3(0,0,0)
createBall()
scene.camera:add(OrbitViewer, vec3(0,0,0), 60, 0, 1000)
Delay = 2
BallTime = 0
Whacks = 0
parameter.boolean("ApplyInScene", true)
end
function update(dt)
scene.debug:line(vec3(-10,depth-1,0), vec3(10,depth-1,0), color(255))
scene:update(dt)
end
function draw()
if not ApplyInScene then
reverse("in draw")
end
update(DeltaTime)
scene:draw()
reporter:draw()
end
function reverse(message)
BallTime = BallTime + DeltaTime
if BallTime >= Delay then
local oldv = BallBody.linearVelocity
local newv = - oldv -- turn him around
local mass = BallBodyMass
local force = mass*(newv-oldv)*60 -- guess at physics time = 1/60
BallBody.sleepingAllowed = false
BallBody.awake = true
BallBody:applyForce(force)
Whacks = Whacks + 1
print(message.. " whack ", Whacks, force)
BallTime = 0
end
end
function createBall()
Ball = scene:entity()
BallBody = Ball:add(craft.rigidbody, DYNAMIC, 1) -- mass 1
BallBodyMass = 1
BallBody.linearVelocity = vec3(0,2,0)
BallBody.sleepingAllowed = false
Ball.position = vec3(0,depth,0)
Ball:add(craft.shape.sphere, 1)
Ball.model = craft.model.icosphere(1)
Ball.material = craft.material(asset.builtin.Materials.Standard)
Ball.material.diffuse = color(255,0,0)
reporter = Ball:add(Reporter)
end
Reporter = class()
function Reporter:init(entity)
self.entity = entity
self.lastDrawTime = ElapsedTime
self.lastFixTime = ElapsedTime
self.deltaDrawTime = 0
self.deltaFixTime = 0
self.applyDuration = 0
self.maxDuration = 0
end
function Reporter:update()
if ApplyInScene then
reverse("in scene")
end
self.deltaDrawTime = ElapsedTime - self.lastDrawTime
self.lastDrawTime = ElapsedTime
if self.deltaDrawTime > self.maxDuration then
self.maxDuration = self.deltaDrawTime
end
end
function Reporter:draw()
pushStyle()
textAlign(LEFT)
textMode(LEFT)
fill(255)
text("Draw Time "..self.deltaDrawTime, 400, 100)
text("Vel "..BallBody.linearVelocity.y, 400, 80)
text("Duration "..self.applyDuration, 400, 60)
text("Max Duration "..self.maxDuration, 400, 40)
if BallBody.awake then
text("Awake", 400,20)
else
text("Asleep", 400, 20)
end
popStyle()
end
I’ll add the comments 1,2,3 above as well.
Here’s a movie showing what happens.
So, Therefore … What?
I guess that for now, further demonstration of how to work with momentum in Codea Craft is off the table. We’ll probably move on to the voxel capability in upcoming articles.
When Simeon and John show me the error of my ways, or fix the problem, we might return to this topic. I think there’s some interesting stuff we could do with applying force and torque in a game context.
But for now, the bear has bitten us.