Fixed rate logic

BlitzMax Forums/BlitzMax Beginners Area/Fixed rate logic

There have been many threads on these forums about various timing methods in Blitz and I now have a pretty good grasp on delta timing but what I am really looking for is a nice, easy to understand fixed rate logic explanation\example as the ones I have looked at seem to make the whole thing really overly complex sounding.

Please would someone be kind enough to explain it in a noob friendly manner for someone who has never used it before. I`m sure that there are many people new to such concepts that would appreciate a helping hand apart from me.

Thanks for any help,

Jason.

Make sure you calculations and frame drawing takes less cpu cycles than the computer has (ie make the program run slower than the computer can do), so that you can draw/calculate 1/25th of a sec(say) faster than 1/25th of a second, then Wait untill 1/25th of a second has finished

Probably easiest way would be you use the message stack, every 1/25(say) post a message to draw screen, and post a mesasge to do logic cycle(again lets say 1/25 of a sec).

As long as teh logic cycle takes less time than the next post for screen, and screen less time than the next post to logic, you game now updates rock solid at 25 frames a second/ (cept your bound to get stupid windows messages messing it up if either logic of drawing is nearly 1/50 of a sec)

I'm doing fixed rate tweening, and have been for years, and I have to be honest. I didn't understand anything H&K said. So if anything I'm about to say repeats on what he just said, it's not because I'm ignorant or that I'm suggesting it's wrong, I just couldn't follow it.

Fixed rate logic is really pretty simple. What you do is decouple your logic (logic is the collective term used for everything except rendering really, so stuff like moving your game objects around, collision, physics, all that stuff) from your rendering. So instead of calling update and render once each per loop, you may call each once, more than once or not at all, depending on how the game is running.

As the name suggests, logic is run at a fixed rate. So you pick a desired frame rate. This frame rate is for logic only and has nothing to do with your rendering frame rate, so I'll call it UPS - Updates Per Second - to help differentiate. Essentially, you're just deciding how many times per second you want to run all your game logic. If you have a lot of complex physics, you might want this as high as 60UPS, but most games can drop down as low as 30UPS and still be very responsive.

So each time you run your loop, you see if enough time has passed that another update is due. If your UPS rate is 30 then you'll be running an update as soon as (1000/30) 33.3 milliseconds have passed. If your game is able to run much faster, you can see that you will often run the main loop and that much time won't have passed. On these occasions, you simply won't call your logic, you'll go straight to rendering. If your game is running too slow and can't keep up, it may be necessary to run your logic twice. IE: If more than 66.6 milliseconds have passed since the last update, you'll need to update twice.

Now the problem that might have occurred to you by now is that things are going to be really jerky updating 30 times a second, and even more so when you render the same frame multiple times without calling your logic at all. And it would, if we stopped there, but we don't. What we then do is "tween" between updates. So if the time which has passed is only half way to the next update, what we then do is interpolate the position, rotation, scale, etc of everything in the scene so that it is half way between where you determined it would be at the end of the last logic update, and where it was at the end of the PREVIOUS logic update.

It's very important to understand that you're tweening backwards. I know it won't make sense at this point, but it works. You can't tween forwards because you don't know where things will be in a future update. Objects will intersect with other objects because you have not yet calculated collisions. So we always tween backwards.

And that's really the essence of the theory. For the practice, I highly recommend Gaffer on Games, whose article is excellent. Personally, I had a few problems getting my head around his theory, which is why I've given you my version as well.

http://gafferongames.com/game-physics/fix-your-timestep/

Incidentally, I'm not his crazy internet stalker. His code is not flawed. Gaffer worked for Dynamix on Tribes 2 and I think he's worked for Pandemic, Sony, etc since then. He knows his stuff. If you can get your head around his stuff, it deals with complex physics, multiplayer, whatever you'll ever have to throw at it. Makes stuff like bullet time spectactularly easy too.

Thanks guys, most helpful. Sorry for the late response.

Gabriel, that was a really good explanation. Any chance of a very simple piece of code (bare minimum) showing it in action?

I think a coversion of the Gaffer code with some comments would be a great help if you could spare a few minutes. I would be most greatful :)

Also how would you do things like bullet time as you mention?

Jason.

The Gaffer code is classic but it took me quite a few readthroughs until I finally understood the very last bit. Remember fixed rate logic means more complex drawing functions but easier logic code. Delta time is the reverse i.e. more complex logic but easier drawing. But the complications for both are really not that bad and frankly one of the two methods is essential if you don't want to rely on a timer or something.

In my games I use the method posted by Swerdnik years ago. It fixes the logic frame rate at a certain value, but it doesn't do complicated thing with drawing. I guess this means you could see stuttering in the graphics though that has never been a problem for me. But it could explain the cobwebs in my online guestbook.

Here's a post by Grey Alien ;) containing Swerdnik's code:

http://www.blitzbasic.com/Community/posts.php?topic=42173#472662

The code is also posted by Mike Boeh himself near the bottom of this thread:

http://forums.indiegamer.com/archive/index.php/t-3099.html

This follows the Gaffer method, as far as I can tell:
Global boxes:TList=New TList
'this is a box. It will accelerate under gravity, spin at a constant rate,
'and fade away as it gets lower
Type box
	Field ox#,oy#,x#,y#
	Field oan#,an#
	Field vx#,vy#,van#
	
	Function Create:box(x#,y#)
		b:box=New box
		b.x=x
		b.y=y
		b.vx=Rnd(-3,3)
		b.van=Rnd(-20,20)
		boxes.addlast b
		Return b
	End Function
	
	Method update()
		'remember previous state
		ox=x
		oy=y
		oan=an
		
		If oy>600
			'if was off screen *last* frame, delete this box
			'shouldn't use current frame to delete box, because on render it might be interpolated back onto the screen
			boxes.remove Self
		EndIf
		
		'move to new state
		vy:+1
		x:+vx
		y:+vy
		an:+van
	End Method
	
	Method draw(alpha#)
		'work out position and rotation at *apparent* time
		cx#=ox+(x-ox)*alpha
		cy#=oy+(y-oy)*alpha
		can#=oan+andiff(an,oan)*alpha	'can't just do (an-oan)*alpha because what if oan=170 and an=-170?
		
		'fade depends on y-position so can use cy instead of keeping track of old fade and new fade
		fade#=1-(cy/600.0)
		
		SetAlpha fade
		SetRotation can
		DrawRect cx,cy,20,20
	End Method
End Type

'this just gives the difference between two angles
Function andiff#(an1#,an2#)
	an1=(an1-an2) Mod 360
	If an1<-180 an1:+180
	If an1>180 an1:-180
	Return an1
End Function



'init graphics
Graphics 800,600,0
SetBlend ALPHABLEND


'set up timing
Global dt#=.01
Global t#=0
Global ctime#=MilliSecs() 'must set ctime to current time because otherwise you do millions of logic steps in the first frame!
Global accumulator#=0

While 1

	'work out time elapsed since last frame started
	newtime#=MilliSecs()
	deltaTime#=(newtime-ctime)/1000.0
	ctime=newtime
	accumulator:+deltatime
	
	If MouseX()>0
		dt=MouseX()/2400.0
	EndIf
	
	steps=0	'keep track of how many steps done this frame, just for curiosity's sake
	
	While accumulator>dt
		If Rand(10)=1	'create a box every 10 logic steps, on average
			box.Create Rand(800),0
		EndIf
		
		
		For b:box=EachIn boxes
			b.update
		Next
		
		t:+dt
		accumulator:-dt
		steps:+1
	Wend
	
	'render stage
	Local alpha#=accumulator/dt
	
	'show some information about the speed of the simulation
	SetRotation 0
	SetAlpha 1
	DrawText "dt: "+dt,0,0
	DrawText "logic FPS: "+1/dt,0,15
	DrawText "steps: this frame: "+steps,0,30
	DrawText "display FPS: "+1/deltatime,0,45
	
	'draw the boxes
	For b:box=EachIn boxes
		b.draw alpha
	Next

	Flip
	Cls
	
	If KeyHit(KEY_ESCAPE) Or AppTerminate()
		End
	EndIf
Wend


QuickSilva, I suggest you have a go at working things out yourself for a while instead of coming here and asking for code, you'll get a much better understanding of how everything works.

OK thanks for all of the info and links, still a little unsure but like Warpy says it`s probably best to give it a go myself. I`ll give it a go and post my results so you guys can tell me if I`m understanding things correctly.

One other thing I would like to ask though before I start, is tweening really needed or is it just the icing on the cake? Can the tweening be added later if need be without changing my game too much?

Finally, can tweening be added to delta timing and is it needed in the same way as with fixed rate logic?

Thanks again for everyones time with regards to this topic.

Jason.

I don't have an example I could share as I've really never used Max2D, and an example using TV3D would probably confuse you ( Quaternions and matrices, it's all 3D. )

Is tweening really needed? Not necessarily, but you will need to increase your logic rate if you don't use it. With tweening, your logic can go at 30 UPS or even 20 or 15 and still be very smooth and responsive. Without tweening, you would need to run your logic at least 60 UPS or possibly much higher. I've seen people running their logic at 100 or even 200 UPS. If your logic is very simple, a casual game for example, then this might be fine. With a physics engine, AI, pathfinding, etc, 200 UPS would kill you.

Can tweening be added later? Yes, but I'd advise against it. In theory it shouldn't be too hard, but you might program things in a way which makes it difficult to add tweening later. If you do everything with tweening in mind, you'll avoid that.

No, tweening can't be added to delta time, because tweening is a visual update without a logic update. Delta time doesn't let you update visuals without updating logic, so there would be nothing to tween between.

No, it's not necessary, or even advantageous to tween with delta timing, for the same reason. Delta timing is all about going as fast as you can, so there's never anything to tween.

Tweening is purely about keeping the screen updates as smooth as possible when you're running at a low logic rate. That's not the problem with delta timing. The problem with delta timing is that you can't use it with a physics engine or any kind of numerical integration or any kind of calcuation where the result must be consistent, because your simulation can and will "explode". Also you can't lower the rate of your logic right down with delta timing, which can be vital if you're CPU bound, as most games are these days. These are the problems that are solved by using fixed rate logic. Tweening is just something you add to fixed rate logic to ensure that you're not sacrificing any visual smoothness in order to fix all those other problems.

Thank you for your insight, Gabriel

Thanks Gabriel that has help me understand things a great deal better. This thread has really helped in general.

One a final note before I go and put this into action, with delta timing I can simply set the delta value to delta*.1 to get slow motion (bullet time effect) I cannot see how this is done with fixed rate logic. Is it simple to do?

Jason.

Same thing with render tweening. Just pass a smaller timestep to your logic code and let the tweening carry on as before. Obviously your timestep won't be fixed if you pass in a smaller timestep, but so long as the value is constant during bullet time and constant out of bullet time ( ie: you're only ever passing in two different values ) it shouldn't cause any problems.

You can also pause everything in your game with this too. Just pass 0 to your logic and if your logic code is correct, everything should stop.

Cool, thanks :)

Jason.

@Foppy: Wow that's ancient, and also in BlitzPlus. I adapted that for my framework when I started using BlitzMax an added in a delta element for ultra smoothness, but it means it's no longer "proper" Fixed Rate because varying sizes of delta can get passed into the logic functions.

@QuickSilva: Yeah like Gabriel says choose a method, test it, make sure you understand it (like how it applies to movement, acceleration/deceleration, gravity, timers, drawing etc.) and then stick to it. Changing timing methods mid game would probably be a code refactoring nightmare.

Fixed rate with higher logic cycles like Ga's framework is ideal for today's modern machines. For older computers I still like delta where each loop does 1 logic/render update. As long as you cap the delta min/max it's a breeze to code.

There`s an example in Krylars book called `The Rolling Timer`. After reading through this thread I assume that it is basically doing the same thing. Am I correct or is this a different method altogether?


; Initialize our main timer.
Main_Timer=Millisecs()

While Not KeyHit(1)
    ; What`s the difference in time since our last check?
    ElaspedTime=Millisecs()-Main_Timer
    
    ; Slowing donw. Clamp update to 40 FPS. (1000/40=25)
    If ElapsedTime>25
        ClampValue=ElapsedTime/25
        For i=1 to ClampValue
            ; Update objects here. 
        Next 
        
        ; Add appropriate offset to Maint_Timer
        Main_Timer=Main_Timer+ClampValue*25  
    Else
        ; Update objects as normal and reset Main_Timer to current time.
        Main_Timer=Millisecs()
    EndIf
Wend



@Grey Alien :

Your framework uses one of the smoothest timing methods around. Do you use tweening or is the high logic rate that you use (200 I recall) enough to avoid this? You also say that you use a delta element for extra smoothness. How do things look without this added?

Also how is the slow motion achieved in your framework if you do not mind me asking? Can you give me a small example of what needs to be added to make this work?

Thanks,

Jason.

@QuickSilva: Yeah I use 200 logic updates per second. This is normally 3.something logic updates per frame. For the first 3 whole logic updates Delta is 1 and for the last update Delta is <1. This makes it very smooth. Without the final fractional delta it is less smooth. Slow motion was achieved by applying a multiplier to delta (the multiplier is less than one). Did you see this in action yet with the bullet time on the Mega Pill explosion in Unwell Mel (level 7), it's rather spectacular :-)

Yes I did. It does indeed look very cool in action. You did a great job on that game. Thanks for answering my question too.

Jason.

@ MGE or anyone else who knows the answer,

When you say cap the delta values min and max values could you please explain how this would be done?

Jason.

Let's say the game experiences a huge delay and instead of Delta being in a "normal" range you get a silly value like 200, well then everything would move WAY too far so you are better off capping it by saying If Delta>2 then Delta=2. 2 is just a Max number I've picked here but it could be whatever you feel is suitable. Not quite sure why you'd cap the lower value though (cap is also probably the wrong word for a lower value unless it goes negative but Delta shouldn't do that unless the timer rolls round on the PC, so you could cap it at 0.)

Ah I see, so simple :) Thanks for the explanation.

Jason.

After taking in all of the great info provided in this thread I`m finally starting to understand things. I just wanted to say a big thanks once more to everyone who helped. I`m sure that this thread will help many others too in the future.

One thing I wanted to ask is that delta timing never seems to be quite as smooth as fixed rate logic. Is this common? Can both methods be made to produce the same smooth look? I`ve even tried a steady delta timing method where the delta only gets updated if it changes by a certain amount. This is still not as smooth as fixed rate timing. Here`s the link to that code,

http://www.blitzbasic.com/codearcs/codearcs.php?code=431

After seeing the benefits I am starting to like fixed rate logic more and more but I would still like to know if the same smoothness can be achieve with delta timing.

Jason.

This is how I do it
Global OldMillisecs:Long=MilliSecs()
Global NewMillisecs:Long=MilliSecs()
Global DeltaString:String = CurrentTime$()
Global DeltaTimePassed:Long = 0
Global DesiredFPS:Long = 60

Global UserUpdate:Long = NewMillisecs

Function CheckDeltaTime()
	'called every cycle but updated every second
	If CurrentTime$()<>DeltaString
		OldMillisecs = NewMillisecs
		NewMillisecs = MilliSecs()
		DeltaString = CurrentTime$()
		
		DeltaTimePassed = NewMillisecs - OldMillisecs
	EndIf
End Function


then call the CheckDeltaTime at the end of the main loop and to do a check do this (DesiredFPS is something like 60 for 60fps)
	If MilliSecs()>UserUpdate+ ( DeltaTimePassed /DesiredFPS )
            'update goes here
        Endif


Hope it helps

One thing I wanted to ask is that delta timing never seems to be quite as smooth as fixed rate logic. Is this common? Can both methods be made to produce the same smooth look?

Yes, it's common that fixed rate logic with tweening will be smoother on machines which can cope well with your game, simply because you spend more time rendering and less time needlessly updating logic. Unless you run your logic at a very high rate, in which case, it will be more or less the same. On slower machines, where the computer is struggling to keep up, I would expect delta time and fixed rate logic to produce similar results.

@ Grey Alien :

You mention that you use a delta value also in you timing code. Is this a replacement for tweening or do you just choose not to tween? How does your timing code look when you set a low logic rate of say 10? Does your delta value have the same effect as tweening?

@ All

The tweening part seems to be the most difficult to implement. Is it hard to do? This is what I am struggling with the most I think. I could just leave that part out but I want to try to understand it.

Also what sort of machines (spec-wise) would be capable of running at a logic rate of 200?

Jason.

Also what sort of machines (spec-wise) would be capable of running at a logic rate of 200?
It depends on the code that is executed. But with my games I have no problem running them at 200 fps on my 6 year old 1.7 Ghz pc. If I were to write an RTS game with lots of pathfinding computations I would program it in such a way that one computation is divided over multiple logic frames.

The tweening part seems to be the most difficult to implement. Is it hard to do?

No, it's a piece of cake in 2D. You're just doing linear interpolation between two positions, two scales, two colors, two rotations, etc. I suppose the rotations offer the biggest challenge since you would need to ensure that they tween the correct way. IE: Interpolating between angles of 1 and 359 would need to be done the short way and not the long way. But even that is just a case of a simple condition to check for it.

In 3D, it's slightly more difficult because you can't tween euler angles and you have to use quaternions which can be easily interpolated with a Spherical Linear intERPolation. In 2D, it's dead simple. All you're really doing is the exact same equation you were doing with delta time except that instead of moving something by 2*Delta pixels you're positioning it at (End-Start)*Tween pixels.

Excellent. I finally think that I understand it now.

Cheers guys!

Jason.

Is it possible to get the true running speed of my program using fixed rate logic?

With delta timing you can set flip to false to get a true reading but if you do this with fixed rate logic it peaks at a certain value. I`m assuming that this is the correct behaviour or am I missing something?

Jason.

@QuickSilva: If the logic rate was reduce below about 60Hz with my method it would start to look crappy (because there is no tweening, that's for rendering only). The delta is not used for tweening, it just smooths out the final logic iteration per frame.

Cheers Grey for clearing that up.

With tweening, size, rotation, scale etc... obviously need to be tweened but what about animation frames? Do we need to do anything fancy with those incase a frame may not have been the same half a step back in time? Is this being too picky or does it need to be looked into.

I`m talking purely 2D games here. Would there be any visible benefit from doing this? I`m guessing not but I just wanted to ask those that may have tried to do it in their own work.

Also the way I understand it is that if you are running at a logic rate of 10 you are saving more cpu time than if you are doing a 200 logic rate. When calling up the task manager to see if this is true both values are giving me about the same cpu usage. Why is this? Adding a delay 10 into my main loop lowers this value in both cases. Is adding this good practice or not?

Finally, logic updates can vary between frames, rendering only ever occurs once per frame. Is this correct? The rendering part is now confusing me a little as when I try to calculate my true fps it seems to reach a limit and then stop. I`m not sure why this is, I`m obviously misunderstanding something with regards to when the rendering takes place. Maybe this is the correct behaviour?

Should I be using flip false or flip true? Are there benefits to each method? Flip true seems to keep the cpu usage at a much lower reading but should I locking to the refresh rate or not?

Any further advice would be most appreciated.

Jason.