Timing, How Does Your App Fare?
Miscellaneous Forums/General Discussion/Timing, How Does Your App Fare?
do most guys/gals here expect their Blitz3d apps to run on anything from a 733mHz GeForce2 machine to a 3 GHz jobbie...
and if so, how do you handle the timing of your apps to take the differences in performance of into account, so that you programs will run smoothly?
--Mike
I've always used Delta Timing and my results have always been super smooth.
I know this isn't Blitz3D but I made my Bonus games (BlitzPlus) work on a 300MHz PC, they ran around 30fps (well about half the refresh rate, whatever that was). On decent PCs theg ames ran at the normal regresh rate e.g. 60Hz or 85Hz whatever was set in Control Panel Display Properties. I just used Fixed Rate logic and made sure it was damn quick, plus I wrote this speical 5 buffer direty rects system to optimise the amount drawn. So inthe end the game was shoving a lot of graphics round and looked like it needed a more powerful PC, but it didn't. There was also a "particles" setting in options for people to modify if need be (but I doubt anyone did). I toyed with the idea of having the game tun a test on first load and then setting the particles level appropriately, but I never had time plus I'm not 100% sure it was needed.
Because my games only used the CPU for graphics, it was easy to predict what it would run like on different specs. This is not so easy in BlitMax because of course 3D cards have radically different performance and a good card in a slow PC will still make a huge difference and vice versa.
My new BlitzMax Game Framework uses Fixed Rate logic but with an extra special tweak that's a bit like delta timing which results in totally smooth ouput (unless the video card has issues or it's on an OLD TFT, but these things can't be worked around) yet still retains the collision detection reliability of fixed rate logic. Fixed Rate Logic is the preffered method of physics simulations. There has been talk of it not working on slow PCs, well this is only an issue if you make your logic routine too slow and clumsy and it can never catch up with itself, but you can limit it to a maximum number of loops so that the game just runs slower.
I use fixed rate logic.
Red: I gave up on BMax because of timing issues and the hassle it was giving me trying to find a decent solution.
However, this weekend, I worked on ironing out the timing issues in B+ when using the GUI & event driven model and running everything with the "software" driver. It seems to be working okay and nice and smooth at 30FPS. I miss the OOP of BMax though.
I can't go back to BPlus for a pro game. It's fine for fun, but not a portal standard game. Bmax has got what it takes, it just needs a shedload of workarounds, which I've now got under my belt (mind you, so did BPlus!).
One Eyed Jack. What speed do you set your fixed rate logic to run at, or does it vary depending on the app? The original example I saw it used in was 500Hz, but I've set mine lower at 200HZ. This could introduce some minor jerkiness (it did in BlitzPlus, not very noticeable) because it's only doing an integer number of ticks (iterations) per frame and that could alternative between 3 and 4 (for example on a 60Hz screen). So I've adjusted the BMax code to perform a final iteration which moves everything by a floating point delta value equal to the remainder of ticks (0.00001 to 0.99999 type thing). This is what a makes it so smooth. There, I've given away my secret now ;-)
thx for the insights guys... appreciate it...
--Mike
60hz logic, rendering is decoupled.
I am using a system based off this pretty cool
article on gamasutra.
The developers talk about the core game framework they use. It covers a few things, timing being one of them. Basically, fixed logic rate, with rendering at full speed. All movement is interpolated between each logic update.
Mine does logic updates 30fps, and it can spread the load across multiple frames( ie, logic update -> ai update.. etc ). Seems to work pretty nice so far. :)
i have no idea!
but i use not the delta force, but the built in timer
commands. i think they work fine. don't spit on me. :)
OEJ: 60Hz, well that's pretty slow. Smooth or any jerks?
I know it is not the best way of doing things but what I prefer to do at the moment is to decide what the lowest spec machine is that I want the game to run at 60fps at all times and then don't let any frame run faster than 60fps by putting in an appropriate length delay command if the frame is completed too quick (less than 15ms).
My logic is either 15 or 30fps, depending on the requried response time.
The racer has 20fps logic and rendering is tweened.
Anyone know of any good tutorials on the various timing methods?
I must admit, game timing isn't something I've looked into properly. I think it's about time I did. :)
TBH, I don't think I fully understand how the 'Fixed Rate Logic' method works. :/ I tried googling but couldn't find anything decent.
Fixed rate logic=CreateTimer()/WaitTimer()
Ah, well, that's what I use at the moment:
I create a 60Hz timer and simply do 'WaitTimer(FPS_timer) : VWait : Flip False'.
That's not ideal, though, so I'd like to gen up on other solutions.
same here... but i've found that this solution doesn't hold up in all situations...
runs fine for the 2.5gHz machine and the 1.8 gHz machine... but when the compiled app is transfered over to a 733mHz machine, there is a lot of jerking, especially seeing as there is a lot of speed calculations and move commands...
verrrry frustrating... i'd have to give delta timing a shot... but that would mean going into tons of logic, and inserting the delta adjustment...
--Mike
Waittimer actually gives a return value of how many ticks have passed.
Use
FrameSkip=WaitTimer(FPS_Timer)-1
To get a value that represents how many frames to skip.
you're right though, the high logic rate may get your system crawling with this technique.
> runs fine for the 2.5gHz machine and the 1.8 gHz machine... but when the compiled app is transfered over to a 733mHz machine, there is a lot of jerking, especially seeing as there is a lot of speed calculations and move commands...
this sounds like its not because of the timing
method but because you are processing too much
for a 733mhz machine. particles, mesh deformation,
etc. will do that, won't it?
it may end up delta timing is not much better, you
could have options to switch off a few things eg.
particles for slower rigs.
Waittimer actually gives a return value of how many ticks have passed.
Ah, another missing fact from the docs! So, do you do something like this, in your main loop?
While Not KeyHit(1)
updates = WaitTimer(fps_timer)
For n = 1 To updates
update_game()
Next
render_game()
VWait : Flip False
Wend
I just tried this method and it does keep the game speed constant, regardless of FPS, but, it's pretty jerky. :/ I think my background tasks are mucking things up.
Looks fine to me, providing that a for loop with both start and end values the same skip the loop.
Fixed rate logic=CreateTimer()/WaitTimer()
No No No, that's not what I'm doing. Mine's the old retro 64 method. No Timers (YUK) involved.
Having said that, Big10p's code sample is a similar structure too mine, I'm just working out the number of ticks in a different manner, using Millisecs() and LastTime etc and making sure that I get 200 ticks per second i.e. high logic rate.
I wasn't commenting on yours Grey. Shift! :P
Timers are a cheat though!
I guess that using Timers *IS* fixed rate logic, so's my method. What's nice with it is you can make a counter and reduce it by 1 each time and know that when it's 0, time's up. Plus you can move things in set increments and know that they'll collide if need be (collision is part of logic not render of course)
Red: Yeah basically timing method must be sorted and tested BEFORE you make a game otherwise you won't feel like converting everything, and if you don't, the game will be dodgy. It was the first thing I looked up 18 months ago when I first came on the forum. In fact I could have sworn I found it in the FAQ, but it's not there now ...
Looks fine to me, providing that a for loop with both start and end values the same skip the loop.
But doesn't the For loop have to iterate at least once per main game loop? Otherwise, nothing gets updated - everything would stand still.
Grey: Where can I read up on this 'Retro 64' timing method?
Big10p, yes, your code above would always update at least once. I didn't say the right thing. Youre code should be okay.
Retro64? He's either just doing for x=1 to 10000 or just
repeat:until timepassed>1000/30
Ah, another missing fact from the docs! So, do you do something like this, in your main loop?
It was mentioned in the b+ tutorial Mark did and he shows how to properly do a timing method in B+. I can vouch for his method, it works perfectly. Check the B+ tutorial section ;c)
Kuron's repost of Mark's is here:
http://www.blitzbasic.com/Community/posts.php?topic=48890The Retro64 method (modified by me a bit) is like this (excuse crap indenting):
;timing vars
Global FRAMERATE# = 200 ;Float on purpose so calcs end up as float
Global MS# = 1000.0/FRAMERATE ;use .0 to ensure float is returned ;milliseconds per frame float
Global LastTime%
Global NumTicks#
Global LastNumTicks#
Function InitTimingVars()
;set a first value for LastTime (it will be used in the main loop)
LastTime = MilliSecs()/MS
NumTicks = 0
LastNumTicks = 1
End Function
;Main code below
InitTimingVars()
Loop=1
Repeat
;for our purposes, a tick is based on FRAMERATE
;get in the time and make sure at least 1 tick expired
Repeat
TmpMS = MilliSecs()/MS
;little sanity check here in case the timer flips back to 0 :)
If TmpMS < LastTime Then LastTime = TmpMS - LastNumTicks
NumTicks = TmpMS - LastTime
Until NumTicks > 0
LastTime = TmpMS
;account for when the user alt-tabs :)
If NumTicks > 500 Then NumTicks = LastNumTicks
LastNumTicks = NumTicks
;now we do the logic loop ... from 1 not 0!
For i = 1 To NumTicks
;some game logic code (no drawing)
Next
;now the drawing code followed by flip
Until Loop=0
I've refined this since though using this rad doc:
http://www.gaffer.org/articles/Timestep.html by adding a final floating point based iteration, bit like delta timing.
Also a lot of people forget to do this, but if you pause the game or lose focus, always call InitTimingVars() again otherwise the game will leap ahead, although the code above limits the jump if it's >500 times. I've made this value a lot smaller since though due to having lots of tracking of possible breaks in gameplay.
In Mark's insectoid demo, he used...
time=MilliSecs()
While Not KeyHit(1)
n_time=MilliSecs()
elapsed=n_time-time
If elapsed>25 ;slowing down! - clamp update to 40 FPS (1000/40=25 millisecs)
cnt=elapsed/25
For k=1 To cnt
UpdateGame()
Next
time=time+cnt*25
Else ;leave frame synced...
UpdateGame()
time=n_time
EndIf
RenderGame()
Flip
Wend
If that helps any?
Thanks all.
I'll have to have a closer look at all this when I'm in a better frame of mind. I don't think Monday's are good for looking into this kind of stuff. :P