What is good cross platform gameloop?

Game development specific discussions.
coderJeff
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

What is good cross platform gameloop?

Post by coderJeff »

In pseudo-code, the main game loop:

Code: Select all

init_game_stuff()
do while not done
	get_inputs()
	update_game_state()
	update_sound()
	update_graphics()
loop
exit_game_stuff()
- init_game_stuff(), typically, I will use SCREENRES to initialize the graphics, calls to FMOD, or SDL wrapper for sound, my own classes for timers, etc.
- get_inputs(), I write a class to collect information on key presses, mouse movement, joystick, etc
- update_game_state(), pure coding stuff, usually not dependent on platform/target
- update_sound(), calls to whatever sound lib I am using
- update_graphics(), typically, if using fbgfx, it is a CLS: draw_stuff(), SCREENSET, sleep(), action
- exit_game_stuff() is the clean-up code

The question is: Has anyone found, even if it is conditional, a game-loop, that works well on all platforms and fbgfx/opengl modes? I typically only test on win32, so if anyone has some experience across all platforms, I'd really like your advice. Thanks.
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: What is good cross platform gameloop?

Post by MrSwiss »

Not so certain about OS differences but, more related to Client-SW running, in
particular, Internet-Browsers (FireFox, Chrome a.s.o.) which seem, to "adjust"
OS's timings ... (we've had that already, in other threads).

Solution:
use high speed timers, to measure: loop-run-time (which you subtract from a
pre-set sleep-time) e.g.:

Code: Select all

Sleep(sTime - rTime, 1)
in order to have, consistent speed ...
coderJeff
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: What is good cross platform gameloop?

Post by coderJeff »

Here is a sample code I put together. I tried to really simplify the demo while still keeping procedures I would use. It's a particle effect just click on the screen to add particles.

The key parts to look at are:
main_loop() -- I am trying to maintain a constant frame rate of 60Hz
gfx_update() -- where we actually flip active and working screens

Here are specifics of what I am trying to find out:
Do I need to call SCREENSYNC?
I think I want to SLEEP only to avoid 100% CPU usage. So do I need some non-FB sleep function using some high res timer for the idle time in each frame? FB's SLEEP() is not that accurate based on docs.
Different if I am running fullscreen mode?

The specifics of the game loop timing would make good material for a tutorial, especially if is very different on each platform, e.g. full screen dosbox compared to window-KDE, etc

Code: Select all

#include once "fbgfx.bi"
const screenw = 1024
const screenh = 768
const MAX_PARTICLES = 1000

type VECTOR2D
   x as double
   y as double
end type

type SPARK
   alive as boolean
   ttl as double
   p as VECTOR2D
   v as VECTOR2D
   c as uinteger
end type

'' game state
dim shared as double t1, t2, tdelta
dim shared as integer page = 0
dim shared as integer mx, my, mw, mb, last_mb
dim shared as boolean left_pressed, left_down, done
dim shared particles( 0 to MAX_PARTICLES-1 ) as SPARK

sub tmr_init()
   t2 = timer
   t1 = t2 - (1/60)
end sub

sub tmr_update()
   t1 = t2
   t2 = timer
   tdelta = t2 - t1
end sub

sub gfx_init()
   screenres screenw, screenh, 32, 2
   screenset page, 1-page
end sub

sub get_input()
   getmouse mx, my, mw, mb
   left_down = cbool((mb and 1) <> 0)
   left_pressed = cbool( ((last_mb and 1) = 0) and ((mb and 1) <> 0) )
   last_mb = mb
   done = cbool( multikey( fb.SC_ESCAPE ) <> 0 )
end sub

sub game_init()
   for i as integer = 0 to MAX_PARTICLES-1
      with particles(i)
         .alive = false
      end with
   next
end sub

sub add_particle( px as double, py as double )
   
   static curr_index as integer
   static last_index as integer
   curr_index = (last_index + 1) mod MAX_PARTICLES
   do
      with particles(curr_index)
         if( .alive = false ) then
            .p.x = px
            .p.y = py
            .v.x = (rnd - 0.5) * 200 '' move up to 100px/sec
            .v.y = (rnd - 0.5) * 200 '' move up to 100px/sec
            .c = rgb( cuint(rnd*255), cuint(rnd*255), cuint(rnd*255) )
            .ttl = 5.0 '' live for 5 seconds
            .alive = true
            exit do
         end if
      end with
      curr_index = (curr_index + 1) mod MAX_PARTICLES
   loop while( curr_index <> last_index )
   last_index = curr_index

end sub

sub add_particles( td as double, px as double, py as double )
   '' add 100 particles per second
   for i as integer = 1 to cint( td * 100 )
      add_particle( px, py )   
   next
end sub

sub update_particles( td as double )
   for i as integer = 0 to MAX_PARTICLES-1
      with particles(i)
         if( .alive ) then   
            .v.y += 3.0 '' simulate gravity
            .p.x += .v.x * td
            .p.y += .v.y * td
            if( .p.y > screenh ) then
               .alive = false
            else
               .ttl -= td
               if( .ttl < 0 ) then
                  .alive = false
               end if
            end if
         end if
      end with
   next
end sub

sub draw_particles()
   for i as integer = 0 to MAX_PARTICLES-1
      with particles(i)
         if( .alive ) then
            '' draw the direction vector shorter
            line( .p.x, .p.y ) - step( .v.x / 4, .v.y / 4 ), .c
         end if
      end with
   next
end sub

sub gfx_update()
   page = 1-page
   '' screensync
   screenset page, 1-page
   sleep 5, 1
end sub

sub main_frame( td as double )

   get_input()
   if( cbool(mb<>-1) and left_down ) then
      add_particles( td, cdbl(mx), cdbl(my) )
   end if
   update_particles( td )

   cls
   draw_particles()
   gfx_update()
   
end sub

sub main_loop()

   dim frame_timer as double = 0
   dim dt as double

   do
      tmr_update()
      dt = tdelta

      '' drop frames
      if( dt > 2/60 ) then
         dt = 1/60
      end if

      frame_timer += dt

      if( frame_timer >= 1/60 ) then
         frame_timer -= 1/60

         main_frame( 1/60 )

      end if
   
   loop until done

end sub

'' MAIN
'' ----

tmr_init()
gfx_init()
game_init()
main_loop()
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: What is good cross platform gameloop?

Post by fxm »

Number the particles between 0 and MAX_PARTICLES-1 (as for the array sizing) or between 1 and MAX_PARTICLES (as for the iterator loops) you must choose for the indexing, but do not mix the two.
coderJeff
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: What is good cross platform gameloop?

Post by coderJeff »

thanks, fxm. A spur of the moment change, in one place not carried through. Updated. Intent was to be 0 to MAX_PARTICLES-1.
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: What is good cross platform gameloop?

Post by MrSwiss »

In order to prevent casting (to Single, behind the scenes) in type VECTOR2D:

Code: Select all

Type VECTOR2D
    As Single  x
    As Single  y
End Type
FBGFX lib only manages Singles (only float-type, in -lang "QB").

Also, color (32bit) in UDT's should be: ULong ... (32/64-bit compiler's compat.)

Note: I don't fancy, the use of "shared" var's., unless absolutely required.
coderJeff
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: What is good cross platform gameloop?

Post by coderJeff »

Thanks for the tips. I appreciate that. I guess I was too hasty putting together the sample code, it really could have been anything, bouncy circles, triangles, etc. The advice is helpful, thanks.
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: What is good cross platform gameloop?

Post by paul doe »

coderJeff wrote:The question is: Has anyone found, even if it is conditional, a game-loop, that works well on all platforms and fbgfx/opengl modes? I typically only test on win32, so if anyone has some experience across all platforms, I'd really like your advice. Thanks.
Check here:
viewtopic.php?f=15&t=26050
One of the best game loop implementation that I've come across. In the comments of the code are the links to the original sources, but just in case:
http://www.koonsolo.com/news/dewitters-gameloop/
coderJeff
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: What is good cross platform gameloop?

Post by coderJeff »

paul doe, that's perfect! Good resources for a starting point for me, thanks. I've used the variable frame rate often, but not a fixed frame rate, which is the one I want to explore more, and your demo includes both. Excellent.
sancho3
Posts: 358
Joined: Sep 30, 2017 3:22

Re: What is good cross platform gameloop?

Post by sancho3 »

@coderJeff
The sample code runs smooth.
But for some reason it does a noticeable lag for a fraction of a second, every second or so.
I don't know if it is the frame regulator or something else. Do you see it as well?
On Linux fwiw.
coderJeff
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: What is good cross platform gameloop?

Post by coderJeff »

Maybe. On my PC, if I increase MAX_PARTICLES to 100000 and add_particles() to 5000/sec, then I see the frames slow down & speed up depending if more & less particles on the screen.

The idea behind the game loop in the sample I posted:
1) With a fixed frame rate of 60Hz, if an object is moving at 60pix/sec then I should see the object move exactly 1 pixel per frame.
2) due to multi-process/multi-threaded OS, each frame might have a little less or little more time to render.
3) If we are ahead, just wait until we should should draw the next frame at +1/60sec.
4) If we are a little behind, speed up the visual frame rate a little bit, but render each frame exactly as if 1/60sec has passed.

My thought is that it should give the illusion that frame rate is fixed, even if actually each frame is different amount of time. Overall, assuming that the CPU/video can almost always compute and drawn the frame in time.

It's been a number of years since I had a real MSDOS running on hardware, but I think I remember having to have a SCREENSYNC to make animations smooth. Maybe similar for Linux that the rendering needs to sync with the hardware or you get an odd skip/stutter in the animation. Maybe the SLEEP 5, 1 I have in there is the bad choice. I don't know.

Perhaps a better test would be a vertical bar moving side to side at FRAME_RATE pixels/second. Then could look for smooth movement and no tearing.
ecxjoe
Posts: 96
Joined: Aug 08, 2009 6:01
Location: Utah, USA
Contact:

Re: What is good cross platform gameloop?

Post by ecxjoe »

You just need to keep track of how much time passes within the game loop.

Then if you want something to move at 60px/sec, you multiply by a factor of sixty.

Example:
Let `t` be the time that passed between frames -- in seconds (using double-precision).
Let `v` be the position vector.

v += 60*t

So if `t` equals one second, it'll move 60 pixels. If `t` is 1/60th of a second, it'll move one pixel. And so on.
jj2007
Posts: 2326
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Re: What is good cross platform gameloop?

Post by jj2007 »

For those who understand assembly: There is a brand new Game Development sub-forum at Masm32. Same discussions over there: Do you need vertical sync? Better use PeekMessage or GetMessage? etc etc
coderJeff
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: What is good cross platform gameloop?

Post by coderJeff »

@ecxjoe, thanks, but I get that part. I've written simulations that use a variable frame rate.

@JJ, thanks, yeah started to read the threads there. That's the kind discussion I'm looking for. As far as FB goes, I've helped with gfxlib2 a little, but for the most part, I am a just a user.

I guess, what I was hoping for, was someone with more knowledge than me to explain what to do. I wish I could find the quote: Lachie D. talked about what it takes to complete a game, and I find I am often distracted by so many things other than finishing the project. In FB/gfxlib2, we have multiple platforms, each having multiple drivers. Like a true basic programmer, in this area, I tend to mash things together until I have something that works for me. I have no idea if it is a good choice, or if it will work for others.

As a USER, I look at the options; I can make something work, but what's good?
ScreenRes for setting full screen / opengl mode / refresh rate / other flags/behaviour - some undocumented
ScreenSet for page flipping
ScreenSync for syncing with vertical blanking interrupt
ScreenLock/ScreenUnlock for updating synchronizing directly
Sleep for timing and synchronizing
Flip for page flipping and synchronizing

Did I miss any? There are many choices! I don't know, maybe I shouldn't get hung up on it, and once I have something worth showing I can fiddle with display.
dafhi
Posts: 1640
Joined: Jun 04, 2005 9:51

Re: What is good cross platform gameloop?

Post by dafhi »

This code won't compile (left out my 3d types)

the physics is pretty straight-forward

Code: Select all

  var   iphys_fps = 1/67, phys_t = 0f
  var   ianim_fps = 1/57, anim_t = 0f
  var   tp = timer
 
  while inkey=""
    if anim_t <= 0 then
      screenlock
        ' ..
      screenunlock
      while anim_t < 0
        anim_t += ianim_fps
      wend
    endif
   
    sleep 1
   
    var t=timer, dt=t-tp
    tp=t
   
    anim_t -= dt
    phys_t += dt

    var frames=0
    while phys_t>0
      frames += 1
      phys_t -= iphys_fps
    wend
   
    var rate = pi*iphys_fps*frames
    incr axis.a0, .1*rate, twopi
    ' ..
  wend
Last edited by dafhi on Jun 01, 2018 3:44, edited 1 time in total.
Post Reply