1
\$\begingroup\$

Edit

Just for clarification, my sprite's 'movement' isn't the problem. If I set my Time variable to 4 seconds, then it will cross the screen in exactly 4 seconds regardless of logic updates rate per second, rendering rate per second or screen resolution. So I am pretty sure I'm scaling the sprite's movement correctly. What I'm pretty sure I'm not doing correctly, is scaling acceleration.

Original Question

I'm trying to implement gravity in my 2d platformer and am having a few problems understanding how to keep it consistent when I change my updates per second.

Here's what I have.

My Gameloop overview

Currently, my gameloop renders at the maximum rate allowed by device and the updates are 'clamped' to an upper limit (At the moment, 60 per second). I am working on the assumption that most of the time, my game will have no problem hitting this, even if the actual rendering dips. Thus I am doing all of my calculations based Delta Time derived from this fixed 'ticksPerSecond' value.

I don't know for sure that this will remain at 60, I may decide at some point during the development to lower this upper limit.

My Gravity - variable declaration and initial values

At the moment, I have:

float spriteYTime=7f;                //This is the (initial) amount in seconds that this sprite will take to move from the top of the screen to the bottom.
float fallAccel=.5;                  //This is the value that will subtracted from he sprite's fall speed (To make it fall ever faster)
float terminalVelocity=1.5f;         //Cap speed at this rate (1.5 seconds)

my sprite's position is worked out on dt as follows:

//Delta time
float ticksPerSecond = 60;
float dt = 1f/ticksPerSecond;
//Velocity
spriteYVel = 1/spriteYTime;
//Update position
spriteYReal = spriteYReal + (spriteYVel * dt);
//Convert to screen coordinates (will be drawn at this Y coordinate)
spriteyScreen=(int) (furmanYReal*height);

My Gravity - code

    //If my sprite's state is 'f' (meaning falling) then apply gravity
    if(sprite.getState('f')==true){
    //Calculate new position
    spriteYReal = spriteYReal + (spriteYVel * dt);
    //Convert to screen coordinates
    sprite.yScreen=(int) (spriteYReal*r.height);
    //Reduce time by fallAccel amount & update velocity value based on new time value (So sprite falls slightly faster this frame compared to the last
    spriteYTime-=fallAccel;
    spriteYVel = 1/spriteYTime;
    //Check that speed isn't faster than terminal velocity
         if (spriteYTime<spriteVelocity)
            spriteYTime=spriteVelocity;
    }

The problem

Now, this does work, but if I change my ticksPerSecond value, it goes wrong (it falls at different rates).

I know that 'Earth Normal' gravity is approximately 9.8 meters per second per second, but this is measured 'per second' whereas I (think) I need to work with 'per frame'. This is where I'm getting confused.

So, take this example:

If set my initial Time value to 4 seconds, then if it remained constant at this speed, it would take 4 seconds to reach the bottom, if I changed my ticksPerSecond, because the time value is worked out using delta, it would still take 4 seconds but if I apply 'fallAccel" to the Time value (i.e. subract it), it goes wrong when I change the ticksPerSecond value - why?

How can I get this to fall at the same rate regardless of the value of 'ticksPerSecond' - any help in understanding this would be appreciated.

\$\endgroup\$
3
  • \$\begingroup\$ Why do you an upper speed limit? I suggest just using delta time between two frames to calculate movement. \$\endgroup\$
    – olevegard
    May 16, 2013 at 13:29
  • \$\begingroup\$ Hi @olevegard, upper speed limit is simply terminal velocity to simulate air-fricton on my character - there has to come a point when it stops accelerating. I am already using Delta between frames (dt = 1/ticksPerSecond) to calculate movement. \$\endgroup\$ May 16, 2013 at 13:32
  • \$\begingroup\$ Downvote with no comment as to why? \$\endgroup\$ May 17, 2013 at 22:56

3 Answers 3

3
\$\begingroup\$

Part of the problem is that your notion of 'velocity' isn't physical. Your updating of position is fine:

spriteYReal = spriteYReal + (spriteYVel * dt);
sprite.yScreen=(int) (spriteYReal*r.height);

This just says that the sprite's position is computed as Pnew = Pold+V*dt, which is fine - it means that V=dP/dt, which is correct. The problem is that the velocity update is incorrect, on a couple of different fronts:

spriteYTime-=fallAccel;
spriteYVel = 1/spriteYTime;

First of all, notice that unlike the position update, this one isn't corrected at all for different tick values, so changing your dt (i.e., your ticksPerSecond value) will change how quickly acceleration changes the velocity. But even correcting for that (for instance, doing something like spriteYTime-=fallAccel*dt) isn't really the right thing to do; that would be equivalent to saying V=1/(T0-A*t) for some value of T0, which is physically incorrect. Instead, you want to do something roughly equivalent to what you've done for position:

spriteYVel = spriteYVel-(fallAccel*dt);

This will compute Vnew as Vold-A*dt, which is what you want.

There's a slightly deeper problem here, though, one that's likely to get you in other ways: right now, this code closely couples the representation of its objects — the sprite — with their display; that is, the screen coordinates and especially the notion of spriteYTime, the 'time from top to bottom', as an inherent quantity of the object itself. I would suggest trying to separate the two ideas as much as possible; if you know approximately how long you want it to take for a sprite to travel from the top to the bottom of the screen then you can use the general formula for gravitational acceleration — X=X0+V0t+(1/2)At2 — to determine what you want its initial velocity to be in order to reach the bottom of the screen in that amount of time. (Note that this doesn't take into account terminal velocity, which makes things much more complicated, but I presume you don't need to know precisely how long it'll take to reach the bottom of the screen in any case).

\$\endgroup\$
4
  • \$\begingroup\$ Hey @StevenStadnicki, thanks for this, I'm still reading through it. I'm not super great on physics - in your equations what does X, V, T & A stand for? Thanks!! \$\endgroup\$ May 16, 2013 at 21:58
  • \$\begingroup\$ @user22241 'X' (and 'P' - I probably shouldn't have changed names!) just represents a position (in your case, actually the sprite's y position!); 'V' represents a velocity; 't' represents a time (and 'dt' the difference in times between frames, of course), and 'A' is the acceleration. \$\endgroup\$ May 16, 2013 at 22:00
  • \$\begingroup\$ Thanks @StevenStadnicki - I've tried to implement your ideas but I'm having a real time of it. If I replace my velocity update line with spriteYVel = spriteYVel+(fallAccel*dt); (I'm assuming you mean a + there, not a -?!), then my character does fall, but I can't control (using the Time variable) how fast. Is this correct? I can even remove my Time variable line (spriteYTime-=fallAccel;) and it makes no difference to the sprite falling. Could you please edit your answer to show how I can incorporate your equations into my code? It would be a great help! - Thanks again! \$\endgroup\$ May 16, 2013 at 22:23
  • \$\begingroup\$ Are position and velocity supposed to be completely separate? Surely they are related? Take this line: spriteYReal = spriteYReal + (spriteYVel * dt); isn't this where the sprite actually gets moved? This is scaled as it uses the delta and the velocity value, even though the velocity value isn't itself scaled - is this not correct then? Just having a really hard time getting my head around it all - any further help you can offer would be appreciated. Thanks \$\endgroup\$ May 16, 2013 at 22:40
1
\$\begingroup\$

Thanks to the contributors who answered this.

I went with my own solution in the end because it was simply enough to implement and gave perfect results.

All I had to do to scale the acceleration, was the following:

float fallAccel=(dt * num);  //Where num is any arbitary amount and dt is the delta time between frames (hence it is scaled and also adjustable)

Once I apply this, my sprite will fall at the same rate regardless of how many game updates per second I run (ticksPerSecond).

I hope this may help others if they have similar problems in the future :-)

\$\endgroup\$
0
\$\begingroup\$

Physics update rate must be independent from rendering frame rate. Becase it's not exactly 60 fps.

void update( float dt )
{
    float maxStep = 1/60.0f;
    if (dt > 0.25)
        dt = 0.25f; // note: max frame time to avoid spiral of death

    accumulator_ += dt;
    while (accumulator_ >= maxStep) {
        physical_world_->Step(maxStep);
        accumulator_ -= maxStep;
    }
}

So you have to take care about constant physics step. Sometimes for example it can be called 2 times per rendering frame.

You wrote:

spriteYReal = spriteYReal + (spriteYVel * dt);
//Convert to screen coordinates
sprite.yScreen=(int) (spriteYReal*r.height);
//Reduce time by fallAccel amount & update velocity value based on new time value (So sprite falls slightly faster this frame compared to the last
spriteYTime-=fallAccel;

I think you problem is you subtract fallAccel from time.

spriteYReal = spriteYReal + ((spriteYVel - fallAccel*dt) * dt);

Write something like this.

\$\endgroup\$
1
  • \$\begingroup\$ Hi @bobenko, not sure I understand, my physics update is independent, my physics is being called 60 times per second. Delta therefore is 1/60 = 0.0166666666666667. It is not variable. I have no problem with sprite movement - my sprite's speed remains constant regardless of the number of physics update per second or rendering rate per second (or indeed screen resolution) - that part of it is fine. But my problem comes when I add acceleration. This is what my question pertains to. \$\endgroup\$ May 16, 2013 at 13:59

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .