I'm using SDL to make a game and I'm using frame independent movement, everything works fine with no gravity or acceleration:

boxOffsetY += yVel * ( DELTA.get_ticks() / 1000.f );

The box moves accordingly, no matter what frame rate i use.

But when i try to add gravity:

yVel += dropSpeed * ( DELTA.get_ticks() / 1000.f );

I add this line first.

boxOffsetY += yVel * ( DELTA.get_ticks() / 1000.f );

And just like before I add this line later.

I've tried various methods but none of them seem to work properly.

Doing this based on Lazy Foo's tutorial: http://lazyfoo.net/SDL_tutorials/lesson32/index.php

Recommended Answers

All 6 Replies

Maybe you should tell us, what's not working.
What's happening when you use gravity-acceleration ?
Just nothing, or does it disappear?

It actually works almost the way i want it to, but lets say when i make the Player jump and the gravity-acceleration kicks in, on higher frame rate the Player jums higher, on lower frame rate it jumps lower.

P.S by gravity i mean that it accelerates, it doesnt stay the same until the Player reaches the ground.

I tried it on SFML, and there seems to be some frame problem.
It happens that when I limit the framerate with SFML-methods my timer doens't count the time correctly.
Maybe that applies also to SDL ?

What happens if you hold the up button? It looks to me like Lazyfoo forgot to add a check to make sure the dot wasn't on the ground. It will therefore occur that when you press the up button in a higher framerate, it will be down for more "ticks" and will jump higher.

There are no problems with SDL's timers.

In this case im using two loops to check collision, gonna change it later, dont think its the main problem, and to avoid confusion, i'm pretty sure that everything is fine in the collision part, the one thing here is that I dont understand is how should I accelerate gravity so it would increase accordingly no matter what framerate there is.

void Player::input()
{
		if ( event.type == SDL_KEYDOWN )
		{
			switch ( event.key.keysym.sym )
			{
			case SDLK_LEFT:
				{
					xVel -= speed;

				} break;
			case SDLK_RIGHT:
				{
					xVel += speed;

				} break;
			case SDLK_SPACE:
				{
					if ( jumped == false )
					{
						yVel -= jumpH;

						jumped = true;
					}

				} break;
			case SDLK_RETURN:
				{
					set_next_state( STATE_EDITOR );

				} break;
			case SDLK_x:
				{
					accelerating = true;

				} break;
			case SDLK_z:
				{
					shoot();

				} break;
			}
		}
		else
			if ( event.type == SDL_KEYUP )
			{
				switch ( event.key.keysym.sym )
				{
				case SDLK_LEFT:
					{
						xVel += speed;

					} break;
				case SDLK_RIGHT:
					{
						xVel -= speed;

					} break;
				case SDLK_x:
					{
						accelerating = false;

					} break;
				}
			}
}
void Player::logic()
{	
	//Adding gravity
	yVel += dropSpeed * ( DELTA.get_ticks() / 1000.f );

	tile_logic();

	//Restarting Delta
	DELTA.start();
}

And

void Player::tile_logic()
{
	//Only set Flags and Cords in collision loops

	//Collision-------------------------

	tileCollX = false; 
	tileCollY = false; 
	
	boxOffsetX += xVel * ( DELTA.get_ticks() / 1000.f );
	
	for ( unsigned int i = 0; i < TILES.size(); ++i )
	{
		if ( touches_wall( boxOffsetX, boxOffsetY, boxOffsetW, boxOffsetH, TILES[ i ]-> get_box_x(), TILES[ i ]-> get_box_y(), TILES[ i ]-> get_box_w(), TILES[ i ]-> get_box_h(), TILES[ i ]-> get_tile_type() ) )
		{
			if ( xVel > 0 )
			{
				boxOffsetX = TILES[ i ]-> get_box_x() - boxOffsetW;
			}
			else
				if ( xVel < 0 )
				{
					boxOffsetX = TILES[ i ]-> get_box_x() + tileW;
				}

				tileCollX = true;
		}
	}

	boxOffsetY += yVel * ( DELTA.get_ticks() / 1000.f );

	for ( unsigned int i = 0; i < TILES.size(); ++i )
	{
		if ( touches_wall( boxOffsetX, boxOffsetY, boxOffsetW, boxOffsetH, TILES[ i ]-> get_box_x(), TILES[ i ]-> get_box_y(), TILES[ i ]-> get_box_w(), TILES[ i ]-> get_box_h(), TILES[ i ]-> get_tile_type() ) )
		{
			if ( yVel > 0 )
			{
				boxOffsetY = TILES[ i ]-> get_box_y() - boxOffsetH;

				jumped = false;
			}
			else
				if ( yVel < 0 )
				{
					boxOffsetY = TILES[ i ]-> get_box_y() + tileH;
				}

				tileCollY = true;
		}
	}

	//-----------------------------------

	if ( tileCollY == true )
	{
		yVel = 0;
	}
}

The problem stems from the fact that you use a very bad integration method for you acceleration.

Here are some basics about motion:

If p is the position of an object, and that object is travelling at a constant speed v, then, at time t, the object will be at position:
[TEX]$p(t)\, =\, v\, t\, +\, p(t_0)$[/TEX].
This is an exact formula (not an approximation). And thus, how you are computing the constant-speed case is correct, with [TEX]$dp\,=\, v\, dt$[/TEX].

If the object is travelling at constant acceleration a (e.g. gravity), then, at time i, the object will have a speed v which is given by:
[TEX]$v(t)\, =\, a\, t\, +\, v(t_0)$[/TEX]
This is also an exact formula, and thus, it is correct to compute the speed with [TEX]$dv\,=\, a\, dt$[/TEX] as you do.

However, if the object is travelling at constant acceleration, its position is given by this exact formula:
[TEX]$p(t)\, =\,\frac{1}{2}\, a\, t^2\, +\, v(t_0)\, t\, +\, p(t_0)$[/TEX]
And so, you should compute your increment in position using the formula:
[TEX]$p(t_{k+1})\, =\,\frac{1}{2}\, a\, dt^2\, +\, v(t_k)\, dt\, +\, p(t_k)$[/TEX]
and:
[TEX]$v(t_{k+1})\, =\,a\, dt\, +\, v(t_k)$[/TEX]


It might seem to you that such a small change in the formula shouldn't make such a big difference, but it does. The formula you were using is very inaccurate, and its accuracy is largely affected by the sampling rate (or frame-rate). What you saw happening is simply the difference in accuracy between the slower and faster frame-rates.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.