Synchronize a graphical display and a game logic running at different intervals is (as far as I know) almost impossible!
Smooth animation / sprite movement
- Kaeru Gaman
- Addict

- Posts: 4826
- Joined: Sun Mar 19, 2006 1:57 pm
- Location: Germany
I think it's simpler to keep everything in the main program to have accurate values to display. If your graphic display occurs when you're computing, let's say, a collision check, you'll display something the user don't want to see. Or you can try to display only finished operations, but then, you'll be never synced, and you'll have to copy every positions to have the "in calculation" ones, and the final ones.
- Kaeru Gaman
- Addict

- Posts: 4826
- Joined: Sun Mar 19, 2006 1:57 pm
- Location: Germany
Fractional frame based. The one thing missing is detection of the (natural) framerate. But it shows the idea... Shouldn't be too hard to turn it into a full fledged asteroids...
Edit: found how to check for the natural framerate. Changed the code a little and updated it here.
Edit: found how to check for the natural framerate. Changed the code a little and updated it here.
Code: Select all
Enumeration
#w_main
;
#f_exit
#f_none
;
#i_arrow ; image used for player
;
EndEnumeration
;
Global i_arrow_h.l
;
Structure spr
alive.l ; set to #false to disable this sprite
sprite2d_nr.l ; 2d sprite used
sprite3d_nr.l ; 3d sprite used
a.f ; object orientation
rotation.f ; rotation speed in degrees / second, does not affect vector
z.f ; object size factor (1 = original size)
x.f ; x-coordinate
y.f ; y-coordinate
w.l ; width in pixels
h.l ; height in pixels
v_angle.f ; vector angle in degrees
v_speed.f ; speed in pixels per second
v_mode.l ; #true to use vector, #false to use x / y components
v_x.f ; horizontal speed in pixels per second
v_y.f ; same vertical
EndStructure
;
InitSprite()
InitSprite3D()
InitKeyboard()
;
ExamineDesktops()
framerate = DesktopFrequency(0)
framelength.f = 1 / framerate ; time per frame in seconds
; Debug framerate
;
screen_w.l = 1280 ; screen width
screen_h.l = 1024 ; screen height
screen_z.l = 32 ; color depth
OpenScreen(screen_w,screen_h,screen_z,"SpriteEngine")
SetFrameRate(framerate)
;
; sorry, no luck with windowed screens yet...
;
; screen_w.l = 800
; screen_h.l = 600
; screen_z.l = 32
; w_main_h.l = OpenWindow(#w_main,10,10,screen_w,screen_h,"SpriteEngine",#PB_Window_ScreenCentered)
; OpenWindowedScreen(w_main_h,0,0,screen_w,screen_h,0,0,0)
; SetFrameRate(framerate)
;
Sprite3DQuality(0)
;
; player sprite
;
#spr_player = 0
;
i_arrow_h = CreateImage(#i_arrow,64,64,32)
StartDrawing(ImageOutput(#i_arrow))
Box(0,0,63,63,RGB(0,0,0))
FrontColor(RGB(0,255,0))
LineXY(4,60,32,4)
LineXY(32,4,60,60)
LineXY(60,60,32,16)
LineXY(32,16,4,60)
StopDrawing()
;
CreateSprite(#spr_player,64,64,#PB_Sprite_Texture)
StartDrawing(SpriteOutput(#spr_player))
DrawImage(i_arrow_h,0,0)
StopDrawing()
CreateSprite3D(#spr_player,#spr_player)
;
#spr_max = 10
Dim spr.spr(#spr_max)
;
With spr(0)
\alive = #True
\sprite2d_nr = #spr_player
\sprite3d_nr = #spr_player
\a = 0
\rotation = 0
\z = 1
\x = screen_w / 2
\y = screen_h / 2
\w = SpriteWidth( \sprite2d_nr)
\h = SpriteHeight( \sprite2d_nr)
\v_angle = 0
\v_speed = 0
\v_mode.l = #False
\v_x.f = 0
\v_y.f = 0
EndWith
;
spr_number.l = 1 ; number of sprites to process
;
action = #f_none
Repeat
;
; calculate movement etc.
;
n = 0
While n < spr_number
With spr(n)
If \alive
\a = \a + \rotation
RotateSprite3D( \sprite3d_nr , \a , 0)
If \v_mode
\v_x = \v_speed * Sin( \v_angle / 180 * #PI )
\v_y = 0 - \v_speed * Cos( \v_angle / 180 * #PI )
EndIf
\x = \x + \v_x * framelength
\y = \y + \v_y * framelength
;
; wrap outside screen borders
;
If \x > screen_w + \w
\x = 0 - \w
ElseIf \x < 0 - \w
\x = screen_w + \w
EndIf
If \y > screen_h + \h
\y = 0 - \h
ElseIf \y < 0 - \h
\y = screen_h + \h
EndIf
;
EndIf
EndWith
n = n+1
Wend
;
; display all active sprites
;
ClearScreen(0)
Start3D()
n = 0
While n < spr_number
With spr(n)
If \alive
DisplaySprite3D( \sprite3d_nr , \x , \y , 255)
EndIf
EndWith
n = n+1
Wend
Stop3D()
;
FlipBuffers(#PB_Screen_WaitSynchronization)
;
ExamineKeyboard()
If KeyboardPushed(#PB_Key_Escape)
action = #f_exit
EndIf
With spr(0)
;
; move the player object using A / W / S / D / UP / DN / LFT / RGHT
;
If KeyboardPushed(#PB_Key_Left)
\rotation = \rotation - 10 * framelength ; with inertia
; \a = \a - 300 * framelength ; without inertia
ElseIf KeyboardPushed(#PB_Key_Right)
\rotation = \rotation + 10 * framelength
; \a = \a + 300 * framelength
ElseIf KeyboardPushed(#PB_Key_Up)
\v_x = \v_x + 500 * Sin( \a / 180 * #PI ) * framelength
\v_y = \v_y - 500 * Cos( \a / 180 * #PI ) * framelength
ElseIf KeyboardPushed(#PB_Key_Down)
\v_x = \v_x - 100 * Sin( \a / 180 * #PI ) * framelength
\v_y = \v_y + 100 * Cos( \a / 180 * #PI ) * framelength
ElseIf KeyboardPushed(#PB_Key_A)
; \x = \x - 100 * framelength ; without inertia
\v_x = \v_x - 100 * framelength ; with inertia
ElseIf KeyboardPushed(#PB_Key_D)
; \x = \x + 100 * framelength
\v_x = \v_x + 100 * framelength
ElseIf KeyboardPushed(#PB_Key_W)
; \y = \y - 100 * framelength
\v_y = \v_y - 100 * framelength
ElseIf KeyboardPushed(#PB_Key_S)
; \y = \y + 100 * framelength
\v_y = \v_y + 100 * framelength
EndIf
EndWith
;
Until action = #f_exit
CloseScreen()
Last edited by blueznl on Thu Aug 28, 2008 9:34 pm, edited 1 time in total.
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB - upgrade incoming...)
( The path to enlightenment and the PureBasic Survival Guide right here... )
( The path to enlightenment and the PureBasic Survival Guide right here... )
It's exactly why it can't work without a major headache... If you just have a linear move, and a constant frame rate, it's not difficult to obtain the right fractional step : if you have 3 steps just multiply by 3. But for others movements, user based, or following non linear curves, how do you obtain the new (and right!) position by calculus?blueznl wrote:The one thing missing is detection of the (natural) framerate.
... by thinking differently, and IGNORING the actual framerate.
Give every object a speed and an angle, or a horizontal and a vertical speed, for example in pixels per second.
Then add a fraction of the moved distance every frame to the position of the object.
That's what I do in the code above. Change the FlipBuffers() parameter above to 'no wait' and play around with the framerate, the objects should still move in the same speed, regardless of the framerate you set.
For everyframe:
newx = oldx + distancetomovepersocond / framespersecond
Give every object a speed and an angle, or a horizontal and a vertical speed, for example in pixels per second.
Then add a fraction of the moved distance every frame to the position of the object.
That's what I do in the code above. Change the FlipBuffers() parameter above to 'no wait' and play around with the framerate, the objects should still move in the same speed, regardless of the framerate you set.
For everyframe:
newx = oldx + distancetomovepersocond / framespersecond
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB - upgrade incoming...)
( The path to enlightenment and the PureBasic Survival Guide right here... )
( The path to enlightenment and the PureBasic Survival Guide right here... )
Do am I the only one to see that if you have a variable frame rate, if the first time the user press a key he's moving by two pixels, and the second time he's moving by four pixels, it will not make sense to him? When you press one time a key, you expect to have the very same effect, whatever is the frame rate!
Maybe I'm wrong. I've tried to modify your code to make a ball bouncing with varying frame rate to demonstrate how difficult it could be. But really it is!
Maybe I'm wrong. I've tried to modify your code to make a ball bouncing with varying frame rate to demonstrate how difficult it could be. But really it is!
The code actually is not using a variable framerate
It's 'fractional' ie. IF it would be using another framerate, the effect of pressing a key would be different.
But framerates do not change on a machine (except when you miss a frame because your processing takes too much time) when you tie in your code to the vertical sync with FlipBuffers()
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB - upgrade incoming...)
( The path to enlightenment and the PureBasic Survival Guide right here... )
( The path to enlightenment and the PureBasic Survival Guide right here... )
No, it's not what I want to avoid 
Actually, I'm just trying to find two solutiosn:
1. how to eat up less CPU time with FlipBuffers() (which inherently would also allow multi threading / task seperation)
and
2. how to get smooth anymation in a window
Anything else is a report of all my stumbling during the journey
Actually, I'm just trying to find two solutiosn:
1. how to eat up less CPU time with FlipBuffers() (which inherently would also allow multi threading / task seperation)
and
2. how to get smooth anymation in a window
Anything else is a report of all my stumbling during the journey
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB - upgrade incoming...)
( The path to enlightenment and the PureBasic Survival Guide right here... )
( The path to enlightenment and the PureBasic Survival Guide right here... )
- Kaeru Gaman
- Addict

- Posts: 4826
- Joined: Sun Mar 19, 2006 1:57 pm
- Location: Germany
Kaeru, what sample?
Oh, by the way, I updated the code above a little (to detect the framerate). The next challenge is properly detect if a user Alt+Tab's out of the game. Anyone a good suggestion?
Oh, by the way, I updated the code above a little (to detect the framerate). The next challenge is properly detect if a user Alt+Tab's out of the game. Anyone a good suggestion?
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB - upgrade incoming...)
( The path to enlightenment and the PureBasic Survival Guide right here... )
( The path to enlightenment and the PureBasic Survival Guide right here... )
- Kaeru Gaman
- Addict

- Posts: 4826
- Joined: Sun Mar 19, 2006 1:57 pm
- Location: Germany
the sample in the other topic where you asked if it was smooth....
I altered it with SetFrameRate, and nor it is NOT smooth any more, strange.
seems it needs some correction...
but at least, this is an example how to theoretically time a fixed FPS without 100%CPU...
btw:
I wrote the old example down because this kind of timing with native PB commands
is alike the timing described in this article: http://www.geisswerks.com/ryan/FAQS/timing.html
I altered it with SetFrameRate, and nor it is NOT smooth any more, strange.
seems it needs some correction...
but at least, this is an example how to theoretically time a fixed FPS without 100%CPU...
btw:
I wrote the old example down because this kind of timing with native PB commands
is alike the timing described in this article: http://www.geisswerks.com/ryan/FAQS/timing.html
Code: Select all
; *********
; Demo for Timing on 30 FPS
EnableExplicit
Define.l FPS, Timer, TimerDuration, TimeGap
Define.l n, t, col
Define.l PX, PY
Define.l EXIT
InitSprite()
InitKeyboard()
OpenScreen(1024,768,32,"TimerDemo")
; ** create a sprite
CreateSprite(0,128,128)
StartDrawing(SpriteOutput(0))
For n=0 To 127
col = RGB(64-n/2,128-n,255-n)
LineXY( 63-n, 63 , 63 , 63-n, col)
LineXY( 64+n, 64 , 63 , 63-n, col)
LineXY( 63-n, 63 , 64 , 64+n, col)
LineXY( 64+n, 64 , 64 , 64+n, col)
Next
StopDrawing()
FPS = 30
TimerDuration = 1000 / FPS
SetFrameRate(FPS)
PX = 512-64
PY = 384-64
Repeat
; ** buffer timer of loop start
timer = ElapsedMilliseconds()
; **
ExamineKeyboard()
If KeyboardPushed(#PB_Key_Escape)
EXIT = 1
EndIf
If KeyboardPushed(#PB_Key_Left)
PX - 4
EndIf
If KeyboardPushed(#PB_Key_Right)
PX + 4
EndIf
If KeyboardPushed(#PB_Key_Up)
PY - 4
EndIf
If KeyboardPushed(#PB_Key_Down)
PY + 4
EndIf
ClearScreen($201008)
DisplaySprite(0,PX,PY)
; ** calculate difference of wanted time ( TimerDuration )
; ** and really elapsed time
TimeGap = TimerDuration - (ElapsedMilliseconds() - Timer)
; ** if rest is bigger than 16ms, we wait with delay
If TimeGap > 16
; ** for 10ms less
Delay( TimeGap - 10 )
EndIf
; ** the rest will wait FlipBuffers
FlipBuffers(#PB_Screen_WaitSynchronization)
Until EXIT = 1oh... and have a nice day.
@Kaeru: I noticed that SetFrameRate seems to be more useful for windowed screens rather than a fullscreen. I commented out the SetFramerate, the timer section and changed FlipBuffers(#PB_Screen_WaitSynchronization) to FlipBuffers(). It works much smoother.Kaeru Gaman wrote:the sample in the other topic where you asked if it was smooth....
I altered it with SetFrameRate, and nor it is NOT smooth any more, strange.
seems it needs some correction...
but at least, this is an example how to theoretically time a fixed FPS without 100%CPU...
It's strange because I thought that was the purpose of all of those things in the first place. Why would it run better without them? Maybe it only happens with certain display frequencies.
Thanks for the link to the timing methods. I was looking for some improvements using API and can see that you adapted them well to PureBasic, though they don't seem to be performing as they were intended.
Being able to display and animate a screen by making display updates during the vsync or vertical blank seems like such a basic element. I'm surprised this isn't a working part of PureBasic yet. I am currently trying to do it through API, mainly Direct3D or something similar.
