DelayFree Animation
Author 
Message 
Insectoid

Posted: Wed Jun 12, 2013 10:57 pm Post subject: DelayFree Animation 


Most of the projects that get posted here have at least one delay in them to attempt to regulate the game speed. This may work on your computer but as soon as you try to share it, your timing will be completely messed up due to the different clock speeds. This can be mitigated somewhat with Time.DelaySinceLast, but why not let your computer do something in that spare time instead of waiting? Why design your program around a static framerate when you could just let it run as fast as possible? Instead of waiting to draw the next frame, why not just draw more frames?
This is relatively easy to do, though it does take a little extra work. Most students will assign their objects a static speed maybe 10 pixels per frame. We're going to do the same, except we'll measure our speed in pixels per second. In our game loop, we will calculate how much time has passed since our last frame and then decide how far our object should have moved in that time.
Let's start with a couple variables:
Turing:  var x : real := 50
var y : real := 50
var speed : int := 100 %speed in pixels per second

I'm sure you recognize X and Y, although usually they're integers. In this example, we'll be using reals because our objects can actually move less than a pixel per frame. Our speed in pixels per second is one hundred. It should take us about 4 seconds to move from the bottom of the standard output window to the top, regardless of how fast your computer is.
Now we need a way to decide how much time has elapsed between frames. Turing has a Time.Elapsed function that shows how long the program has been running. If we record the Time.Elapsed in one frame, and then the next frame record it again, we can subtract the first from the second and get the time in between.
Turing:  var timeAtLastFrame : int := 0 % this global variable records the time elapsed in the previous frame
function timeSinceLast : int %this function returns the time elapsed since the last time this function was called.
var t : int := Time.Elapsed %here we get the time since the program started
var out : int := t  timeAtLastFrame %subtract the time at the last frame from the current time
timeSinceAtFrame := t %record the current time so that we have it for the next frame
result out
end timeSinceLast 
Now that we can calculate our time between frames, we can start moving things. In your bogstandard animations, you simply add your speed to your x or y variable: x := x + speed. We do the same thing here, but first we have to multiply the speed by the number of seconds since the last frame. Also, since our speed is in pixels per second and our time is in milliseconds, we need to divide it by one thousand. So to move up, our equation would look like 'y := y + speed * time_since_last_frame / 1000.0'.
Turing:  if keys (KEY_UP_ARROW) then
y + = speed * t / 1000. 0 %t is our time since the last frame
elsif keys (KEY_DOWN_ARROW) then
y  = speed * t / 1000. 0
end if 
Now all that's left to do is put it all together. I've added a variable delay so you can see what it would look like on a slower computer. Press 'w' to increase the delay by 10ms and 's' to decrease it. Use the arrow keys to move around. There's a timer at the top so you can see that the time it takes to move across the screen doesn't change, no matter the delay.
Turing:  %Delayfree animation
View.Set ('offscreenonly')
var x : real := 50
var y : real := 50
var speed : int := 100 %speed in pixels per second
var _delay : int := 0
var _timeSinceLast : int := 0
var t : int
var keys : array char of boolean
function timeSinceLast : int
var t : int := Time.Elapsed
var out : int := t  _timeSinceLast
_timeSinceLast := t
result out
end timeSinceLast
loop
t := timeSinceLast
Input.KeyDown (keys )
if keys (KEY_UP_ARROW) then
y + = speed * t / 1000. 0
elsif keys (KEY_DOWN_ARROW) then
y  = speed * t / 1000. 0
end if
if keys (KEY_LEFT_ARROW) then
x  = speed * t / 1000. 0
elsif keys (KEY_RIGHT_ARROW) then
x + = speed * t / 1000. 0
end if
if keys ('w') then
_delay + = 10
elsif keys ('s') and _delay > 0 then
_delay  = 10
end if
delay (_delay )
cls
locate (1, 1)
put Time.Elapsed/ 1000. 0
Draw.FillOval (round (x ), round (y ), 10, 10, red)
View.Update
end loop 






Sponsor Sponsor



Insectoid

Posted: Wed Jun 12, 2013 11:32 pm Post subject: RE:DelayFree Animation 


You can also apply this to variablevelocity movement models, where the object accelerates instead of moving at a constant speed. In this case, we swap out our speed variable for an acceleration variable measured in pixels per second squared. We also gain X and Y velocity variables measured in pixels per second. This again takes a little more work we need to account for time elapsed when calculating how much we've accelerated in this frame as well as how far we've moved. In this sample, you can press the spacebar to return the ball to the starting position in case you lose it.
Turing:  %Delayfree animation
View.Set ('offscreenonly')
var x : real := 50
var y : real := 50
var velX : real := 0 %velocity in pixels per second
var velY : real := 0
var acceleration : int := 400 %acceleration in pixels per second
var deceleration : int := 200 %deceleration in pixels per second, for when no keys are pressed.
var _delay : int := 0
var _timeSinceLast : int := 0
var t : int
var keys : array char of boolean
function timeSinceLast : int %this hasn't changed
var t : int := Time.Elapsed
var out : int := t  _timeSinceLast
_timeSinceLast := t
result out
end timeSinceLast
loop
t := timeSinceLast
Input.KeyDown (keys )
if keys (KEY_UP_ARROW) then
velY + = acceleration * t / 1000. 0 %here we add our acceleration corrected for time to our velocity
elsif keys (KEY_DOWN_ARROW) then
velY  = acceleration * t / 1000. 0
else %if no key is pressed, we slow down
if velY > 0 then
velY  = deceleration * t / 1000. 0 %reduce velocity if it's positive, again corrected for time
elsif velY < 0 then
velY + = deceleration * t / 1000. 0 %increase velocity if it's negative
end if
end if
if keys (KEY_LEFT_ARROW) then %same thing for the X axis
velX  = acceleration * t / 1000. 0
elsif keys (KEY_RIGHT_ARROW) then
velX + = acceleration * t / 1000. 0
else
if velX > 0 then
velX  = deceleration * t / 1000. 0
elsif velX < 0 then
velX + = deceleration * t / 1000. 0
end if
end if
if keys ('w') then
_delay + = 10
elsif keys ('s') and _delay > 0 then
_delay  = 10
end if
if keys (' ') then %this returns the ball to the starting position when the spacebar is pressed.
x := 50
y := 50
velX := 0
velY := 0
end if
%When decelaration is corrected for time, we often end up bouncing between very small positive and negative velocities, so the ball never stops.
%This corrects it. When the velocity is smaller than the smallest possible deceleration corrected for time, we set it to 0. Using some kinematic
%equations, we could also set the X and Y values to exactly where the ball should be in this time, but that's not important for this demonstration.
if velX < deceleration*t/ 1000 and velX > deceleration*t/ 1000 then
velX := 0
end if
if velY < deceleration*t/ 1000 and velY > deceleration*t/ 1000 then
velY := 0
end if
y + = velY * t / 1000. 0 %Finally, we add the velocity corrected for time to the X and Y values.
x + = velX * t / 1000. 0
delay (_delay )
cls
locate (1, 1)
put Time.Elapsed/ 1000. 0
Draw.FillOval (round (x ), round (y ), 10, 10, red)
View.Update
end loop 






Tony

Posted: Wed Jun 12, 2013 11:44 pm Post subject: RE:DelayFree Animation 


Nice.
A caveat is that this assumes constant speed. Things get somewhat more complicated when one wants to animate say... a jump. (edit: that has already been addressed above)
The basic idea is still the same  figure out where along the expected trajectory the objects should be at time t, and draw them there. All the relevant formulas are available from a standard high school physics textbook.
One has to be careful though then steps average/round intermediate calculations. This shouldn't be a problem when the entire jump's trajectory can be calculated ahead of time, but if it's done step by step (because maybe there's inair movement/collision), then one ends up with things like Quake3 where the frame rate affects the height of the jump  http://www.psycco.de/125fps/UpsetChaps%20Quake3%20Guide%20%20Why%20Your%20Framerate%20Affects%20Jumping.htm 
Tony's programming blog. DWITE  a programming contest. 




Insectoid

Posted: Wed Jun 12, 2013 11:55 pm Post subject: RE:DelayFree Animation 


I made sure only to round when actually drawing the object. All calculations are done on the raw floats, though there may be floating point errors involved.
Actually, I do round it off in deceleration, and this was causing an issue initially at high values of _delay as I mentioned in the comments in the code. I've already fixed that in my own code so the X & Y values are adjusted before setting velocity to 0. 






