Sorry your browser is not supported!

You are using an outdated browser that does not support modern web technologies, in order to use this site please update to a new browser.

Browsers supported include Chrome, FireFox, Safari, Opera, Internet Explorer 10+ or Microsoft Edge.

DarkBASIC Professional Discussion / How do I make sure that everything in my game moves at the speed when run on different processors

Author
Message
Juggernaut
14
Years of Service
User Offline
Joined: 12th Mar 2012
Location:
Posted: 24th Jan 2013 23:23
Hello,

How do I make sure that everything in my game moves at the speed when run on different processors - Pentium III, Single Core Celeron and P4,
Dual Core Celeron and P4 and lastly the i3, i5 and i7 processors.

To clarify - I want camera movement, static 3D object movement/ rotation and animated character movement to occur at the same speed irrespective of the power of the processor the game is running on.
How can I achieve this ?

Thanks,
zeroSlave
17
Years of Service
User Offline
Joined: 13th Jun 2009
Location: Springfield
Posted: 24th Jan 2013 23:48 Edited at: 25th Jan 2013 18:02
You're going to want to utilize Timer based movements. Here is how I typically do it:



Once you have deltaTime#, you will then multiply any movement or angle change by this value to get the interpolated movement.

Everybody is a genius. But if you judge a fish by its ability to climb a tree, it will live its whole life believing that it is stupid.
Juggernaut
14
Years of Service
User Offline
Joined: 12th Mar 2012
Location:
Posted: 25th Jan 2013 00:13
As far as the Dark Basic Pro documentation goes, the timer() command
will get the internal system time, which continually increments at a thousand times a second. The system time is returned in milliseconds, where 1000 units represent 1 second.

In the statement deltaTime# = (worldTime - oldTime) / 60.0

The numerator (worldTime - oldTime) gives us the amount of milli-seconds that has gone past since the whole piece of code started executing in milliseconds. I wonder -

1. Why you divide the elapsed time ( in milliseconds ) with the float value of 60.0 ? What is the concept behind it ?

2. What happens when deltaTime# is multiplied with the angle value of the command "turn object left" ?

3. Do I have to apply the deltaTime# to each and every functions of Dark Basic Pro including the plugin commands found in EZRotate ?

4. Is this a full proof method ? Has this method worked on diverse processor power without any glitch ?
Dar13
18
Years of Service
User Offline
Joined: 12th May 2008
Location: Microsoft VisualStudio 2010 Professional
Posted: 25th Jan 2013 01:20 Edited at: 25th Jan 2013 14:03
1. zeroSlave actually used the wrong formula for Timer-based movement. A good discussion about timer-based movement can be found here.

2. To use timer-based movement, you take the factor variable given to you by your chosen routine or formula and multiply all movement/rotation/etc by that factor.

3. You only have to apply that factor when you're using a function or command that will move/rotate an object/camera/light/etc. EZRotate is mainly a math library IIRC, so you don't have to use the factor then(though it might have functions where it will rotate the camera for you, then you would use the factor when you use that function).

4. It's fairly foolproof, though can be issues when the screen frame rate is either extremely low or extremely high.

The larger the factor is (the lower the screen FPS is), the less smooth that movement/rotation becomes. That's simply due to the fact that if you increment by too large an interval, that interval becomes more noticable. Another side effect of trying to use timer-based movement on a really slow processor is that it will appear to move/rotate slower than it should be. That is probably because the time is so long between updates of the timer-based movement factor that the factor is used longer than it optimally should be. Unfortunately, there's not much you can do to fix that issue other than to optimize the rest of the game to run faster.

On the other end of the spectrum, there can be issues when screen fps is above 200 on newer processors/GPUs where the factor can be so miniscule that movement using the factor can seem to be faster than it should be. This can be easily fixed by restricting the frame rate to be no higher than your target frame rate or just above it (target frame rate would be ~60, maximum would be ~80).

Note: I'm just reciting what I remember from memory. If I made a mistake somewhere, please correct me.

EDIT: I some words.

zeroSlave
17
Years of Service
User Offline
Joined: 13th Jun 2009
Location: Springfield
Posted: 25th Jan 2013 01:26 Edited at: 25th Jan 2013 18:03
(Dar13 beat me to my mistake! and the answers! AND I updated to the correct math. Thanks, Mage.)

1. The value is essentially "milliseconds per frame." I edited the post above to use a realistic value. Sorry I missed that the first time. :/
deltaTime# = (worldTime - oldTime) / (1000 / 60.0)
1000 milliseconds in a second, divided by 60.0 frames per second.

I'm basically stating that I want movements to be the same as they would be in a set 60 fps program. If I wanted it to emulate a 30 or 120 fps system:
deltaTime# = (worldTime - oldTime) / (1000 / 30.0)
deltaTime# = (worldTime - oldTime) / (1000 / 120.0)

I'm also using a float to enforce that the return value will be a float and not an integer.

This explains it in more detail:
http://www.underwaredesign.com/forums/index.php?topic=1909.0

2. TURN OBJECT LEFT 1, 12 * deltaTime# would turn the object left (12 * deltaTime#) degrees...

3. Any values that you want a time constraint on would need to multiplied by deltaTime#. Object movement and rotations being the majority.

4. I don't really think it's foolproof. You'd have to take in the consideration that person may never turn their computer off, so the value return from timer() would be too large to be usable. I haven't encountered this, but I would assume it's a possibility.

Sorry if any of this is confusing. I can code and find solutions better than I can explain them. Hopefully it helps a bit, though!

Everybody is a genius. But if you judge a fish by its ability to climb a tree, it will live its whole life believing that it is stupid.
Mage
Valued Member
19
Years of Service
User Offline
Joined: 3rd Feb 2007
Location:
Posted: 25th Jan 2013 03:59 Edited at: 25th Jan 2013 04:26
Quote: "1. The value is essentially "milliseconds per frame." I edited the post above to use a realistic value. Sorry I missed that the first time. :/
deltaTime# = (worldTime - oldTime) * (1000 / 60.0)"

This is not correct.
Should Be: deltaTime# = ABS(worldTime - oldTime) / (1000 / 60.0)
Simplfied To: deltaTime# = ABS(worldTime - oldTime) / (16.6666)

Your code doesn't work and will completely fail when the timer reaches too high. The timer becomes negative and starts counting backwards. It also doesn't relate everything to 60fps as was intended as the math is wrong. Technically, you can remove all of the division and multiplication and just use the ABS() difference.


Quote: "1. zeroSlave actually used the wrong formula for Timer-based movement. A good discussion about timer-based movement can be found here."

This is a good and simple discussion. Excellent Example.


Quote: "On the other spectrum, there can be issues when screen fps is above 200 on newer processors/GPUs where the factor can be so miniscule that movement using the factor can seem to be faster than it should be. This can be easily fixed by restricting the frame rate to be no higher than your target frame rate or just above it (target frame rate would be ~60, maximum would be ~80)."

This is an excellent point to make. This also relates to lag spikes. If the game suddenly slows or stops briefly, you don't want those movement increments to turn into people teleporting through walls, etc. A frame limiter is good for the high end, a "factor" limiter is also good for the low, set a minimum frame rate.
If ABS(worldTime - oldTime) > 1000 Then deltaTime# = 0.0

This is really important and almost everyone seems to skip this. If you are in a menu, paused, holding down a key-press or whatever, and the timer code isn't used for a long period like seconds or more... When the game-play returns there's going to be a huge super jump everywhere. Building a delta / factor limiter means you don't have to remember to put timer reset code everywhere.



Quote: "
1. Why you divide the elapsed time ( in milliseconds ) with the float value of 60.0 ? What is the concept behind it ?

2. What happens when deltaTime# is multiplied with the angle value of the command "turn object left" ?

3. Do I have to apply the deltaTime# to each and every functions of Dark Basic Pro including the plugin commands found in EZRotate ?

4. Is this a full proof method ? Has this method worked on diverse processor power without any glitch ? "


1. The mentality (there are others) is that you can write the code as units per frame at 60 frames per second. Move 12 * factor? Would move 12 units at the classic PC Game ideal frame rate of 60. If the frame rate is too fast or too slow the 12 is scaled to make on screen movement appear the proper speed. The 60 should actually be 1000/60 and this whole part is not necessary, but it can help visualize movement when reading the code.

2. If on each frame you are turning a certain number of degrees, that rotation is going to slow or speed up with frame rate. So the amount of each rotation is scaled on each frame to cancel out speed changes from the frame rate.

3. Anything that is affected by frame rate changes. Movement and rotation both decrease when frame rate goes down because they happen less often. So individual movements need to go get bigger to compensate, so you cover the same distance in the same amount of time.

However what nobody mentions is that random events are opposite. Random events need to get less likely when frame rate goes up, and more likely when frame rate goes down.

4. This method is enough for pretty much any game. You can get more precise timers with plugins. The vast majority of the time the built in timer is fine.


Quote: " I don't really think it's foolproof. You'd have to take in the consideration that person may never turn their computer off, so the value return from timer() would be too large to be usable. I haven't encountered this, but I would assume it's a possibility."

This is not true, with correct code. The Timer() loops back to 0. There is a glitch with DBP and at a certain point the timer suddenly turns into a negative number counting backwards. But ABS() solves this completely. It is full proof, unless you need timing precision higher than a typical video game.


Here is a fixed example:


The example is unrealistic for anything but the simplest of games and using Functions is more flexible. Better to do this:


Juggernaut
14
Years of Service
User Offline
Joined: 12th Mar 2012
Location:
Posted: 25th Jan 2013 12:44
Thank you everybody for the overwhelmingly detailed explanation on the subject. Thank you ZeroSlave, Mage and Dar13 for participating and showing me the way. I will look into the whole concept more deeply and let you folks know if I stumble on something.
zeroSlave
17
Years of Service
User Offline
Joined: 13th Jun 2009
Location: Springfield
Posted: 25th Jan 2013 18:01
Sorry folks. You are absolutely right, Mage. My math sucks! That's what I get for not testing it in DB. And thanks for the info on the absolute value with the negative time!

deltaTime# = ABS(worldTime - oldTime) / (1000 / 60.0)

Milliseconds per frame
The reason I choose to do (1000 / 60.0) is because it is a more accurate representation of the math (me being pedantic) than 16.666. Also, with the math correct, anything updated with deltaTime would change at the same speed as it would without the update running with a refresh rate of 60. It is correct that you don't need this multiplier, however, it's probably best to set it up there instead of doing each object as:
move object 1, 1 * deltaTime# / (1000 / 60.0)
move object 1, 1 * deltaTime# / 16.666

or
move object 1, 0.06 * deltaTime#
(or whatever your rate of movement is.)


deltaTime# = ABS(worldTime - oldTime) / (1000 / baseTimeRate#)
Is how I typically do it. By being able to change this variable, one can achieve bullet time, subtle game slow downs before a pause, being able to set a game speed (such as SimCity, etc.)

And technically, wouldn't it still not be a foolproof method unless other things were taken into account? Such as the game loosing focus, yet the timer continues to count up. The same as selecting the title bar in a windowed app and moving it around. It pauses the actual display update, but does not pause the timer event. Once the mouse click up event occurs, anything being updated by the TBM system jumps to it's new position according to system time and not game time. There will be more issues when losing display focus such as media being lost and you'd probably want a new TBM update after the media has been recreated, yet that is whole different can of worms.

I updated the above incorrect math so as to not confuse any future readers.

Everybody is a genius. But if you judge a fish by its ability to climb a tree, it will live its whole life believing that it is stupid.
GIDustin
18
Years of Service
User Offline
Joined: 30th May 2008
Location:
Posted: 25th Jan 2013 18:50 Edited at: 25th Jan 2013 18:51
I am a firm believer that the abs() is unnecessary and wastes CPU time. For example:



In that example, OriginalTime is really close to wrapping over. CurrentTime is wrapped over. The math still works out, and the difference is still 10.

However, being a programmer, all you have to do to convince me otherwise is to post a snippet, like mine above, that proves that abs() is necessary.
Le Verdier
14
Years of Service
User Offline
Joined: 10th Jan 2012
Location: In the mosh-pit
Posted: 26th Jan 2013 01:11
Hello
Dont forget an important thing when using Timer(),
If the result is in milliseconds' the resolution is not!!
A test on my old laptop give me something like 15/16 ms, that is not workable at all...
The good practice is to use hifreq timer 'Perftimer()'
(There are already discussions about this, just search the forum...
..or click my banner for an example..)

Dar13
18
Years of Service
User Offline
Joined: 12th May 2008
Location: Microsoft VisualStudio 2010 Professional
Posted: 26th Jan 2013 01:55 Edited at: 26th Jan 2013 01:56
Quote: "This is not true, with correct code. The Timer() loops back to 0. There is a glitch with DBP and at a certain point the timer suddenly turns into a negative number counting backwards. But ABS() solves this completely. It is full proof, unless you need timing precision higher than a typical video game."

Timer() loops back to the negative limit of a 32-bit integer, not 0. Mage, please go back and reread this series of posts about Timer() overflow and the ability to not use ABS when handling time values. The code will have to be adjusted in order to account for this information(I think you can figure it out from my link above), but this way is avoid a function call that would otherwise happen every single frame.

Also, it's not a glitch. It's just the reality of using 32-bit integers to store time. The C++ code behind the Timer() function is a straight-forward timeGetTime() function call.

Mage
Valued Member
19
Years of Service
User Offline
Joined: 3rd Feb 2007
Location:
Posted: 26th Jan 2013 03:50 Edited at: 26th Jan 2013 04:13
Quote: "I am a firm believer that the abs() is unnecessary and wastes CPU time. For example:

+ Code Snippet

In that example, OriginalTime is really close to wrapping over. CurrentTime is wrapped over. The math still works out, and the difference is still 10.

However, being a programmer, all you have to do to convince me otherwise is to post a snippet, like mine above, that proves that abs() is necessary. "


I forgot this behavior was discussed several months ago with Scheduling In-Game Events. When you responded I remembered...
http://forum.thegamecreators.com/?m=forum_view&t=200026&b=1&msg=2393639#m2393639
Timer problem is merely a signed/unsigned variable issue.

That being said, here's how it fails.

If you get sloppy with the data types, the effect can become a much larger problem. Still not a reason to use ABS() in my opinion, but good to know nonetheless.


Quote: "Timer() loops back to the negative limit of a 32-bit integer, not 0. Mage, please go back and reread this series of posts about Timer() overflow and the ability to not use ABS when handling time values. The code will have to be adjusted in order to account for this information(I think you can figure it out from my link above), but this way is avoid a function call that would otherwise happen every single frame.

Also, it's not a glitch. It's just the reality of using 32-bit integers to store time. The C++ code behind the Timer() function is a straight-forward timeGetTime() function call."


Yes, I was going to respond about this earlier today before you replied. I was thinking of the thread you mentioned. I'm fully aware of the issue. Just completely forgot about the unsigned integer, as it was 4am yesterday when I posted previously.

I promise I'll get more sleep. Can't win 'em all.


Edit: What's really going through my head is I forgot the leading bit isn't a merely a sign bit, but instead the binary number is represented in 2's compliment. As result ABS() isn't needed.
Quote: "Me: Your code doesn't work and will completely fail when the timer reaches too high. The timer becomes negative and starts counting backwards."

With 2's compliment the timer rolls over to a large negative number and starts counting up to 0. Since it's not counting backwards the DBP code still works.


Dar13
18
Years of Service
User Offline
Joined: 12th May 2008
Location: Microsoft VisualStudio 2010 Professional
Posted: 26th Jan 2013 04:58
@Mage
No problem, wasn't sure if you had remembered that tidbit or not.

Quote: "I promise I'll get more sleep. Can't win 'em all."

I know the feeling. Sleep-deprivedness is great for zombie programming, but can be quite an issue when forum post writing.

Chris Tate
DBPro Master
17
Years of Service
User Offline
Joined: 29th Aug 2008
Location: London, England
Posted: 26th Jan 2013 05:36 Edited at: 26th Jan 2013 05:37
Why not just clamp the delta# value in a suitable range?

deltaTime# = Clamp( worldTime - oldTime) / (1000 / 60.0), 0.1, 5.0 )

Not that I use delta's in every situation...

What if altering the speed of a turning object's movement causes it to turn in a completely different path of movement?

Why don't you sweep through the process


Just a simple illustration.

Since you are not using a physics system, don't you need sweeping collision detection anyway?

Juggernaut
14
Years of Service
User Offline
Joined: 12th Mar 2012
Location:
Posted: 26th Jan 2013 08:23
May I know if I have to use this timer technique to Dark Physics commands too ?
Mage
Valued Member
19
Years of Service
User Offline
Joined: 3rd Feb 2007
Location:
Posted: 26th Jan 2013 11:54
Quote: "@Mage
No problem, wasn't sure if you had remembered that tidbit or not. "


I laughed when I read your comment and realized I was quoting the same thread to GIDustin in the beginning of my reply before reading your comments where you quote the thread to me.

mr Handy
18
Years of Service
User Offline
Joined: 7th Sep 2007
Location: out of TGC
Posted: 27th Jan 2013 19:15


(c) Fallout

*** Merry Chuckmas! ***
GIDustin
18
Years of Service
User Offline
Joined: 30th May 2008
Location:
Posted: 27th Jan 2013 22:10
@Mr Handy:

That looks like it could work, but maybe stick a nice sleep 1 in your while loop to reduce CPU usage.
mr Handy
18
Years of Service
User Offline
Joined: 7th Sep 2007
Location: out of TGC
Posted: 27th Jan 2013 23:22
what difference between wait 1000 and sleep 1000?

*** Merry Chuckmas! ***
Le Verdier
14
Years of Service
User Offline
Joined: 10th Jan 2012
Location: In the mosh-pit
Posted: 28th Jan 2013 02:57
This is very inacurate, there is a function to get the perftimer frequency(perffreq() but only in dbp 7.7)
otherwise:


Mage
Valued Member
19
Years of Service
User Offline
Joined: 3rd Feb 2007
Location:
Posted: 28th Jan 2013 07:26
Quote: "That looks like it could work, but maybe stick a nice sleep 1 in your while loop to reduce CPU usage"


Marvelous.

Login to post a reply

Server time is: 2026-07-08 03:49:42
Your offset time is: 2026-07-08 03:49:42