(http://www.underwaredesign.com/PlayBasicSig.png)
Visit www.PlayBasic.com (http://www.playbasic.com)
>> Timer Based Movement in PlayBasic PART #1<<
'The Interpolation Approach'
By Kevin Picone
19,May,2007
www.UnderwareDesign.com (http://www.underwaredesign.com) - www.PlayBasic.com (http://www.playbasic.com)
(c) copyright 2007/2012 Kevin Picone, All Rights Reserved.
Document Revision: V006
Download
Timer Base Movement Tutorial & Examples (http://www.playbasic.com/sourcecodes.php?page=0&Order=1&Category=Tutorials&Author=KevinPicone)
Intro
The following tutorial demonstrates one possible solution to approximate 'consistent' movement and spawning rate in a game, regardless of computers physical frame rate.
To clarify - Physical frame rate refers to how fast a computer can process & draw the objects/characters on screen. Back in the 8/16 bit era of computing, this was largely unnecessary as games were designed to run on one standard hardware configuration. Meaning there was virtually no diversity between users home systems. So developers could assume all systems would run have the same CPU and graphics speed. So how it runs on one machine, gives a 99.9% indication of how it will run
across all machines of that type.
Today however we don't have this luxury. Why ? - Because the computers people use today, are built from a virtually endless combination of components I.e. CPU, VIDEO, SOUND RAM and MotherBoard etc etc. This diversity gives each system vastly different performance. While this is great for users power & flexibility, let alone their bank balance. It does mean that program authors can no longer rely upon the "if it works on my machine" it'll work on all machines philosophy.
Traditionally movement and redrawing in 2D games was all interconnected. So each update programmers would move the characters and draw them to the screen at their new positions. This process is commonly referred to a refresh or frame. How fast this occurs, is therefore directly linked to how fast the computer can move and refresh the screen.
For example, lets say we have a program that draws 100 characters all moving left to right across the screen. If our program moves each character 1 pixel (dot) to the right each refresh. Then how fast the characters appear to move is now totally dependent upon how fast the computer can execute the program.
To get a clearer picture, Lets compare how long it would take a characters to move all the way across the screen, on two different computers. Assuming the screen is 800 pixels wide.
* Machine A - (slow system - older than 5 years) Sub 1000 Mhz
Lets say this machine can execute the program at a solid refresh rate
of 30 times per second. This means that each character moves
30 pixels to the left in any one second time period. So to complete
it's journey, it's going to take (800/30) = 26.6 seconds from start
to end.
* Machine B - (fast system - less than 1 year old) faster than 3000 Mhz
Lets say this machine can execute the program at a solid refresh rate
of 1000 times per second. This means that each character moves 1000
pixels to left in one second. So to complete it's journey it's going
to take (800/1000) = 0.8 seconds from start to end
Clearly there's a big discrepancy here. People running the program on system A would see the character slowly moving across the screen, while people running it on system B, would see the character flash across the screen in less than a second.
You might have noticed this yourself playing old DOS games on a new PC's, Or perhaps while playing retro games under emulation. It's the exact same issue.
How Do We Solve This ?
There's a few approaches, the simplest is to cap the performance of your application. This will stop it from running too fast on more modern computers. To do this, we simply check how long it took our program to update the game characters and draw the screen. If the computer is faster than we need it to be, then it'll be able to refresh everything in less time than we expect! When this occurs, we force the computer to wait the excess time. Thus slowing our game down to our desired rate. This has the added benefit of giving back any extra processing time back to any other applications that might be running in the background.
If that sounds too complicated, then you're in luck. In PlayBASIC you can just use the SETFPS feature, it does the exact the same thing.
I.e.
[pbcode]
; Tell PB to limit this program to a 30 frames per second or less
SetFps 30
; declare the XPOS variable
Dim Xpos as integer
; Start of the main loop
Do
; Clear The Screen
Cls rgb(0,0,0)
; Move our example object from left to right
Xpos=Xpos+1
Circle Xpos,300,30,true
; Display how Fast it's running
Print Fps()
; Refresh the display
Sync
; loop back to the do-statement to keep the program running
loop
[/pbcode]
While this will work, it only addresses stopping the program from running too fast. Which is only halve of the problem. What happens if we run our program on a machine that is unable to maintain the required refresh rate ? The program slows down. The slower the computer, the slower the program. For games this can be unacceptable.
You can either choose to ignore those users (ie. why support obsolete technology?) , or look into some methods that approximate movement based on some type system constant. Like the system TIMER()
The Raw Concept
The previous program above follows a linear path. Where it handles the movement / drawing each complete refresh cycle. That is to say, the program steps the character(s), draws the character(s) and repeats that process over and over. So the user sees every step of the characters movement. The problem with this, as we've just discovered, is that this approach ties our programs performance to the performance of the computer it's been executed on. Which is not always good.
One alternative (and the method used in the included example) is that we can scale our movements according to the amount of time that has elapsed since the object was created.
About Timer()
When we talk about game timing in PlayBASIC, we're not simply referring to getting the current time and date. Those just aren't accurate enough for our needs. This is because our game will be refreshing anywhere between say 30 to 100 frames per second. So we need something that can measure time at least at that speed or faster preferably. Introducing Timer()
The Timer() function returns the current time in what's called milliseconds. In case you're not familiar, there's 1000 milliseconds in a second, Or another way of thinking of it, is that each millisecond represents a 1000th of a second. Either way, it's very fast!
Since the Timer() returns values that represent 1000th of second, it's not bound to the speed to the computer. So 10, 50, 100, 500 milliseconds take the same amount of time regardless of how the fast the computer we're running on. So it's perfect for our game timing needs.
Basic Implementation
To do this, we need to keep track of the creation time of each spawned character in our game. Then each refresh, we subtract the current time (in milliseconds) from it's creation time (in milliseconds). This gives us the total number of milliseconds that object has been alive for.
E.g
TimePast#= Timer()- ThisCharactersCreationTime
Now depending upon your personal preference here, and personally I like keep my objects speeds in pixels per frame, rather than pixels per millisecond. This means that once I've established the TIMEPAST, I then divide this by the Number of milliseconds that each frame should take. This gives me the number of Frames past since the object was created, or perhaps changed direction.
E.g
FamesPast#=TimePast#/MillisecondsPerFrame#
So calculating the objects position at any given time is just a matter of multiplying the characters movement speed by the FramesPast# value. This gives us how far the character has moved since it was created. So assuming the object is just moving along the X axis, that calculation might look like this.
E.g
DistanceMovedAlongXaxis#=CharacterSpeed#*FramesPast#
All that remains now is to calculate it's Current position. Which is just a matter of adding the Distance the character has moved since it was created, to it's starting position.
E.g
CurrentXposition# = CharactersStartingXposition# + DistanceMovedAlongXaxis#
And there we have it, the great mystery of the Timer Based Movement is solved. or is it ?
Calculating Milliseconds Per Frame You'll have notice in the above there's one missing ingredient from our calculations, whats this mysterious MillisecondsPerFrame# value ?
MillisecondsPerFrame# is a the number of the milliseconds one complete refresh should take, when our game is running at our 'perfect' frame rate. To calculate it, we divide the total number of milliseconds in one second (1000) by whatever our ideal frame rate should be. In the included example is 50 frames per second.
E.G
GamesIdealFramesPerSecond# = 50
MillisecondsPerFrame# = 1000.0 / GamesIdealFramesPerSecond#
This gives us the number of milliseconds one complete frame should take. This can then use it to calculate the how many frames have past, or for example it can be used to calculate and control when new characters should be spawned.
Spawning New Characters ?
One of the most commonly overlooked topics when talking about timer based movement, is not only keeping our characters moving at a nice relative rate, but making sure we spawning new characters evenly also.
To get an idea of the problem, lets revisit the previous example. Lets imagine were want to move our characters on screen from left to right again. Each time we update the refresh the game, we add a new random character to the left hand side of the screen. So we theoretically have a constant stream of characters being spawned and moving across the screen.
The the main loop (in pseudo code) might look something like this.
; Start of main loop
do
; clear the screen
Cls
; draws/moves our list of characters across the screen
DrawCharacters
; Randomly Spawn a new character every loop
SpawnNewCharacter
; refresh the screen so the user can see the changes
Sync
; loop back to the DO statement to keep the program running
loop
Now assuming the characters movements are based on the computers timer, the characters should each appear to move at relatively the same speed regardless of the computer the loop is running on. But what about SpawnNewCharacter ?, should this be executed every time the main loop is processed ?
No, it shouldn't. Why ?, because the speed the of computer dictates how frequently the Main loop is being executed here. The slower the machine, the less frequently this loop will be executed. The faster the machine, the more frequently the loop is executed. In other words slower machines spawn less new characters than faster ones. Therefore Making the your games behave differently across different speed systems. Which is Not ideal!
How do we get over come this? Just as we did previously with movement, we just have make sure our character spawning mechanisms work independently of the speed of the host computer. So rather than adding a new character each time the computer processes the main loop. We only add a new character after a certain number of milliseconds has transpired.
So if we wished to spawn new characters at our games ideal frame rate, we need monitor the spawn time relative to our ideal frame rate. The method I normally use is to simply hold the Time of when the next frame is expected to start. Then each time the main loop executes, we're comparing the current system timer() with our expected time.
[pbcode]
; Our ideal frame rate of our game
IdealFramesPerSecondRate=50
; the number of milliseconds one complete frame will take
MillisecondsPerFrame= 1000.0/IdealFramesPerSecondRate
; Init the Expected time in milliseconds of when the current frame has ended
StartOfNextFrame=Timer()+MillisecondsPerFrame
; Start of main loop
Do
; clear the screen
Cls 0
; draws/moves our list of characters across the screen
DrawCharacters
; Only Spawn a new character if enough time has passed
if Timer()=>StartOfNextFrame
; randomly spawn a new character
SpawnNewCharacter
; Adjust our time variable forward to the time that the
; next frame will start.
StartOfNextFrame=Timer()+MillisecondsPerFrame
endif
; refresh the screen so the user can see the changes
Sync
; loop back to the DO statement to keep the program running
loop
[/pbcode]
This will work, but there's a problem. It's only limiting the number of character spawns on computers that can process the main loop faster than our ideal frame rate. But on slower machines we'll still miss character spawns. Which the will change the behavior the game on those systems.
The solution is simple, rather than just assuming only one frame has passed and spawning one character, we'll actually calculate the number of frames (to meet our ideal frame rate) that have past since the last refresh. On fast machines this will generally only be one frame, but on slower machines, or when the game is being bogged down by rendering too much graphics perhaps. Two, three or more frames might have passed. So in order to cope with this, we need back track our character spawning and generate any characters that would have been otherwise missed.
What this effectively does is ensures that we get a fairly consistent spawning behavior across the broad spectrum of possible computer speeds. It's not necessarily perfect, but it's highly unlikely the player will notice any inaccuracies. So it's more than good enough to make the illusion appear seamless !
Return To PlayBasic Tutorial Index (http://www.underwaredesign.com/forums/index.php?topic=2552.0)
(http://www.underwaredesign.com/PlayBasicSig.png)
Visit www.PlayBasic.com (http://www.playbasic.com/)
>> Timer Based Movement in PlayBasic PART #2<<
'The Interpolation Approach'
By Kevin Picone
19,May,2007
www.UnderwareDesign.com (http://www.underwaredesign.com/) - www.PlayBasic.com (http://www.playbasic.com/)
(c) copyright 2007/2008 Kevin Picone, All Rights Reserved.
Document Revision: V005
Potential Problem Areas
Yes, believe it or not, there are some issues that need to be resolved in order to get a timer based system working perfectly. Some of which are character Collisions, Animations and None Linear Movements.
Collision Collision detection can create issues in timer based systems when detection relies purely upon two regions overlapping. The problem occurs when our program is running on machines that's are unable to keep up with the games ideal frame rate. This is a problem because on those machines, characters are being moved greater distances each refresh, to compensate for the slower machine. So some frames have been skipped. In those situations, it's not uncommon for two reasonable fast moving objects to 'skip' over each other, even when on a collision course.
Solution Ideas:
1) A quick way to detect if they might have hit, is to create bounding
boxes around the objects paths (padded to include the objects size). Then
we can just compare the regions together. If the regions overlap, then
it's possible they intersected at some point during the last update. If not,
there's no need to compare them any further. When they do overlap though,
we'd still need some secondary method to check if they actually hit each
other while traveling. This just gives us a way to reject trivial/impossible
cases quickly.
2) Solution would be keep track of the paths the objects are move from last
update to this update. We'd then compare the two paths (rays) to find the
close point between the two paths. Ie The first point where they could have
potentially hit (when the distance between the paths is less than the total
combined radius of both objects). This doesn't necessarily mean the objects
are touching, just that a collision is possible.
3) Another solution cheap & easy is to use a binary chop approach. That is we
examine how far the two objects have moved between this update and the last
update. We then chop this distance in half and check for a collision at this
point. If no collision, we check at the end point. This could be taken
further by recursively cutting up the paths the two characters are moving along
and comparing them in smaller steps.
4) A Variation of #3 is that rather just subdividing the paths, we get the speeds
of the two characters. Then take the speed of the slowest moving character,
and use that to step along the two paths. Comparing the object at each step.
5) Ray intersection. This is similar to solution #1+2 combined. After
comparing the two paths via bounding boxes (method #1) we then make two
rectangle/polygon shapes from the paths. The shape represent the paths of
both object (including the objects width) So where making two fat polygon lines.
Once we have these we can check if these regions overlap using PlayBasic
shape intersections. If they do, we could then run ray/line to shape
intersections to try an find the closest point along the movement paths where
they intersect.
There are lots of combination approaches, but hopefully these will give you some
ideas on how to cope with collisions better.
Animation This not so such a huge problem, just something that we need to bring into our considerations when making games run off the system timer(). Commonly 2D games use frame based animations. By that I mean, the each time the game refreshes the world, it steps animated characters forward through a series of user defined animation frames. This not only gives the illusion to the player that the character is performing some action, it make animations bound to the speed of the computer. Which is what were trying to avoid here.
Thus Animation needs to be tackled much the same as movement, where the current animation frame will be calculated from the time this redraw refresh is taking place. For cyclical animation this is pretty straight forward. All we need is the time when the object was spawned, the number of frames in the animation and the animations ideal frame rate. So here we can calc what frame we should be displaying at this refresh.
In more complex animation systems, (where animations are chainable perhaps) you'll have to keep track of what animation your currently in. Which just means keeping track of when this animation starts and is expected to end. If the current refresh time is greater than the end time, we move to the next animation sequence. We do this recursively, since It's also possible that the next animation may well have already ended. So we'd need to skip those also. Once we've found the current anim set, we just locate the current frame as per above.
NOTE: For the best collision results, Movement & Animation need to be considered!
None Linear Movement
This tutorial and example that comes with it, basically assumes characters are moving in straight lines at fixed speeds. While this suits most traditional 2D games, things can get a little more complicated if you want/need a more elaborate movement system.
There's three basic approaches that come to mind.
1) You can use formulas that lets you plot movements against time
(Projectile Motion)
2) You manually step the movement forward according to the number of
frames that past since last update.
3) Combination of both.
Done
Hopefully this will give you some insight on how to go about implementing timer control mechanisms into your games & demos. I wish you the best and hope to see your creations one day !
Download
Timer Base Movement Tutorial & Examples (http://www.playbasic.com/sourcecodes.php?page=0&Order=1&Category=Tutorials&Author=KevinPicone)
Related Delta Time Examples:
Timer Based Movement - Frame Rate Independent Motion Example (https://www.underwaredesign.com/forums/index.php?topic=4800.0)
Return To PlayBasic Tutorial Index (http://www.underwaredesign.com/forums/index.php?topic=2552.0)
#deletatime #timer #timing #fps #calculate