Dark Bit Factory & Gravity
PROGRAMMING => Freebasic => Topic started by: Xalthorn on August 22, 2008
-
I'm sure I've seen a thread about this, but I can't find it.
How do people maintain a consistent frame rate in freebasic across different computers?
I've had code before where if I run it full screen it runs normally, but if I run it in a window it goes mental.
I might have missed the obvious, but is there a way to check if we've had a vertical refresh or some other standard method of maintaining consistency?
I know we can read the timer and then have a little loop at the end of our main loop to wait until the timer has increased by a certain amount, but it just feels clunky.
-
It's impractical to force a maximum refresh rate by waiting.
Instead just "skip" the frames you couldn't display because you were busy drawing the last one.
Look how much time has passed since the last frame and move all objects scaled by that time-delta.
Unluckily there's no realiable way to count the vsyncs, but those wouldn't help much anyway because the refresh-interval varies from pc to pc.
-
put this in your main loop
do until nextupdate>timer
{ game logic goes here }
nextupdate=nextupdate+mydelay
loop
{ rendering goes here}
-
Xalthorn, The delta timing method I always use is just to base all the movement off the difference between the internal timer at the start of the main loop and the end.
Unfortunately you can't rely on people to have v-sync enabled, and even if you use opengl to do your programming, you can request a refresh rate but there doesn't seem to be a straight forward way of ensuring that it will be set.
I can't really see how this works;
put this in your main loop
do until nextupdate>timer
{ game logic goes here }
nextupdate=nextupdate+mydelay
loop
{ rendering goes here}
Seems like it wouldn't work well.
So just do what Hellfire says..
By the way if you are making scrolling texts or anything smooth pixelwise scrolling then you'd do well to use floating point variables to keep track of the offset because otherwise you'll end up with some horrible glitches using integers and delta timing.
-
I think it would work, and I've seen people do it.
Imagine you wrote your code on a computer that ran at 60Hz all the time. That way you could avoid doing any time based calculations and just assume that the movement is going to happen in 16.67milliseconds.
If you then end up running on a slower machine, say it was only doing 20fps, then you could call your movement code 3 times each frame and only render once. The problem comes that the slower your machine runs, the more times you have to call the movement code, which makes the machine run even slower. So there does come a point where it breaks.
The technique most of us use, which Shockwave calls Delta Timing, is to assume that the frame you are calculating will take roughly the same amount of time as the last frame you drew. So, if you time how long it takes to do each frame you can use that time as an input into the next frame's physics calculations.
The problem with this technique is that now your movement is non-linear in time, which can make things like collision detection difficult. It can also make some time-based calculations unstable if your delta time goes outside some sensible range. One way round that is to insist that if your frame only took 1ms to render then you should probably be friendly to the OS and just sleep for 10ms or so. I much prefer this technique though.
Jim
-
Argh!
The reason I'm looking into this is because I'm working on another entry for the reduced res challenge and running it in a window was making it run way too fast. With some fiddling (simply reading the timer at the start of the loop and then waiting until the timer was at least 0.02 seconds advanced before starting again to keep it at 50 frames a second, in theory) it's running better.
I then went back to watch my xalboy and bluegold entries again and they're going ballistic, way too fast. I know I installed some directx update to try and watch someone elses demo, and I daresay windows saw fit to update itself with some stuff, but this is ridiculous.
I tried to add the same delay to those demos and the effect I'm now getting is that it races, pauses, races, pauses, and so on which is arguably more annoying to watch.
Can I ask how fast (insane?) the xalboy and bluegold demos run? They should be speedy, but watchable. In the xalboy demo, the bubbly plasma should be smooth not ... mad, and the dragon walking should be a fair pace that you'd expect to see in a gameboy game, not throwing as many frames to the screen as possible, same with the clouds on the title screen.
I also ran some demos by other people and they seem to be having a similar issue. I'm hoping it's just my machine having a bit of a fit, but this is my nightmare with PC coding, an inconsistent platform :(
-
do until nextupdate>timer
{ game logic goes here }
nextupdate=nextupdate+mydelay
loop
that's rather bungling.
let's say you implemented the "game logic" (update interval) to run fine at 120hz.
how often do you repeat the loop at a refresh-rate of 70hz?
that's basically the essential problem when porting console-games from ntsc (60hz) to pal (50hz) - and it fails frequently.
-
Yeah, you end up stuttering, based on common multiples of 70 and 120...83 frames of 1 and then 1 frame of 2. Nasty, and you get bumping, kind of like when you try to convert a 24fps video to 30fps.
Xalthorn, your stuff ran OK, which is cool, if you have real problem can you start another thread about it?
Jim
-
you probably mis-understand how my code works.
it's called frameskipping, and gives the appearence of the logic being updated regularly.
if your video refresh-rate is 70hz, then i take that to mean that your rendering takes about 14 millisecs. at a logic rate of 120hz, the 'mydelay' value is about 8. that means there will be an average of 1.75 logic updates per video update. if the video happens to take longer, then the logic will be updated more times, depending on how long the video update took to complete.
as long as the logic update code doesn't take longer than <mydelay> millisecs, it works great. plus it's numerically stable, unlike the delta time method. also, it's easier to control your logic update frequency.
-
Nah, it's pretty clear that the intention is to get the average number of updates as you say...but the problem is that on any given frame there must be a whole number of updates, so that if the software is running at 2/3rd speed, there will be 2 updates on even frames and 1 update on odd frames. That will look a bit odd, don't you think?
As I have already said, you do get the numerical stability and that is a big plus for your technique, but you lose the ability to move anything linearly if you are not running at full frame rate. It's not that you're wrong, not at all, it's that it's important to know the pros and cons of the possible techniques. Thanks for bringing it up so we can talk about it :)
Jim
-
Hi Mazemaker, thanks for clarifying that, it works how I thought :)
The only really satisfactory way of doing this is to use delta timing as far as I am concerned.
I have attached a recent remake I just released, this uses delta timing for everything except the starfield.
If you run it in windowed mode and experiment with different refresh rates it should make very little difference to the movement pattern of the bobs and the speed of the scroller.
These are classic effects where glitches would be really noticeable.
Here is the main loop from that remake:
ANIMTIMER=TIMER
WHILE (GETASYNCKEYSTATE(VK_ESCAPE) <> -32768 AND PTC_GETLEFTBUTTON = FALSE)
GXA=XAR
GYA=YAR
DELTA=TIMER
XV=XV+(GXA*MOVE)
YV=YV-(GYA*MOVE)
IF TIMER-ANIMTIMER>=.015 THEN
ANIM=ANIM+1
IF ANIM>36 THEN ANIM=1
ANIMTIMER=TIMER
END IF
IF TIMER-KEYD>=.1 THEN KEYBOARD()
DRAWSTARS()
DRAWGLOBES()
SCROLLER()
PTC_UPDATE@BUFFER(0)
ERASE BUFFER
SLEEP 2
MOVE=((TIMER-DELTA)*200)+.00001
WEND
END
The command "TIMER" returns the value held in the internal timer as a double so it's really accurate.
The variable "ANIMTIMER" is concerned with limiting the animation to 50fps (its' speed on the Amiga)
A variable called "MOVE" is calculated at the end of each loop, this is a factor for movement since it takes into account how long the last frame took.
hth :)
-
Sorry Jim, I was replying at the same time as you!
-
ok so for smooth movement, the time delta is probably best. especially if it's used to interpolate, not just increment.
and for timing things like frame animation, and more complex movement (inertia, some bounciing balls with gravity, acceleration etc), frameskipping is better.
-
@Xalthorn: the best way to archive this is using high performance counter/frequency, take a look here:
http://www.geisswerks.com/ryan/FAQS/timing.html
-
Thanks for that link rbz and thanks for the responses folks, time to do some experimenting with another demo of sorts I think. Maybe something with things that are moving along a predictable path where jumps will be noticeable.
-
and for timing things like frame animation, and more complex movement (inertia, some bounciing balls with gravity, acceleration etc), frameskipping is better.
I have had exactly the same problem!
Just out of interest, I was delta timing other stuff but what I did in the end was to use a sine wave and if sin (theta) produced a negative result I just inverted the sign which gave a perfectly consistent bounce no matter what the refresh rate (ok I admit it's a very specific example and not practical in all situations).
Thanks for that link rbz and thanks for the responses folks, time to do some experimenting with another demo of sorts I think. Maybe something with things that are moving along a predictable path where jumps will be noticeable.
There's another comp on the horizon where this will be really significant if you're interested :)
It's the Amiga remake comp and thanks to Rbz the prize fund is doubled.
-
Thanks for that link rbz and thanks for the responses folks, time to do some experimenting with another demo of sorts I think. Maybe something with things that are moving along a predictable path where jumps will be noticeable.
There's another comp on the horizon where this will be really significant if you're interested :)
It's the Amiga remake comp and thanks to Rbz the prize fund is doubled.
I'd like to have a play with that, if only to investigate how things were done, but I really don't expect to place in that one. Won't stop me trying though ;)