After discussing the matter with Ric on the DBPro board, we discovered that time based physics gets rather complicated when you get past damping movements of objects (even that took a while to figure out), so I set of to find a better solution, and found this. RK4 integration. Although it sounds quite complicated, it isn't actually that hard to impliment (though understanding it is another matter, I certainly don't
).
It works by taking four measurements, using the integration function included in the code, and weighing the values to make a good estimate of what the position and velocity of the object will be after dt time. The great thing about it is that the movement is all based on forces, which can all be calculated in one line. If you write...
accel = 10
It will handle it for you, and make the object accelerate at 10 units. If you write...
accel = -vel*damping
It will handle it. If you write...
accel = -pos*spring - vel*dampings - force
It will handle it. I have even included air resistance in the demo, like this...
accel = -pos*spring - vel^2*airRes*vel/abs(vel) - vel*damping - force
It handles it very well, so long as the frame rate is above around 10. My demo is in one dimension, but two dimensions could be built in with a bit of tweeking. here is the code, press space to apply a force left...
remstart
===================================
RK4 Integration Demo
29/12/04
Converted and Edited by Joseph Thomson
===================================
Original code copyright info...
===================================
Simple RK4 integration framework
Copyright (c) 2004, Glenn Fiedler
http://www.gaffer.org/articles
===================================
This demo shows how to use the RK4 integration technique for game physics, undoubtedly
it will have to be edited and re-arranged to suit your purpose.
RK4 integration is a technique used in game physics, it takes any operation you are doing
to an object (eg. springs, damping, air resistance, friction, forces) and makes a best
estimate about what state it will be in (position and velocity) after a set time (dt).
You can use this by making dt your loop time, then the physics will always be the same,
no matter how fast your game loop is. Unfortunately, due to fact that the calculations
are estimates, the calculations are really only reliable over 10fps (my estimate), dt
time calculations should be limited to the 10fps, or more, equivilent, this way, if the fps
go below 10, the simulation will run more slowly, but won't explode.
Please visit...
http://www.gaffer.org:8080/articles/Integration.html
...for more information
===================================
remend
`Create constants to refer to vectors by
#constant Vstate 1
#constant Va 2
#constant Vb 3
#constant Vc 4
#constant Vd 5
#constant Vempty 6
#constant Vspare 7
`Create vectors. Vectors in this example will NOT be used for x and y space coordianates,
`instead they are used for position and velocity in one dimension (ie. the state of the object)
tmp = make vector2(Vstate)
tmp = make vector2(Va)
tmp = make vector2(Vb)
tmp = make vector2(Vc)
tmp = make vector2(Vd)
tmp = make vector2(Vempty)
tmp = make vector2(Vspare)
`Main loop
main()
end
`This function will integrate the state of the object using dt, with the RK4 method
`t is not used in this example, but could be used if you want to affect the object
`based on the time
function integrate(state as integer, t as float, dt as float)
local dxdt as float
local dvdt as float
`RK4 requires four integrations, with these preset values, each integration uses the result from the one
`before it (which is the velocity as acceleration, since integrating displacement and velocity gets
`velocity and acceleration respectively). Each one also uses state, which is integrated at each step
evaluate(Va, state, t, 0, Vempty)
evaluate(Vb, state, t, dt*0.5, Va)
evaluate(Vc, state, t, dt*0.5, Vb)
evaluate(Vd, state, t, dt, Vc)
`This bit is just another part of RK4 integration, it weighs the four results, and does an estimate of
`the average velocity and acceleration
dxdt = 1.0/6.0 * (x vector2(Va) + 2.0*(x vector2(Vb) + x vector2(Vc)) + x vector2(Vd))
dvdt = 1.0/6.0 * (y vector2(Va) + 2.0*(y vector2(Vb) + y vector2(Vc)) + y vector2(Vd))
`Change the state displacement and velocity using the estimated velocity and acceleration for this time step
set vector2 state, x vector2(state) + dxdt*dt, y vector2(state) + dvdt*dt
endfunction
function evaluate(Vout as integer, initial as integer, t as float, dt as float, Vin as integer)
`First, simply integrate the position and velocity over dt, using the velocity and acceleration passed in
set vector2 Vspare, x vector2(initial) + x vector2(Vin)*dt, y vector2(initial) + y vector2(Vin)*dt
`Now calculate the velocity and acceleration. Velocity is simple, but acceleration is worked from another function
set vector2 Vout, y vector2(Vspare), acceleration(Vspare, t+dt)
endfunction
`This function requires on dt or integration, it is simply what you want to do to your object
function acceleration(state as integer, t as float)
spring as float = 0.02
damping as float = 0.001
airRes as float = 0.002
force as float = 0.0
result as float
`You can make the object go left when you press this
if spacekey() then force = -4.0
`The acceleration is increased when the displacement from the origin get higher (spring force)
`it is increased when the velocity increases (damping)
`it is increased when the velocity increases, by a factor of vel^2 (air resistance)
`it also has a constant added to it
result = -spring*x vector2(state) - damping*y vector2(state) - airRes*y vector2(state)^2*(y vector2(state)/abs(y vector2(state))) + force
endfunction result
function main()
local t as float = 0.0
local dt as float
local time as integer = 10
local syncRate as float
local loopSpeed as float
local lastLoopTime as integer
local loopTime as integer
local loopConstant as float
local dt as float
`Initial dt variables
syncRate = 60.0
loopSpeed = 60.0
loopConstant = loopSpeed/1000.0
sync on
sync rate syncRate
`Set the position and velocity of the object
set vector2 Vstate, 200.0, 0.0
lastLoopTime = timer()
do
`Change the frame rate with up and down keys
if upkey()
inc syncRate,0.5*dt
sync rate syncRate
endif
if downkey()
dec syncRate,0.5*dt
if syncRate < 10.0 then syncRate = 10.0
sync rate syncRate
endif
`Work out dt variable
loopTime = timer() - lastLoopTime
lastLoopTime = timer()
dt = loopTime * loopConstant
`Draw the circle and integrate the state
integrate(Vstate, t, dt)
circle x vector2(Vstate)+320,240,30
inc t,dt
`info
text 0,0,"Set Frame Rate: " + str$(syncRate)
text 0,20,"Actual Frame Rate: " + str$(screen fps())
sync
cls
loop
wait key
endfunction
Isn't it? Wasn't it? Marvellous!