Jump to content

  • Log In with Google      Sign In   
  • Create Account

How do I implement gravity after walking off a tile?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
8 replies to this topic

#1 farmdve   Members   -  Reputation: 194

Like
0Likes
Like

Posted 04 September 2013 - 07:52 PM

I was finally able to make collision detection in my 2D platformer written in C with SDL 2.0.

 

I then tried to make jumping work, though it's still a bit unrealistic, though not that 2D is ever going to look realistic smile.png

 

7uuhezys.jpg

 

As you can see, my red square which represents the player for development purposes is sitting on top of a soil tile and colliding smile.png. If I jump the player goes up and then back down at a constant speed(for now)

 

This is my jumping code

	if(player.key_press[SDLK_SPACE])
	{
		player.is_jumping = true;
		player.pos.y -= 1000 * prev_frame_tick;
	}
	
	if(player.is_jumping)
	{
		player.pos.y += 200 * prev_frame_tick;
		for(int i = 0; i < Map->tile_num; i++)
		{
			if(Map->tile[i]->type == 2) // Type 2 is a solid tile. Needs renaming.
			{
				if(check_collision_rect(player.pos, *(Map->tile[i])))
				{
					player.pos.y -= 200 * prev_frame_tick;
					player.is_jumping = false;
					break; // if it's colliding with one tile, then that is enough.
				}
			}
		}
	} 

But after jumping and colliding, if I move and there is no solid tile, I just float there

 

smKq3dxs.jpg

 

But I am unsure how to implement gravity to work there, so that I fall on the ground.

 

Also, if it is allowed, I would also like to ask how to fix my collision detection so it does not loop through all the tiles to find the one the player is on or hitting.

The way I currently store my map is using this design

struct Tile {
	int type;
	int y, x;
	int h, w;
	
	int sY, sX; // location on sprite sheet
};

typedef struct _Map {
	struct Tile **tile;
	
	int tile_num;
} _Map;

I store a single tile in it's own structure, allowing me flexibility in modifying the attributes of each tile. But I was told by a person that if I want to be able to use a more CPU-friendly collision detection algorithm I'd need to use a 2D array, but I will then miss out on everything plus how would it work with a 2D int array exactly?



Sponsor:

#2 molehill mountaineer   Members   -  Reputation: 595

Like
4Likes
Like

Posted 05 September 2013 - 12:52 AM

I only skimmed the code but it seems to me that you're only moving the player avatar downwards when the player is jumping.

if(player.is_jumping)
    {
        player.pos.y += 200 * prev_frame_tick;

In other words - gravity only works after the player pressed space.

You could make gravity pull the player downwards every tick, then check for collisions (and make any corrections so that the player doesn't get "stuck" in the tiles).

 

Sorry for the brevity, I'm heading off to work in ten minutes.



#3 PandaDragonThing   Members   -  Reputation: 311

Like
3Likes
Like

Posted 05 September 2013 - 02:28 AM

So the way to implement realestic jumping and falling is to have a force acting on your object, gravity. This is simply a negative acceleration( distance^2/time ). When you jump you apply a positive velocity( distance/time ) which gives that you slow down as you reach the top of your jump and you speed up as you start falling down. You can look up kinematic equations for doing these.

As for the falling down when stepping over a tile, or while falling and you hit the ground. You can check 1 pixel below each of the bottom corners and see if there's nothing below you. If so you enable gravity to pull you down. If there is, then you disable gravity. and plant yourself.

However, on what I said above. If you are moving say 3 pixels per second, but only check 1 pixel below, you'll clip into the ground. You can fix this by seing if your stuck in the tile and pop yourself up until you're free, or you do smart checking and check the distance of the rate you travel and then see the exact coordinates you should stop and wait to reach those coordinates and then stick yourself to them.

 

I made some pseudo code which may help explain:

//Called every tick
YAcceleration = -5;

if( GravityIsEnabled() )
{
    // v = v0 + at ( equation )
    YVelocity = YVelocity + ( YAcceleration * TimeSinceLastTick );
}

//Called when player presses jump key.
if( KeyPressed( KEY_SPACE ) )
{
    YVelocity = 10;
    EnableGravity();
}

//Checking tiles down below us for possible collision
if( check_future_collision_rect( player.pos, tile.under, YVelocity ) )
{
    //Check if player has reached the future points of contact 
    if( player.pos >= get_future_collision_rect() )
    {
        //We've hit the ground
        DisableGravity();
        YVelocity = 0;
    }
}

//Check tile directly underneath us.
if( NoTileUnderneathUs )
{
    EnableGravity();
}

Edited by PandaDragonThing, 05 September 2013 - 02:52 AM.


#4 Satharis   Members   -  Reputation: 1263

Like
0Likes
Like

Posted 05 September 2013 - 09:30 AM

To give a brief on what others are saying: essentially there's no "pretty" way to handle collision in any game with constant contact terrain. You basically have to assume all the worst case scenarios in terms of collision. You could be standing still and have a bunch of balls come down and hit you, obviously you'd want to detect those. You might want the ground to fall away even when you're standing still.

Using that thinking you can determine that you can't really "assume" that you're on the ground, probably the best way to handle it is to always be checking if the ground under you has stopped being solid. Using that thinking you can have a "jumping" state and a "falling" state if you'd like, in order to effect gameplay like when you can jump or to change movement while in air. Gravity should basically be applied unless you find yourself not in a falling state, and would stay in that state until the ground underneath disappeared or you jump.

To give a little example: say you walk off a cliff after being on flat ground. At that point you could detect the ground is no longer under your character and toggle "falling" at that point you could make gravity affect the player. You could technically leave gravity on but this is kind of a collision resolution waste because it will be trying to pull you back up out of the ground every update loop, it also may cause "jittering" visual glitches if you use something like interpolation.

There's a few articles under the game programming section here that cover things like platformers and how older 2d platformers handled, or how the author assumed they handled collision. You might pick up some ideas by looking!

Oh, but keep in mind in terms of gravity and collision that you'll be wanting to check for collision in other directions as well, i.e. falling into a pit and hitting the side, or something bonking you on the head, as I hinted at before.

Edited by Satharis, 05 September 2013 - 09:33 AM.


#5 farmdve   Members   -  Reputation: 194

Like
0Likes
Like

Posted 13 September 2013 - 04:38 AM

I tried to implement this many times and it ends up not working correctly. I figured I only need to check the bottom of my player's rect and the top of the tile below me for collision so I did this
 
int check_collision_below(Rect A, struct Tile B)
{
    int topB;
    int bottomA;
 
    topB = (int)B.y;
    bottomA = (int)A.y + A.h;
 
    if(bottomA <= topB) 
        return 0;
 
    return 1;
}
 
I used this code
 
Rect collision_underneath;
collision_underneath = player.pos; // assign player position.
collision_underneath.y += 1.00; // check one pixel below us.
for(int j = 0; j < Map->tile_num; j++)
{
    if(Map->tile[j]->type == ENUM_TILE_AIR) // if tile is non-solid i.e air for instance
    {
        if(!check_collision_below(collision_underneath, *(Map->tile[j]))) // if there is NO collision, it means we should fall.
        { 
            puts("Nothing there, should fall");
            break;
        }
     }
}
 
but it doesn't work properly and fails to detect if there is ground or not... Actually, it does. But what happens is that when I walk off a tile it doesn't detect it, but when I jump it starts detecting that there is nothing below me till I hit the ground(and depending on my logic it fails detect any solid tiles above the ground when I stand on them), which is weird because the code from jumping and detecting walking off a tile are separate.
 
And if you are wondering about the types, here they are.
 
struct Tile {
	int type;
	int y, x;
	int h, w;
	
	int sY, sX; // location on sprite sheet
	
	int row, col;
	bool solid;
};

typedef struct Rect {
	int h, w;
	float x, y;
	float prev_x, prev_y; // Previous positions
	float velX, velY; // Velocity for X and Y coordinate. 
} Rect;

Edited by farmdve, 13 September 2013 - 04:46 AM.


#6 markr   Crossbones+   -  Reputation: 1653

Like
1Likes
Like

Posted 13 September 2013 - 08:46 AM

The way gravity normally works in game (and incidentally, real life), is that it is *always* pulling objects down. If they happen to be on a solid platform, the platform is continuously pushing the object back up, so it can't fall. Assuming you use Euler Integration (if you don't know, you probably are.), you're storing a velocity vector of the object, then if you get a collision with a solid thing like the ground, you'll want to modify the velocity to remove the component which is going into the ground (in practice, this is something like: if (velocity.y < 0) velocity.y = 0; ) Mark

#7 Ludus   Members   -  Reputation: 970

Like
0Likes
Like

Posted 13 September 2013 - 08:59 AM

For collision, you should be checking whether or not the player's bounding box overlaps with the bounding box of a tile.

 

Use something like this:

bool Collision_check(Rect A, struct Tile B)

{

if (A.x + A.w < B.x) return false; //collision is impossible since the right edge of the player is left of the leftmost edge of the tile
if (A.x > B.x + B.w) return false;
if (A.y + A.h < B.y) return false;
if (A.y > B.y + B.h) return false;

return true; //since none of those conditions were met, collision must of happened, so return true

}

Edited by Ludus, 13 September 2013 - 09:01 AM.


#8 farmdve   Members   -  Reputation: 194

Like
0Likes
Like

Posted 13 September 2013 - 11:50 AM

 

For collision, you should be checking whether or not the player's bounding box overlaps with the bounding box of a tile.

 

Use something like this:

bool Collision_check(Rect A, struct Tile B)

{

if (A.x + A.w < B.x) return false; //collision is impossible since the right edge of the player is left of the leftmost edge of the tile
if (A.x > B.x + B.w) return false;
if (A.y + A.h < B.y) return false;
if (A.y > B.y + B.h) return false;

return true; //since none of those conditions were met, collision must of happened, so return true

 

Thank you, but I have no problem with collision detection except for falling off, I just can't detect that there is no solid tile below me properly.


Edited by farmdve, 13 September 2013 - 11:51 AM.


#9 Ludus   Members   -  Reputation: 970

Like
0Likes
Like

Posted 13 September 2013 - 06:42 PM


Thank you, but I have no problem with collision detection except for falling off, I just can't detect that there is no solid tile below me properly.

 

If acceleration due to gravity is always being applied in each loop, then the player should be constantly checking collision below himself. By default, he will fall due to the constant gravity if nothing is below him.






Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS