Following this post: https://gafferongames.com/post/fix_your_timestep/ I wanted to provide a non-fixed timestep. Essentially, a game tick rate independent of frame rate (due to users being able to run game at non-60fps, vsync on / off...)
I came across this gist which gets the job done:
https://gist.github.com/Leandros/98624b9b9d9d26df18c4
The issue is that if we set TICKRATE to 1/1000 love.update will run every 1/1000th a second right? Well using a mix of the latest 11.X https://love2d.org/wiki/love.run and the gist above, pumping events like above with `love.keyboard.setKeyRepeat(true)` (such as a user moving their character on the screen, holding down an arrow key) does not produce a love.keypress every 1/1000th a second as the update loop.
Even if I add the event loop within the tickrate loop, it seems that the keypress (e.g. left arrow key) happens in a fixed rate, regardless of if TICKRATE is 1/1, 1/60, 1/1000...
Is my only option to sync the holding down of keypresses and a non-fixed timestep to use keyboard.isDown within the love.update function?
EDIT:
I've noticed https://love2d.org/wiki/love.keyboard.setKeyRepeat mentions that it depends on the users system. I've tried changing my system repeat rate and that definitely affects how love.keypressed is called. Even adding a dt timer in love.keypressed seems to not matter, as realistically a slower repeat rate from system will slow a users actions down for quick actions. I think the only solution is keyboard.keydown in update
EDIT2:
I've created a Fork of the gist here: https://gist.github.com/jakebesworth/ac ... 77105a41f8 that works with 11.X, licensed, and will hopefully be continually updated as necessary
Love2d Timestep and keypress (with repeat) not updating correctly?
Love2d Timestep and keypress (with repeat) not updating correctly?
Last edited by love2d on Mon Apr 30, 2018 5:53 am, edited 2 times in total.
- zorg
- Party member
- Posts: 3469
- Joined: Thu Dec 13, 2012 2:55 pm
- Location: Absurdistan, Hungary
- Contact:
Re: Love2d Timestep and keypress (with repeat) not updating correctly?
First i'm going to assume you did notice that the gist was from 2016, and you modified the infinite loop to be a returned function, like how the wiki page gives you the current 11.x default love.run function.
Theoretically yes, depending on your cpu speed, os scheduler and stuff, you might get 1000 ticks per second with it.
Now, the important bit, setKeyRepeat is cancer because it uses the OS' own repeat detection rates, which is basically useless for any and all purposes, even though people like to say otherwise.
What you want to do is forget ever setting that to true, and use a combination of love.keyPressed and love.keyboard.isScancodeDown (or love.keyboard.isDown if you want to mess with people who have non-US key layouts selected.) to detect a press, and detect if it's still held down or not; that way, it will be accurate down to your tickrate. (You can also use keyReleased if you want to)
In short, yes, you need to use one of those two functions inside love.update or whereever to get what you want.
That said, what you call "non-fixed timestep" is a bit of a misnomer, since you do want the timestep (the tickrate) to be fixed to 1/1000, you just don't want it to be tied to the graphical framerate.
Theoretically yes, depending on your cpu speed, os scheduler and stuff, you might get 1000 ticks per second with it.
Now, the important bit, setKeyRepeat is cancer because it uses the OS' own repeat detection rates, which is basically useless for any and all purposes, even though people like to say otherwise.
What you want to do is forget ever setting that to true, and use a combination of love.keyPressed and love.keyboard.isScancodeDown (or love.keyboard.isDown if you want to mess with people who have non-US key layouts selected.) to detect a press, and detect if it's still held down or not; that way, it will be accurate down to your tickrate. (You can also use keyReleased if you want to)
In short, yes, you need to use one of those two functions inside love.update or whereever to get what you want.
That said, what you call "non-fixed timestep" is a bit of a misnomer, since you do want the timestep (the tickrate) to be fixed to 1/1000, you just don't want it to be tied to the graphical framerate.
Me and my stuff True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
Re: Love2d Timestep and keypress (with repeat) not updating correctly?
Yes I did. After this research is done, I intend to submit my latest version to the gist.Zorg wrote: First i'm going to assume you did notice that the gist was from 2016, and you modified the infinite loop to be a returned function, like how the wiki page gives you the current 11.x default love.run function.
That's the conclusion I've come to, so it's good to get reassurance.Zorg wrote: Now, the important bit, setKeyRepeat is cancer because it uses the OS' own repeat detection rates, which is basically useless for any and all purposes, even though people like to say otherwise.
Ok, solves my question!Zorg wrote: In short, yes, you need to use one of those two functions inside love.update or whereever to get what you want.
Good point!Zorg wrote: That said, what you call "non-fixed timestep" is a bit of a misnomer, since you do want the timestep (the tickrate) to be fixed to 1/1000, you just don't want it to be tied to the graphical framerate.
In reality I'll probably use some fancy number like 60, 100, 128, tick rate, I need to decide on that (it can't be too low if x + 1 movement of objects is too slow).
Thanks Zorg for answering my question!
Re: Love2d Timestep and keypress (with repeat) not updating correctly?
NO! You might be able to clamp the lower bound of your "tickrate" to 1/1000 but there is no way to ensure that love.update is called every 1/1000th of a second. There is no way to guarantee this when you are running in a multi-tasking operating system.
The following piece of code may lock indefinitely on a slower CPU because it doesn't cap the "frameskip" or rather the "tickskip":
Code: Select all
while lag >= TICKRATE do
if love.update then love.update(TICKRATE) end
lag = lag - TICKRATE
end
So you need to include something like "maxframeskip" or "maxtickskip".
Re: Love2d Timestep and keypress (with repeat) not updating correctly?
As far as I can tell, it's only useful in GUIs or other situations where the user is typing.
lf = love.filesystem
ls = love.sound
la = love.audio
lp = love.physics
lt = love.thread
li = love.image
lg = love.graphics
ls = love.sound
la = love.audio
lp = love.physics
lt = love.thread
li = love.image
lg = love.graphics
Re: Love2d Timestep and keypress (with repeat) not updating correctly?
Right. Even though love.textinput repeats always, you still need setKeyRepeat to auto-repeat arrow keys for faster movement.
Re: Love2d Timestep and keypress (with repeat) not updating correctly?
Thanks for the reply ivan. I understand that in practice it won't run every 1/1000th of a second as you mentioned. Also, ideally I'd set TICKRATE to like 60, or 256 or some smaller number, not that it fixes your concern (PCs can slow down for a multitude of reasons).ivan wrote: ↑Sun Apr 29, 2018 7:04 amNO! You might be able to clamp the lower bound of your "tickrate" to 1/1000 but there is no way to ensure that love.update is called every 1/1000th of a second. There is no way to guarantee this when you are running in a multi-tasking operating system.
The following piece of code may lock indefinitely on a slower CPU because it doesn't cap the "frameskip" or rather the "tickskip":You see, the problem is that your "love.update" callback might take longer than 1/1000th of a second to execute.Code: Select all
while lag >= TICKRATE do if love.update then love.update(TICKRATE) end lag = lag - TICKRATE end
So you need to include something like "maxframeskip" or "maxtickskip".
I'm a little confused with your concern though, can you elaborate?
Even if love.update takes say 1/2th of a second to execute (more than 1/1000th), (meaning the game will appear slower for slow computers). But even if we don't, eventually that loop should end, I'm not sure where the indefinite lock comes into play.
What do you mean by "tickskip" or "frameskip" in this context (if you can provide a small pseudo-code snippet that might help me wrap my head around the problem).
Other than that, assuming I've updated the code to return a function and based it off of 11.X https://love2d.org/wiki/love.run do you see any other issues with the code snippet?
Re: Love2d Timestep and keypress (with repeat) not updating correctly?
Well, I'll put it this way, let's say that love.update takes 10 ms every time.
After you execute your loop once you already have produced 10 ms lag.
Then you execute the loop a second time, with 10 ms of lag.
Since your "target update rate" is 1ms you have to call love.update 10 times.
Therefore after the second run you have to call love.update 10 times to compensate for the lag, but that adds an additional 100 ms of lag...
My suggestion would be to break the loop or limit the maximum amount of lag that can accumulate.
Code: Select all
local interval = 1/targetFrameRate
lag = math.min(lag + dt, interval*maxFrameSkip)
while lag >= interval do
lag = lag - interval
update(interval)
end
If the lag becomes too much, the game will slow down temporarily (but it won't hang).
Re: Love2d Timestep and keypress (with repeat) not updating correctly?
Awesome, thanks ivan! That makes sense, It's very much the "spiral of death" mentioned in the "Fix your Timestep" article, which I'm surprised wasn't added in the gist, I'll have to add it.ivan wrote: ↑Sun Apr 29, 2018 3:31 pmWell, I'll put it this way, let's say that love.update takes 10 ms every time.
After you execute your loop once you already have produced 10 ms lag.
Then you execute the loop a second time, with 10 ms of lag.
Since your "target update rate" is 1ms you have to call love.update 10 times.
Therefore after the second run you have to call love.update 10 times to compensate for the lag, but that adds an additional 100 ms of lag...
My suggestion would be to break the loop or limit the maximum amount of lag that can accumulate.This way you may skip some frames if any lag occurs.Code: Select all
local interval = 1/targetFrameRate lag = math.min(lag + dt, interval*maxFrameSkip) while lag >= interval do lag = lag - interval update(interval) end
If the lag becomes too much, the game will slow down temporarily (but it won't hang).
Re: Love2d Timestep and keypress (with repeat) not updating correctly?
Hey Ivan (and others) here's my updated code. I've tested it, and it seems to work (I can't think of a way to check the frameskip portion) where game tick rate is independent of frame rate, with following the guidelines to disallow spiral of death issues:ivan wrote: This way you may skip some frames if any lag occurs.
If the lag becomes too much, the game will slow down temporarily (but it won't hang).
Code: Select all
--[[
Based off of: https://gafferongames.com/post/fix_your_timestep/
https://github.com/SSYGEN/blog/issues/11
Code from: https://gist.github.com/Leandros/98624b9b9d9d26df18c4
---]]
-- 1 / Ticks Per Second
local TICK_RATE = 1 / 1000
-- How many Frames are allowed to be skipped at once due to lag (no spiral of death)
local MAX_FRAME_SKIP = 250
-- No Framerate cap currently, either max frames CPU can handle (up to 1000), or vsync
function love.run()
if love.load then love.load(love.arg.parseGameArguments(arg), arg) end
-- We don't want the first frame's dt to include time taken by love.load.
if love.timer then love.timer.step() end
local lag = 0.0
-- Main loop time.
return function()
-- Process 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
if not love.quit or not love.quit() then
return a or 0
end
end
love.handlers[name](a,b,c,d,e,f)
end
end
-- Cap number of Frames that can be skipped so lag doesn't accumulate
if love.timer then lag = math.min(lag + love.timer.step(), TICK_RATE * MAX_FRAME_SKIP) end
while lag >= TICK_RATE do
if love.update then love.update(TICK_RATE) end
lag = lag - TICK_RATE
end
if love.graphics and love.graphics.isActive() then
love.graphics.origin()
love.graphics.clear(love.graphics.getBackgroundColor())
if love.draw then love.draw() end
love.graphics.present()
end
-- Even though we limit tick rate and not frame rate, we might want to cap framerate at 1000 frame rate as mentioned https://love2d.org/forums/viewtopic.php?f=4&t=76998&p=198629&hilit=love.timer.sleep#p160881
if love.timer then love.timer.sleep(0.001) end
end
end
One thing I will probably add later is a Framerate cap availability if the user wants it...
Who is online
Users browsing this forum: Bing [Bot] and 2 guests