Ive been struggeling for the longest time now with how to correctly slow down time in my game.
Ive tried to keep it simple and get it to work correctly and precisely in a simple scenario 'a falling rock affected by gravity'
Ive been following an example wich states that this is how you do it:
Set SpeedY_ to: SpeedY_ + GravityStrength_ * TimeRatio_
Set Ypossition to: Ypossition + SpeedY_ * TrimeRatio_
However, doing the math on this produces inaccurate results.
Ive also tried few other methods and solutions, but i cant get it to work and i cant seem to wrap my head around it.
What i want to do is simple enough:
I want a falling Rock affected by gravity, to move the same distance accross 2 frames at 100% speed, as it does across 4 frames at 50% speed.
I know this is not an easy question.
Could someone pleace help me slow down time?
Put the parts of your game that you want slowed down into one big group. We'll call it "SlowGroup". We'll also have some sort of timer for slowing it down. We'll call this counter "SlowTimer". Then do something like the following outside of that group somewhere.
> Start of level: set SlowTimer = 0.0
> Always: Disable group "SlowGroup", add 0.5 to SlowTimer
> if SlowTimer >= 1.0: subtract 1.0 from SlowTimer, Activate group "SlowGroup"
It'll take some extra work to slow down animations as well, but this will slow down any physics or other game logic going on inside "SlowGroup".
Adjust the amount that is added to SlowTimer in that 2nd event to adjust how much time is slowed down. Adding 0.5 slows time down in "SlowGroup" to 50% normal time. Adding 0.25 slows time down in "SlowGroup" to 25% normal time, and so on.
But that method doesnt really slwo anything down, at 50% speed ( above method ) Objects move 'every other frame' raher than 'half speed each frame', wich might look 'laggy' and not as nice.
If i wanted to do something like this i can imaging it would be easier to just alter the FPS durring runtime ( wich is possible in MMF2 ).
You need to keep all positions stored in variables, so you don't get integer arithmetic introducing rounding errors. MMF will only store integers for the pixel positions of all objects, but for your purposes, you want to store all positions as floats (or even better doubles, but I'll assume you don't have the developer version).
Store XPosition and YPosition as alterable variables. Make all updates to these variables instead, and then have an "Always" event that sets the actual X and Y of the object to these variables.
Some example pseudo-code:
Set SpeedY_ to: SpeedY_ + (GravityStrength_ * TimeRatio_)
UPDATE POSITION VARIABLE
Set YPos_ to: YPos_ + (SpeedY_ * TimeRatio_)
Remember to test it in practice. I don't mean testing the arithmetic, I mean make a little man that walks around and slows down / speeds up as you change the time ratio and watch how his motion looks to your eye.
Basically, although *eventually* the gravity seems to fall out of kilter when you process it in Excel, it's almost unnoticeable in a simple platform game, because each time you land, your vertical inertia is reset to zero. So the player's on-screen velocity never gets to be high enough to see any problems. Otherwise, you may end up chasing decimal points.
In essence, it happens like this, because I can't upload a spreadsheet from here:
At gravity of 1px per frame, after 11 frames, the player's Y position is about 3px out. Bearing in mind that I usually use gravity at much less than 1px, it gives quite a lot of room for error.
You could apply inertia once every N frames, but I suspect that wouldn't fix the problem. If you see in the calcs above, the problem isn't the inertia in itself, because the inertias line up quite nicely. The percentage problem arises when the inertia is added into the position.
Let's look at the 2nd frame, where at 100% inertia is -4 and position 996px.
The maths behind that was Inertia = Inertia + 1 * 1.0; Position = Position + Inertia * 1.0.
But the corresponding frame at 50% has inertia -4 but position 995.75px.
The maths is as follows, using resolved numbers for clarity:
Inertia = -5 + 1 * 0.5 = -5 + 0.5 = -4.5;
Position = 1000 + -4.5 * 0.5 = 1000 + -2.25 = 997.75px;
Inertia = -4.5 + 1 * 0.5 = -4.5 + 0.5 = -4.0;
Position = 997.75 + -4 * 0.5 = 997.75 + -2 = 995.75px;
Wrong-diddly-wrong, because we apply two lots of 50% instead of one lot of 100%.
Dines, you confuse me with the name Intertia. Im guessing that Intertia is what i call MoveX_ and MoveY_ in my Applications ( current movement speed in pixels ).
I have tried theese methods in practice in my applications wich have lead to the same problems as i find when doing the math on paper.
Look at this example, TimeRatio is at 1 ( 100% game speed )
Think about it, if TimeRatio is at 0.25 ( 50& game speed )
Gravity: 0 + (1 * 0.25) = 0.25 ( Gravity is only increased the first frame of every 4 Frames )
Position: 0 + 0.25 = 0.25
Position: 0.25 + 0.25 = 0.5
Position: 0.5 + 0.25 = 0.75
Position: 0.75 + 0.25 = 1
Gravity: 0.25 + (1 * 0.25) = 0.5 ( Gravity is only increased the first frame of every 4 Frames )
Position: 1 + 0.5 = 1.5
Position: 1.5 + 0.5 = 2
Position: 2 + 0.5 = 2.5
Position: 2.5 + 0.5 = 3
I have actually created the solution i just posted above, the only problem im having at the moment is that TimeRation can only be 0.1 or 0.2 or 0.5 But im working on it.
Also using this method you wont have a 'smooth' accelleration from Frame1 to Frame4, Frame5 to Frame8 and so on, but you rather get a constant accelleration.
When i started with this i realy hoped for a simple solution for slowing down time using TimeRatio.
I still hope there is a simple solution to all this, but it has proven to be quite the challenge.
For your tests in applications, make sure you're testing it on the actual motion of an object, not just looking at the values in counters. If you make a Mario clone with bullet time, does Mario noticeably fall along a different curve during bullet time than he does at normal time?
For instance, my Tortoise example has this mathematical inconsistency in it, but no one would notice unless they audited the maths by hand like you have. To the naked eye, there's no real difference.
I understnd what your saying Dines, that if it works in your particular game and the inaccuracy of the method is minimal and hard to notice, then its OK and the inconsistency dont matter much.
But it just happens to be that i am not creating any particular game as of yet, i am creating a 'Game Engine'.
What this means is that out of this Game Engine i will be able to creat many different games wich again might have very different mechanics. For example one game might have the Player moving at lighning speed acoross a huge game area.
Because of this i am very reluctant to use any inacurate method, since the inacuracy will then be present in all game created of the engine.
Speeds are applied by the length of 1 frame, rather than the ratio of time.
So at 50% speed, a normally 50fps game will use 100fps to show one second.
So 1 frame represents 0.01 seconds of game time at 50% speed.
So speeds are recorded in pixels per second (acceleration like gravity at pixels per second per second) and multiplied by the number of seconds that each frame represents.
So a speed of 1px/s at 50% time travels a distance of 1px * 0.01 seconds per frame = 0.01 px per frame
That seems to be fairly accurate - it's sometimes 0.1px out, but it seems to auto-correct over time.
EDIT: No it doesn't But now I really do know what the solution is, I just need to set it out programatically.
Originally Posted by CakeSpear I am storing my possitions as decimals in Alterable Values, and i do own Developer.
How do i choose beytween Floats and Doubles in MMF2???
That pseudo code is however inaccurate, do the math an see for yourself.
1000 * 1.25 = 1250
is not the same as
1000 * 1.125 = 1125
1125 * 1.125 = 1265,625
Math notice from the teacher:
100*1.25 isn't the same as 100*1.125*1.125
Because 1.125*1.25 = 1.265625
And this isn't a rounding error, this is the mathematical difference between a linear and exponential function.
If you want to multiply by 1.25 in two steps, you need to have the squareroot of 1.25, which is something about: 1.1180 with 4 decimals, this way your trick will work.
Because 100.125 is approximately 100*1.1180*1.11.80 (because of only using 4 decimals instead of the entire series of digits).
It's just like the rent on savings.
If you want to use *1.25 for your movement for the lapse of 1 second, you should use the squareroot for 0.5 seconds instead of simply dividing 1.25 by 2. So if you want to split *1.25 up into 10 little steps, you don't do 1.25/10 = 0,125 (because that certainly only would get smaller), you need to take the tenth root of 1.25. In common computer code it's this: 1.25^(1/10) and if you need 20 steps, you use 1.25^(1/20).
However I am not sure if this is what you want. You probably want something like increasing the number of pixels falling every second because this way you can use a division by 2 for 2 steps or division by 10 for 10 steps. But of course if you use a division by 2 or 10 you should add 1/10 or 1/2 of the pixels to add for falling.
I am hoping I am writing some piece of text which is readable and understandable.
So from your link (the 'previous' page of the article about RK4 integration), the RK4 system is applied by essentially always creating 4 additional frames to sniff out the curve being applied by the current acceleration?
So if we say in pseudocode:
Bob's Y position is 100px
Bob is flying upwards with a velocity of 150px/s/s
Gravity pulls Bob at a rate of 50px/s/s
--- RK4 checks this at the start of the frame,
--- RK4 checks it after 50% of delta time
--- RK4 checks it after 50% of delta time from previous check
--- RK4 checks it at end of frame
--- RK4 does its magic to work out the weighted average acceleration based on the 4 samples
Move the player's position accordingly