Introduction:
The Main Cycle:
To enforce the rules we established above, we'll use time-stamping.
Time-stamping is the act of saving into a variable the exact time (unix time) something was done.
Your timestamp variable should always be global (unless you're using C/C++ and pointers) and its type must be Long.
Enforcing the rules:
Conclusion:
I came across this challenge about one year ago, when I was trying to integrate sprites that were designed to be displayed at 24 FPS on a 60 FPS game loop.
For the sake of simplicity, the subject of sprite animation won't be covered at all in this tutorial.
Although this tutorial may seem videogame focused, remember you may apply this concept in any kind of project.
For the sake of simplicity, the subject of sprite animation won't be covered at all in this tutorial.
Although this tutorial may seem videogame focused, remember you may apply this concept in any kind of project.
The Main Cycle:
The Main Cycle is usually a Timer but it can been anything kind of loop, for example, the LibGDX Render event.
60 cycles per second can described either as 60Hz (Hertz) or 60FPS (Frames per Second).
It's pretty much the same, nothing more than two different terms describing the cycle frequency.
The term FPS is usually applied in graphical focused cycles, such as the ones found in game engines or video players.
Setting a timer to run at this speed it's a pretty simple thing to do.
Since Timers are set in milliseconds, the code will be: myTimer.Initialize("myTimer", 1000 / 60).
The reason I'm using the this value (60) is pretty much arbitrary.
60Hz is the usual screen refresh rate, unless you have a 3D-TV (120Hz or more).
If you want your cycle to run "as fast as possible", set the Timer interval to 1 millisecond.
myTimer.Initialize("myTimer", 1)
Now that we have our main cycle set-up, let's give it something to do.
Imagine your game sprites were designed to be displayed at 24FPS.
Can you already spot the problems we could encounter when running this code?
Not only the sprite animation would seem to be played in "fast-forward", accessing your online database dozens of times per second would most likely overload your server and/or game with requests.
To solve this, we need to establish some rules.
1. The animation function shall only be called 24 times each second.
2. The current score shall only be uploaded (in background) every 10 seconds.
3. The current highscores shall only be retrieved (in background) every 60 seconds.
Time-stamping:60 cycles per second can described either as 60Hz (Hertz) or 60FPS (Frames per Second).
It's pretty much the same, nothing more than two different terms describing the cycle frequency.
The term FPS is usually applied in graphical focused cycles, such as the ones found in game engines or video players.
Setting a timer to run at this speed it's a pretty simple thing to do.
Since Timers are set in milliseconds, the code will be: myTimer.Initialize("myTimer", 1000 / 60).
The reason I'm using the this value (60) is pretty much arbitrary.
60Hz is the usual screen refresh rate, unless you have a 3D-TV (120Hz or more).
If you want your cycle to run "as fast as possible", set the Timer interval to 1 millisecond.
myTimer.Initialize("myTimer", 1)
Now that we have our main cycle set-up, let's give it something to do.
B4X:
Sub myTimer_Tick
Move_Player
Move_Enemy
Collision_Detection
Animate
Display_Current_Frame
Upload_Score_To_Online_Database
Retrive_Current_Highscores_From_Online_Database
End Sub
Imagine your game sprites were designed to be displayed at 24FPS.
Can you already spot the problems we could encounter when running this code?
Not only the sprite animation would seem to be played in "fast-forward", accessing your online database dozens of times per second would most likely overload your server and/or game with requests.
To solve this, we need to establish some rules.
1. The animation function shall only be called 24 times each second.
2. The current score shall only be uploaded (in background) every 10 seconds.
3. The current highscores shall only be retrieved (in background) every 60 seconds.
To enforce the rules we established above, we'll use time-stamping.
Time-stamping is the act of saving into a variable the exact time (unix time) something was done.
Your timestamp variable should always be global (unless you're using C/C++ and pointers) and its type must be Long.
B4X:
Sub Process_Globals
Dim animationTimestamp As Long
Dim dbUploadTimestamp As Long
Dim dbDownloadTimestamp As Long
End Sub
Let's start with the Animate function.
In order to time-stamp it, all we have to do is add the following line of code:
Alright! Now, every time the functions is called, its time-stamp will be overwritten with the current value of DateTime.Now.
From this point on we are prepared to establish a way of enforcing our rules.
In order to time-stamp it, all we have to do is add the following line of code:
B4X:
Sub Animate
animationTimestamp = DateTime.Now
End Sub
From this point on we are prepared to establish a way of enforcing our rules.
Enforcing the rules:
Before we proceed, let's create a custom Type (struct) in Process_Globals to keep things more organized.
We're now ready to add our secret sauce to our Animate function.
An alternative way to achieve the same result would be using a Return instruction:
For the Database access functions, well use exactly the same code, we only need to change their delay values:
The Main Cycle code will stay the same, but it would be a good practice to add some comments.
B4X:
Type AccessControl(Timestamp as Long, Delay as Int)
Dim acAnimate as AccessControl
B4X:
acAnimate.Delay = 1000 / 24 ' = 41 milliseconds
B4X:
Sub Animate
If (DateTime.Now - acAnimate.Timestamp) >= acAnimate.Delay Then
acAnimate.Timestamp = DateTime.Now
'The animation code goes below
...
...
...
End If
End Sub
B4X:
Sub Animate
If (DateTime.Now - acAnimate.Timestamp) < acAnimate.Delay Then Return
acAnimate.Timestamp = DateTime.Now
'The animation code goes below
...
...
...
End Sub
For the Database access functions, well use exactly the same code, we only need to change their delay values:
B4X:
acDBUpload.Delay = 10000 '= 10 seconds
acDBDownload.Delay = 60000 '= 1 minute
The Main Cycle code will stay the same, but it would be a good practice to add some comments.
B4X:
...
myTimer.Initialize("myTimer", 1000 / 60) 'Timer Speed = 60 Hz, Timer Interval = 16
...
Sub myTimer_Tick
Move_Player 'Speed: 60 Hz, Delay = 16 ms - Called every cycle
Move_Enemy 'Speed: 60 Hz, Delay = 16 ms - Called every cycle
Collision_Detection 'Speed: 60 Hz, Delay = 16 ms - Called every cycle
Animate 'Speed: 24 Hz, Delay = 41 ms - Called 24 times every second
Display_Current_Frame 'Speed: 60 Hz, Delay = 16 ms - Called every cycle
Upload_Score_To_Online_Database 'Speed: 0.1 Hz, Delay = 10000 ms - Called every 10 seconds
Retrive_Current_Highscores_From_Online_Database 'Speed: 0.0166 Hz, Delay = 60000 ms - Called one time per minute
End Sub
Conclusion:
I hope you enjoyed reading this tutorial and that it proves useful for you in the futures.
Please don't hesitate in correcting me if I got anything wrong.
Thank for reading.
@Erel, if you see fit, please move this thread into the Teaching section.Please don't hesitate in correcting me if I got anything wrong.
Thank for reading.
Last edited: