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:

  1. 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.
  2. Sometimes, *on both my fast iPads, every 7 or 8 seconds, applyForce, called inside draw, has no effect, and for a while thereafter, it has no effect. Then, 7 or 8 seconds later, it again starts to work.
  3. Generally, applyForce called in draw takes effect, subject to the problem #2 above. Generally, using applyForce during scene: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.

ball

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.