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.

My timing code.

FPS/Delta Timing

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