FixedRateLogic needs converting from BMax to GLBasic!!!

Previous topic - Next topic

Amon

Can anyone help convert this fixed rate logic code from BlitzMax to GLbasic, please?

Code (glbasic) Select
Global Delta:Float
Global FixedRateLogic: TFixedRateLogic = New TFixedRateLogic
Global width:Int = 100
Global x# = 0
Global change# = 0

Function ccCalcMSPerFrame#(theFrameRate%)
Return 1000.0/theFrameRate 'use .0 to ensure Float is returned
End Function

' -----------------------------------------------------------------------------
Type TFixedRateLogic
'Use to keep a game's logic running at a fixed rate, independent of refresh rate.
Field Framerate# = 200 'Speed you want the logic to run at.  Float on purpose so calculations end up as float
Field LastTime:Double
Field NumTicks:Double
Field LastNumTicks:Double
Field MS# = 0 'milliseconds per frame Float

Method Init()
CalcMS()
'Set a first value for LastTime (it will be used in the main loop)
'This method should also be called if gameplay is paused for any reason such as
'showing Paused or a Menu, or focus regained after loss of focus.
LastTime = MilliSecs()
NumTicks = 0
LastNumTicks = 1
SetDelta(1)
End Method

Method CalcMS()
MS = ccCalcMSPerFrame(Framerate)
End Method

Method Calc()
'Used Fixed Rate Logic (Retro64 method) to calculate number of Logic Loop interations
'For our purposes, a tick is based on FRAMERATE.
'Get the time and make sure at least 1 tick expired

Local TmpMS:Double 'float isn't accurate enough because Millisecs is such a big number

'New code JB 30/04/06
Local Now:Int = MilliSecs()

'little sanity check here in Case the timer flips back To 0 :)
If Now < LastTime Then
NumTicks = LastNumTicks 'best we can do
Else
TmpMS = Now - LastTime
NumTicks = TmpMS/MS
EndIf
LastTime = Now

'Account for when the user alt-tabs (i.e. don't allow a silly amount of logic iterations)
'Remember NumTicks is not in millisecs, it's Millisecs/MS and MS is normally 5 if framerate is 200.
If NumTicks > 100 Then NumTicks = LastNumTicks 'use the last sensible value (hopefully)
If NumTicks > 50 Then NumTicks = 50 'prevent too many iterations on really slow machines.
LastNumTicks = NumTicks
End Method

Method SetDelta(d#)
'Sets a global variable called Delta for use with all movement
Delta = d#
End Method
End Type

Function MainLogicLoop()
FixedRateLogic.Calc()

Local oldx# = x
'Now we do the logic loop ... from 1 not 0!
For Local i:Int = 1 To Floor(FixedRateLogic.NumTicks)
FixedRateLogic.SetDelta(1)
LogicWrapper()
Next
'Is there a remaining bit in the Numticks float?
Local remainder# = FixedRateLogic.NumTicks Mod 1
If remainder > 0 Then
FixedRateLogic.SetDelta(remainder)
LogicWrapper()
EndIf
change:Float = x - oldx
End Function

' -----------------------------------------------------------------------------
Function LogicWrapper()

End Function

' -----------------------------------------------------------------------------
Function Draw()

End Function

FixedRateLogic.Init()
Global ScreenUpdate:Double

While Not KeyHit(KEY_ESCAPE)
MainLogicLoop()
Cls
Draw()
' Flip 0 'try this to see how it stays the same speed even with VSync off
Flip 1
Wend
- http://www.amon.co | Dev Tools - BlitzMax - Shiva Unlimited - Unity Pro - Carrara 8 Pro - Hexagon 2.2.5 - Blacksmith 3D Paint - FuturePaint Pro - Bryce 7 Pro.
I'm never wrong at all; I just discover 1000 ways that don't work!

Moru

There are several GLBasic versions on the boards already, look around :-)

Kitty Hello


Amon

Nope, limitFPS causes jitters everytime I use it. FixedRate logic seperates the Drawing from the logic and processes the logic seperately. This keeps the framerate the same no matter the computer it runs on (within reason).
- http://www.amon.co | Dev Tools - BlitzMax - Shiva Unlimited - Unity Pro - Carrara 8 Pro - Hexagon 2.2.5 - Blacksmith 3D Paint - FuturePaint Pro - Bryce 7 Pro.
I'm never wrong at all; I just discover 1000 ways that don't work!

Moru

If you set LIMITFPS to 60 and the computer running the game can only manage 30 FPS because it's too slow, all your timed events will run at half speed. Did I understand the problem right? :-)

Kitty Hello

Yes, sort of. But it will skip any delays until it will reach full 60 FPS then.
And the method he suggests runs at 60 fps, because it skips the drawing then?

Amon

Yep! Drawing and logic are in 2 different function and processed seperately. The logic is processed at 200ticks while the drawing is left completely to the refresh rate. This way you get smooth movement and perfect logic processing.

I hope that makes sense? :)

LIMITFPS ont he other hand probably processes it all and like Kitty says waits for 60fps again and this is where the jitter happens.
- http://www.amon.co | Dev Tools - BlitzMax - Shiva Unlimited - Unity Pro - Carrara 8 Pro - Hexagon 2.2.5 - Blacksmith 3D Paint - FuturePaint Pro - Bryce 7 Pro.
I'm never wrong at all; I just discover 1000 ways that don't work!

Amon

Quote from: Ocean on 2010-Feb-09
Quote from: Amon on 2010-Feb-09
Yep! Drawing and logic are in 2 different function and processed seperately. The logic is processed at 200ticks while the drawing is left completely to the refresh rate. This way you get smooth movement and perfect logic processing.

I hope that makes sense? :)

LIMITFPS ont he other hand probably processes it all and like Kitty says waits for 60fps again and this is where the jitter happens.

that makes total sense to me.... =D

Ocean

:)
- http://www.amon.co | Dev Tools - BlitzMax - Shiva Unlimited - Unity Pro - Carrara 8 Pro - Hexagon 2.2.5 - Blacksmith 3D Paint - FuturePaint Pro - Bryce 7 Pro.
I'm never wrong at all; I just discover 1000 ways that don't work!

sf-in-sf

Hi!
I am facing an other problem, when I want to transfer my generative pgm to an android tablet for the multi-touch function. (it's more intuitive to use fingers than typing numbers in a console). The logic/renderer can be very long, and it must be interrupted whenever a touch/event happens from the fingers, for a decent responsiveness. What is the way to set a kind of real-time interrupt (or vector) like with the microcontrolers? ...that only reacts to the system ticks/time/clock (usually in ms), independently of the FPS / refresh rate.

In other words, LIMITFPS becomes useless in this case, when the logic takes much longer than the refresh rate. What's the way around it? Thank you!   (Please note: LIMITFPS is v. useful to synchronize some short logic with a steady frame-per-second rate, for good visual results.) 
On the day the atom is a cube I will start believing in the square pixel.

erico

 :offtopic: Nice dig, I see a quote from Ocean, but can´t see his post, is he gone from the boards? Damn he is a nice chap.

spacefractal

You can use a frame skip measure when the update() called twice when the render is over budget. Or you can use timer based measure.

It's depend on game wath the used best.

Recently I'm let user set 30 or 60fps in both karma miwa (using timer measure) and greedy mouse (using frameskip measure).

Also on some devices multi touch can slowdown the device quite much, which is nothing I'm can do.
Genius.Greedy Mouse - Karma Miwa - Spot Race - CatchOut - PowerUp Elevation - The beagle Jam - Cave Heroes 2023 - https://spacefractal.itch.io/

MrTAToad

Usually you would set LIMITFPS to -1 to allow it to go as fast as possible, and then use a rate logic routine, with one being (which I converted from BlitzMax ages ago) :

Code (glbasic) Select
// --------------------------------- //
// Project: AppTimer
// Start: Thursday, September 11, 2008
// IDE Version: 5.360

//! This help file contains information for using the AppTiming system, which can be used to give a consistent movement speed
//! independant of the processor speed
//! The original code was for BlitzMax, and was written by JoshK
//! Converted to GLBasic by Nicholas Kingsley
//! You call the routines like :
//! LOCAL blah AS TAppTime
//! blah.Initialise_AppTime()
//! speed=blah.Update_AppTime()

TYPE TAppTime
AppTime_UPS;AppTime_Iterator;AppTime_CurrentTime;AppTime_PauseStart;AppTime_Speed;AppTime_DesiredLoopTime;AppTime_LastUpdateTime
AppTime_LastUPSTime;AppTime_DesiredFrequency%

//! This function initialises the AppTimer system, and must be called first.
//\param frameRate% - This is the frame rate that you want all movement to be based on.  It defaults to 60 FPS, which is the lowest rate that should be used.
//\return Nothing is returned
FUNCTION Initialise%:frameRate%=60
LOCAL sW%,sH%

self.AppTime_UPS=0.0
self.AppTime_Iterator=0.0
self.AppTime_CurrentTime=0.0
self.AppTime_PauseStart=0.0
self.AppTime_Speed=1.0
self.AppTime_LastUpdateTime=0.0
self.AppTime_LastUPSTime=0.0
self.AppTime_DesiredLoopTime=1000.0/(frameRate*1.0)
RETURN TRUE
ENDFUNCTION

//! This function updates the timing system.  It needs to be called once per loop.
//\param No parameters are passed
//\return A movement value is returned.  This should be muliplied by the amount you want to move.  If the routine is paused, then 0.0 is returned
FUNCTION Update:
LOCAL time
LOCAL elapsed

IF self.AppTime_PauseStart<>0
RETURN 0.0
ENDIF

time=GETTIMERALL()

IF self.AppTime_LastUpdateTime=0.0
self.AppTime_Speed=1.0
self.AppTime_LastUPSTime=time
ELSE
elapsed=time-self.AppTime_LastUpdateTime
IF elapsed=0.0
elapsed=1.0
SLEEP 1
INC time,1.0
ENDIF

self.AppTime_Speed=elapsed/self.AppTime_DesiredLoopTime
ENDIF
self.AppTime_LastUpdateTime=time
self.AppTime_CurrentTime=time

INC self.AppTime_Iterator,1.0 // Its a float as it can go very large...

IF self.AppTime_CurrentTime-self.AppTime_LastUPSTime>=1000.0
self.AppTime_UPS=self.AppTime_Iterator/((self.AppTime_CurrentTime-self.AppTime_LastUPSTime)/1000)
self.AppTime_LastUPSTime=self.AppTime_CurrentTime
self.AppTime_Iterator=0
ENDIF

RETURN self.AppTime_Speed
ENDFUNCTION

//! This function is used when your program is paused or not.  If it is, then the step speed is still updated
//\param pause% - If this is TRUE, then your program is paused.  If FALSE, then it isn't
//\return Nothing is returned
FUNCTION Pause%:DoPause%
LOCAL elapsed

IF DoPause%=TRUE
IF self.AppTime_PauseStart=0.0
self.AppTime_PauseStart=GETTIMERALL()
self.AppTime_UPS=0
self.AppTime_Speed=0
ENDIF
ELSE
IF self.AppTime_PauseStart<>0.0
IF self.AppTime_LastUpdateTime<>0.0
elapsed=GETTIMERALL()-self.AppTime_PauseStart
INC self.AppTime_LastUpdateTime,elapsed
ENDIF

self.AppTime_PauseStart=0.0
self.Update()
ENDIF
ENDIF
ENDFUNCTION

//! This function returns the updates per second
//\param No parameters are passed
//\return Updates per second
FUNCTION Speed:
RETURN self.AppTime_Speed
ENDFUNCTION
ENDTYPE



Ian Price

You do realise that the original post was created 5 years ago?

:S
I came. I saw. I played.

bigsofty

Strangely enough, I have just went from fixed rate logic back to frame based. I am doing a little retro shmup and realised that I actually wanted everything to slow down if things get hectic and not just jerk about trying to keep up with fixed rate logic extrapolation if below the ideal fps. Dumping deltatime felt completely alien to begin with, its strange how you get 'too' used to these things.  :S
Cheers,

Ian.

"It is practically impossible to teach good programming style to students that have had prior exposure to BASIC.  As potential programmers, they are mentally mutilated beyond hope of regeneration."
(E. W. Dijkstra)

spacefractal

When read the sf post which has was created yesterday I'm could eventually split that to own topic since he faced intoxicated similar issue but he did not created a new thread as he really should.
Genius.Greedy Mouse - Karma Miwa - Spot Race - CatchOut - PowerUp Elevation - The beagle Jam - Cave Heroes 2023 - https://spacefractal.itch.io/