Page 1 of 1

custom fixed timestep

Posted: Mon Aug 06, 2018 9:50 pm
by touchdown51
Hey everyone, I've been trying to rewrite love.run() function to implement fixed timestep the way I want it to work. Basically I set a dt constant value (FRAME_DT). After that I execute love.load(), then enter a game loop. Then in the game loop I want to do this: check time since programme started, call love.update(FRAME_DT), call love.draw(), check the amount of time it took (update and draw). If it took less time than dt seconds, wait dt - time seconds, else don't wait. Rinse and repeat. It seemed to work okay but if I change my FRAME_DT it seems to changes my objects speed. That should'nt happen since love.update use FRAME_DT to adapt objects speed to the timestep… Here is the code I wrote :

Code: Select all

function love.run()
  -- Custom fixed delta time for use in the game loop
  local FRAME_DT = 1/60 -- Frame delay (in seconds)
  
  -- Initialize game
  if love.load then love.load() end
  
  -- Game loop
  local running = true
  while (running) do
    local frame_start = love.timer.getTime()
    
    -- Handle events
    if love.event then
        love.event.pump()
        for name, a,b,c,d,e,f in love.event.poll() do
            if name == "quit" then
                running = false
            end
	love.handlers[name](a,b,c,d,e,f)
        end
    end
    
    -- Update game state
    if love.update then love.update(FRAME_DT) end
    -- Draw game state
    love.graphics.clear(love.graphics.getBackgroundColor())
    love.graphics.origin()
    if love.draw then love.draw() end
    
    -- Wait until its time to process the next frame
    local frame_time = love.timer.getTime() - frame_start
    if (frame_time < FRAME_DT) then
      love.timer.sleep(FRAME_DT - frame_time)
    end
    
    -- Flip backbuffer
    love.graphics.present()
  end
end
I know there are other methods for this, but the reason is wrote here is not so much how to do this another way, but to understand why it does not work the way I think it is supposed to. Seems like either I made a really dumb mistake I can't see or because something I don't know about timers, the way love works... Anyway, thanks for taking the time to read this!

Re: custom fixed timestep

Posted: Mon Aug 06, 2018 10:53 pm
by pgimeno
Have you disabled vsync?

Also, your love.graphics.present call is misplaced. For best responsiveness, first present the graphics, then do the waiting (will give players time to react), and then read the user's input based on what they saw. If you wait, then present, then read inputs, you're giving far too little reaction time to the players.

Re: custom fixed timestep

Posted: Mon Aug 06, 2018 11:01 pm
by touchdown51
Thanks for your reply pgimeno :) yep, I've disabled vsync (set it to 0). Just curious, how exactly could it interfere with what I want to do ? Also, thanks for your suggestion for love.graphics.present(). It makes total sense. I put it here because it seemed logical (player see an image every FRAME_DT seconds. But you're right, it interferes with gameplay. Moved it :)

Re: custom fixed timestep

Posted: Mon Aug 06, 2018 11:12 pm
by zorg
You do multiply the speed of your objects with the dt regardless of how you edited love.run, right?
otherwise don't expect it to work the way you want it to.

Re: custom fixed timestep

Posted: Mon Aug 06, 2018 11:37 pm
by touchdown51
Thanks for you reply zorg! Yes I do, this is why I pass FRAME_DT (fixed frame delay duration) to love.update in my game loop. And I use it in my update code (like xposition = xposition + xvelocity*dt). I too thought I forgot to do this at first due to the problem, but no.

Re: custom fixed timestep

Posted: Mon Aug 06, 2018 11:50 pm
by pgimeno
touchdown51 wrote: Mon Aug 06, 2018 11:01 pm Thanks for your reply pgimeno :) yep, I've disabled vsync (set it to 0). Just curious, how exactly could it interfere with what I want to do ?
Say your vsync rate is 1/60th of a second. Say you set FRAME_DT to something like 1/100. Then when vsync is on, love.graphics.present() will introduce a delay, therefore your frame rate will be 1/60 s, and your FRAME_DT is lying. Your objects will move slower. That's why it's important that vsync is off.

I remember having read somewhere in this forum that some graphics cards don't allow disabling vsync. Ensure that yours does.

Apart from that, it's possible that there's some other problem elsewhere in the code, but that's hard to say without being able to see it.

Re: custom fixed timestep

Posted: Tue Aug 07, 2018 1:19 am
by touchdown51
Thanks pigmeno! Interesting, i didn't know that and it seems it was both a problem with vsync and a problem with my code. I think I fixed it. I'll try so summarize both the problem and the fix now so this can hopefully help anyone who might have a similar problem.

First problem, I made a mistake and set window.vsync to 0 in the config file (conf.lua) as its specified in the wiki (https://love2d.org/wiki/Config_Files). But I failed to notice the config file example was for love 0.11.0 and I'm using love 0.10.2. In 0.10.2 window.vsync should be equal to true or false (boolean value), not 0 or 1 (number value) like in 0.11.0. I used love.getMode() to check and using 0/1, vsync was always on (value in config file was ignored). Using false deactivated it.

Second problem, vsync on caused the problem described by pigmeno above. Correctly deactivate vsync the game objects speed is consistent regardless of the value of FRAME_DT. Before, objets seemed to be slower and slower if FRAME_DT was smaller than 1/60 seconds. Now 1/120, 1/240, 1/480 values for FRAME seems to work fine and dandy.

Really small values like 1/600 and smaller I begin to see stuttering movement and then at values likes 1/700 stuttering is worse and speed seems to get inconsistent from frame to frame. At 1/960 objects move way too fast and teleport(jerky fast forward mode). I don't know why (decimal value precision related problem for very small values, maybe ?) But at this point I think one may just use the standard love.run.

For me its absolutely fine because I don't think i'll use a time step smaller than 1/120 seconds.