Frame Limit
BlitzMax Forums/BlitzMax Beginners Area/Frame Limit
I am trying to do Frame Limiting code... This is what I came up with. Any help would be appreciated.
I use timer ticks if there is more than one per frame I am skipping the drawing phase.
Regards,
Eric
SuperStrict
Global FrameTimer:TTimer=CreateTimer(60)
Global Skipped:Float
Global Drawn:Float
Graphics 640,480
Repeat
Local Ticks:Int=Frametimer.Wait()
If KeyDown(Key_Space)
Delay (14)
End If
'Update Logic Every Frame
If Ticks<=1
Cls
'Update Draw As much as possible
DrawText "Draw Frame",320,0
DrawText "Frames Skipped"+Skipped,320,15
DrawText Float(Skipped/(Drawn+Skipped))+"% Skipped",320,75
Drawn:+1
Flip 0
Else
Skipped:+1
End If
Until KeyHit(Key_Escape) Or AppTerminate()
Well...This is working for crap.
How do people get such smooth movement in their games?
SuperStrict
Global FrameTimer:TTimer=CreateTimer(60)
Global old_tick:Int , new_tick:Int
Global counter:Int
Graphics 640,480
Repeat
'independent of tick
counter:+1
new_tick = TimerTicks(Frametimer)
If new_tick>old_tick
Cls
DrawText "Ticks:"+new_tick,100,100
DrawText "Counter:"+counter,100,116
Flip
old_tick = new_tick
EndIf
Until KeyHit(Key_Escape) Or AppTerminate()
Bradford,
This is what you are using in your game?
Eric
similar. this is just another way of interleaving processes between a timed render
you can set logic timers that fire off in a staggered way.
you might not want to update AI or physics every frame drawn or if you are doing networking you could time packets
just don't use timers. Track the last time and compare to the current time to work out a different (your delta) then move everything multiplied by this amount and then draw a frame. That's simple delta timing. Fixed rate logic is similar but allows you to run logic at a high FPS.
Grey is right. I don't know what i was thinking on the earlier post. Using a Delta might be a better way to do this. here is a simple one i put together
Strict
SetGraphicsDriver GLMax2DDriver()
Graphics 800,600,32
Global TimeDelta:Float
Global RightNow:Int
Global Before = MilliSecs()
Global FPS:Float = 60
AutoMidHandle(True)
Local GW = GraphicsWidth()
Local GH = GraphicsHeight()
Local midw=GW/2
Local midh=GH/2
Local angle:Float
Local Thrust:Float
Local ThrustX:Float
Local ThrustY:Float
Local Angular_Velocity:Float
Local Friction:Float = 0.99
Local Xpos:Float = Float(midw)
Local Ypos:Float = Float(midh)
Local Velocity:Float
Local Xvelocity:Float
Local Yvelocity:Float
Local ship:timage = CreateImage(64,64)
' draw the ship...
SetColor 0,255,0
DrawLine 0,0,64,32
DrawLine 64,32,0,64
SetColor 50,50,50
DrawRect 0,16,32,32
SetColor 255,255,0
DrawOval 36,24,22,16
GrabImage(ship,0,0)
Cls
Repeat
' Calculate the Time Delta and divide it
RightNow = MilliSecs()
TimeDelta = Float(RightNow - Before)/60
Before = RightNow ' Changed from Before = MilliSecs() thanks Grey Alien
' Calculate the Heading of the ship (the Angle)
Angular_Velocity:*Friction ' slow the spin
angle:+Angular_Velocity * TimeDelta ' apply the angular velocity to the current Angle
' Calculate the Speed
XVelocity:*Friction ' slow the speed down (I know there is no friction in space but It looks better :)
Yvelocity:*Friction
Thrust:*0.96 ' this reduces the thrust amount each frame
' Here we are dividing the Thrust into its X and Y components
ThrustX = Thrust*Cos(angle)
ThrustY = Thrust*Sin(angle)
' now we add the resulting Thrust to the Velocities
Xvelocity:+ Thrustx * TimeDelta
Yvelocity:+ Thrusty * TimeDelta
' AND update the ships current X,Y Cartesian Coordinates
Xpos:+XVelocity * TimeDelta
Ypos:+YVelocity * TimeDelta
' Get Key Input
If KeyDown(KEY_UP) Then Thrust:+0.02
If KeyDown(KEY_LEFT) Then Angular_Velocity:-0.2
If KeyDown(KEY_RIGHT) Then Angular_Velocity:+0.2
' WRAP to SCREEN
If xpos > GW Then Xpos = 0
If ypos > GH Then Ypos = 0
If Xpos < 0 Then Xpos = GW
If Ypos < 0 Then Ypos = GH
Cls
' Draw the stuff to the screen
SetRotation angle
DrawImage ship, Xpos , Ypos
SetRotation 0
SetColor 255,0,0
DrawRect midw-20,midh-20,40,40
SetColor 255,255,255
Flip 1
Until KeyDown(KEY_ESCAPE)
I Just tried the above code and I still get "Hiccups" in the movement. Do you? I have to stop coding my game until I figure out how to get smooth running graphics without the hiccups in movement.
good little demo, smooth here. I would make a teeny change though. Change Before = MilliSecs() to Before = RightNow so that Millisecs() is only called once and it's value is transferred to Before via RightNow.
I'll have to try it on my home computer.
Grey out of curiosity do you have a CRT or LCD. It seems that since I got an LCD things do not run as smoothly.
I'm not sure why...Can a monitor do that or am I just losing it.
Noel,
Cool piece of code, I have no idea how it works because I truly don't understand Hooks.. Can you explain this to me.. I try not to just plug in code until I understand how it works.
Thanks,
Eric
Basically, hook functions are functions that are called when a hook is run. Think of a hook function as something that handles an event. In the case of my code, calling flip is an event that you want the frame limiter to be aware of since it means that you're done drawing and the frame is done with.
The flip hook is called before the actual flip occurs (that is, Flip() calls the hooks then swaps the buffers), so you can draw stuff in a flip hook as well. It's rather convenient, but beside the point.
Anyways, what my code does is that when you call Flip, it increments the frame counter and gets a new delta time value. There is also a timer that is run only once per second. Each time the timer ticks, it queues an event that the hook picks up. When it ticks, the FPS is changed and the frame counter is reset (frames per second, resets once per second, etc.).
One thing to note: my code does not work with multiple canvases with MaxGUI as far as I know. That can be changed simply by making the timing update code a normal function instead of a hook and split the timer hook off into a new function. I'd do this myself, but the existing implementation is sufficient for my purposes.
I need to stop writing vaguely lengthy posts like this...
Grey,
thanks. changed my code.
Noel,
That's some fancy code you have there. a little too complicated for my feeble brain :)
Eric,
I'll see if i can figure out why you are getting hiccups in teh movement. what is your setup (PC, Graphics card, OS, etc)
try to comment out this line
SetGraphicsDriver GLMax2DDriver()
the defaut is the DX driver. maybe that will work better on your PC
Mini-tutorial in code.
Const USE_HOOK=0
Global Running:Int = True
Graphics( 640, 480, 0, 0 )
If USE_HOOK Then
Rem
This is the same
EndRem
While Running
While PollEvent( )
' Check the event ID
Select CurrentEvent.id
' If a key is released
Case EVENT_KEYUP
' And if it's the escape key...
If CurrentEvent.data = KEY_ESCAPE Then
' Then we're done
Running = 0
EndIf
' Or if the nice, pretty, very tempting-to-press X in the corner is pressed
Case EVENT_APPTERMINATE
' Then we're also done
Running = 0
Default
End Select
Wend
Wend
Else
Rem
As this
EndRem
Rem
The ID for this is EmitEventHook whenever it's called. If you use the
same function for multiple hooks, you can check the ID to see which hook
is being called.
Data is the data. In this case, it's a TEvent object.
Context is anything you pass to AddHook's context argument. It's helpful
if you want to associate a hook with a specific object. For example, if you
add an event hook for a specific GUI gadget, you could pass the gadget as the
context and if the hook applies to the context then use it, otherwise pass.
EndRem
' Bet you didn't know you could have functions nested in If constructs..
Function EventHook:Object( id:Int, data:Object, context:Object )
' Safety: check if the hook ID is what we're expecting.
' Not really necessary.
If id <> EmitEventHook Then Return data
' More safety: Really, only this part is necessary if you aren't using a
' function with more than one hook ID
Local e:TEvent = TEvent( data )
If Not e Then Return data
' Rinse and repeat
Select e.id ' The event ID
Case EVENT_KEYDOWN
If e.data = KEY_ESCAPE Then
Running = 0
EndIf
Case EVENT_APPTERMINATE
Running = 0
Default
End Select
Return data ' Return the data so any other hooks can still work with it
' If you don't want other hooks to work with it, you'd return Null, obviously
' But you have to make absolutely sure that other hooks do check for Null data
' and skip it when they are given null data, otherwise you are going to have a
' runtime error when you try to access the null data.
End Function
' Make EventHook a hook that is called whenever EmitEvent is called
AddHook( EmitEventHook, EventHook )
While Running
While PollEvent( )
Wend
Cls
Flip
Wend
EndIf
End
Bradford,
I ran the code and it hiccups both ways until the program runs for a while then it settle out.. I'm wondering if my computer is the cuplrit.
Noel,
Wow thanks... I think I finally understand this hook business as little better. Time to dig in and see what I can come up with.
With the highest regards,
Eric
Noel,
I'm taking a look at your code for game timing and I like it. So far so good. I have a question...can you explain how you derived?
deltat = deltat*.005+((ctime-ltime)*fps)*.995
Thanks,
Eric
Thats a quite "easy" and intelligent formula:
deltat*0.005 is like "remembering the previous value" with a bias of 0.005. It takes the old knowledge partially into equation for the new knowledge.
The other half is the regular timing equation for deltatiming.
ctime - ltime = time difference since last render (currentTime - lastTime)
fps is the fps modifier for the desired fps rate (1 / fps#) *at least I assume so* and 0.995 is the rest from 1.0 (0.005 is used for remembering the past deltat) which is needed to calculate 1.0 (= 100%) of the current deltat.
Why is noel using 0.5% of the previous delta time value? Does this make smoother animation?
don't know ... to me it doesn't make that much sense either as it underweights the "past" ... I would have used a value of 0.2 - 0.3 to prevent "fps fluctuation" and make the FPS beeing smoother
Ok Thanks Dreamora I missed the .995 and .005 correlation.
yeah I thought 0.5% is a tad small.
It's been working very nicely for me, so I don't have any plans on changing it.
It seems to be doing nicely for me also... Thanks alot for this Noel.. The only reason I asked was to understand the code.
Best Regards,
Eric