Canvas drawing speed

For demo purposes I created Atari’s Pong in Xojo (‘Xong’). For the bouncing ball I have a timer. In the Action event I check where the ball is and if it hits something and changes its path by mutating X and Y. The ball itself is created in the Paint event with G.FillOval.
In the Timer.Action I Invalidate the canvas after recalculating the X and Y of the ball. The ball moves 1 pixel every tick.

But, even if the timer fires every 1/1000 sec, the speed of the ball is rather slow. It varies form very slow (timer interval 50) to slow (interval 1).
What can I do to improve this?

Decouple the ‘update position’ code from the ‘timer has fired’ code

If something is travelling left at 2 pixels per tick ( the speed)
Then in the timer, check how many ticks have elapsed since the last time you checked.
Update the position by (ticks since last time) * 2
Then invalidate

[quote=447287:@Jeff Tullin]Decouple the ‘update position’ code from the ‘timer has fired’ code

If something is travelling left at 2 pixels per tick ( the speed)
Then in the timer, check how many ticks have elapsed since the last time you checked.
Update the position by (ticks since last time) * 2
Then invalidate[/quote]
I think I understand. The ‘Update’ code should be in a loop you reckon?

None of it should be in a loop
Timers are the right place to do stuff.
They just need to be aware that timers do not fire accurately or always when you expect,

To take an extreme example, imagine you want to animate a clock
You have a timer set to fire every minute.

In your existing code, you add a minute to the time, and display the time.
But if the timer is delayed , and doesnt happen for 2 minutes, your clock is wrong and never catches up.
If the timer did not fire for 5 minutes…?

So for the CLOCK your timer would not try to add minutes to ‘last known time’
It would go and get the actual time now, and display that. Even if the minute hand jumped by 5 minutes to catch up.

On your game, you record ‘ticks last time I was painted’ on an object
When you paint it, record the ticks

Next time the event fires, subtract ‘ticks last time I was painted’ from ‘ticks now’, and store ticks now into '‘ticks last time I was painted’ ’
The difference tells you how many ticks have elapsed since the last paint event.

Multiply that by ‘pixels per tick’

The adjust the position of the object by that many pixels.

eg if ticks last time was 6
and pixels per tick is 1
and ticks now is 10
Then the object has travelled 10 - 6 ticks (4) multiplied by 1 = 4 pixels since the last time it was painted.
Draw it in the new place.

The smoothness then depends upon how fast the timer fires.
But the speed of the object is always right

IMHO; When animating, you should always work in time.

Animation = object X, moved from point Y to point Z over TIME.

When you draw a frame to the screen you calculate the point in TIME ( currentTime - startTime ) / duration.
Then you use that value to calculate the difference in position.

object X position = ( point Y * ( 1.0 - TIME ) ) + ( point Z * TIME ).

Hope this helps.

You should check out Part 2 of my Animating Xojo guest post series when it goes live on the Xojo blog soon. I cover using linear interpolation(lerp) to calculate coordinates based on time elapsed. Sam is talking about liner interpolation.

Thanks all. This is a different angle on animation for me.

[quote=447316:@Sam Rowlands]IMHO; When animating, you should always work in time.

Animation = object X, moved from point Y to point Z over TIME.

When you draw a frame to the screen you calculate the point in TIME ( currentTime - startTime ) / duration.
Then you use that value to calculate the difference in position.

object X position = ( point Y * ( 1.0 - TIME ) ) + ( point Z * TIME ).

Hope this helps.[/quote]

That approach requires knowing the duration of the entire displacement between Y to Z, which is not what I would use for Pong.

For Pong I would use the direction and speed of the ball, plus the last position of the ball at the corresponding time. To calculate a new position you just need the time elapsed from the previous one. This approach allows changing the speed of the ball easily.

Let’s say the variables containing those parameters are Speed (in pixels per unit time), Direction (an angle, an integer between 0 and 359 degrees, 0 could be east direction, 90 north, etc.), LastPositionX and LastPositionY, LastTime, NewPositionX, NewPositionY and NewTime.

In your loop, compute ElapsedTime as NewTime-LastTime (in the same time unit as the Speed, microseconds for example). Then:

NewPositionX=LastPositionX+Speed*cos(Direction)ElapsedTime
NewPositionY=LastPositionY+Speed
sin(Direction)*ElapsedTime

Of course you also need to do:
LastPositionX=NewPositionX
LastPositionY=NewPositionY
LastTime=NewTime

You can then call invalidate on your canvas.

But

The human eye is limited to a certain refresh rate (around 25 Hz or FPS), so trying to refresh your ball at 1000 Hz makes no sense (and your screen can’t deliver that refresh rate either). I would start with a timer interval of 20-30 ms (50-33 FPS). In any case, following this approach the speed of the ball is not influenced by the timer interval, and you can adjust it by varying the variable Speed.

HTH,

Julen

@Julen Ibarretxe

I never noticed Pong changing the angle of the ball. It was always 45 deg imho. So my approach:
45 deg angle gives you 4 possible directions, NE, NW, SW, SE. I have a parameter Direction and according to current Direction it reverses while touching a wall:

[code]Const BallDimension = 10
Const Speed = 3

Select Case Direction
Case “SW”
BallX = BallX - Speed
BallY = BallY + Speed

If BallY + BallDimension >= PlayCanvas.Height Then
Direction = “NW”
End If

If BallX + BallDimension/2 <= 0 Then
Direction = “SE”
End If

Case “NW”
BallX = BallX - Speed
BallY = Bally - Speed

If BallX - BallDimension / 2 <= 0 Then
Direction = “NE”
End If

If BallY - BallDimension / 2 <= 0 Then
Direction = “SW”
End If

Case “SE”
BallX = BallX + Speed
BallY = BallY + Speed

If BallY + BallDimension >= PlayCanvas.Height Then
Direction = “NE”
End If

If BallX + BallDimension >= PlayCanvas.Width Then
Direction = “SW”
End If

Case “NE”
'touch side
'touch ceiling

BallX = BallX + Speed
BallY = BallY - Speed
If BallX + BallDimension >= PlayCanvas.Width Then
Direction = “NW”
End If
If BallY + BallDimension <= 0 Then
Direction = “SE”
End If
End Select

PlayCanvas.Invalidate
[/code]

I changed the speed to 3 pixels a tick, which is now ok.

Actually in a real PONG game the angle DOES change… based on where on the paddle the ball hits… .Otherwise the ball would follow pretty much the same path as in your example above.

I didn’t play Pong enough back in the day to remember whether the angle changed or not, but this video shows it did (it is clearly seen around 1min 30-50 secs into it).

Anyway, your Xong, your rules, of course.

Julen

:smiley: like

Pong wall return value: both were used (if my memory is correct). 90° usually, and sometimes another value as well as in the ‘_’ …

And I think it was possible to hit the ball with the ‘-’ cursor mouvement that also change the angle.

But I may be wrong or recall that from a different game.

the angle of reflection is the inverse of the angle of incidence

[quote=447380:@Julen Ibarretxe Uriguen]In your loop, compute ElapsedTime as NewTime-LastTime (in the same time unit as the Speed, microseconds for example). Then:

NewPositionX=LastPositionX+Speed*cos(Direction)ElapsedTime
NewPositionY=LastPositionY+Speed
sin(Direction)*ElapsedTime

Of course you also need to do:
LastPositionX=NewPositionX
LastPositionY=NewPositionY
LastTime=NewTime[/quote]

I looked up Cos() and Sin(), (Cosinus en Sinus) but they give an angle in radians. To get the degrees (0-360) you have to convert them. I will try your approach and let you know. Thanks.

If the angle needs to be in radians you can define Pi as a constant Pi=3.1415… and then:

NewPositionX=LastPositionX+Speedcos(Direction/180Pi)ElapsedTime
NewPositionY=LastPositionY+Speed
sin(Direction/180*Pi)*ElapsedTime

Julen