Jumping and tile collision

Hello everybody, I’m trying to learn SDL using the tutorials for some time now, and I’m trying to figure out how to make a 2D platformer. I have figured out most of the stuff, but I have some problems getting jumping, gravity and tile collision to work together.

I’m currently trying to make a very simple platformer with a tile based level, but I can’t quite get the character to behave right when he collides with the tiles.

This is the piece of code that seems to cause me all the trouble.
You may need a barf bag.

const int gravity = 10;
 
void sprite::events()
{
    if( event.type == SDL_KEYDOWN )
    {
        switch( event.key.keysym.sym )
        {
            case SDLK_d:
            vel += SPRITE_WIDTH / 4;
            break;
 
            case SDLK_a:
            vel -= SPRITE_WIDTH / 4;
            break;
 
            default:
            {
 
            }
        }
 
        if( event.key.keysym.sym == SDLK_SPACE  && jump == false )
        {
            jump = true;
            jvel = -50;
        }
    }
 
    if( event.type == SDL_KEYUP )
    {
        switch( event.key.keysym.sym )
        {
            case SDLK_d:
            vel -= SPRITE_WIDTH / 4;
            break;
 
            case SDLK_a:
            vel += SPRITE_WIDTH / 4;
            break;
 
            default:
            {
 
            }
        }
    }
}
 
void sprite::move( tile * tiles[] )
{
    x += vel;
    box.x = x;

    }
 
    if( ( x < 0 ) || ( x + SPRITE_WIDTH > LEVEL_WIDTH ) || collision( box , tiles ) == true )
    {
        x -= vel;
        box.x = x;
    }
 
    y += jvel;
    jvel += gravity;
    box.y = y;
 
    if( jvel >= gravity * 5 )
    {
        jvel = gravity * 5;
    }
 
    if( y > LEVEL_HEIGHT )
    {
        y = 0;
        x = 0;
        jvel = 0;
    }
 
    if( collision( box , tiles ) == true && jump == true )
    {
        jump = false;
        y -= jvel;
        box.y = y;
        jvel = 0;
    }

    if( collision( box , tiles ) == true ) jvel = 0;

    if( collision( box , tiles ) == false && jvel >= gravity ) jump = true;
}

When the character stands on a tile, he continuously jumps 10 pixels up and down.

From what I understand, this is caused by gravity. When the character collides with the tiles, y (the character position) gets reduced by jvel, and jvel becomes 0. But then, gravity makes jvel 10 again, and y gets reduced by it to start all over.

If I do not turn jvel to 0, the character will stand OK on the ground, but if he jumps on a platform he will be standing jvel pixels above it.

I’m not sure what to do here, so some help would be appreciated.

Thank you in advance. :slight_smile:

Take a look at this

http://www.parallelrealities.co.uk/2011/10/intermediate-game-tutorial-4-tile-based.html

Basically, you should check each axis separately, so test the horizontal movement first, then the vertical movement. If the character hits a wall, set the velocity of that axis to 0 and place the character as close to the wall as possible.

Thanks.
I actually figured it out without having to seperate vertical from horizontal collisions.
Here is the code for anyone interested. :slight_smile:

class sprite
{
    private:

    SDL_Rect box;

    int jvel;
    int y;

    bool jump;

    int offs;
    int vel;
    int frame;
    int status;

    public:

    sprite( int X , int Y );
    void events();
    void jumps();
    void move( tile * tiles[] );
    void scamera();
    void show();
};

sprite::sprite( int X , int Y )
{
    jvel = 0;
    y = Y;

    jump = false;

    offs = X;
    vel = 0;
    frame = 0;
    status = FR;

    box.x = offs;
    box.y = y;
    box.w = FW;
    box.h = FH;
}

void sprite::events()
{
    if( event.type == SDL_KEYDOWN )
    {
        switch( event.key.keysym.sym )
        {
            case SDLK_d:
            vel += FW / 4;
            break;

            case SDLK_a:
            vel -= FW / 4;
            break;

            default:
            {

            }
        }
    }

    if( event.type == SDL_KEYUP )
    {
        switch( event.key.keysym.sym )
        {
            case SDLK_d:
            vel -= FW / 4;
            break;

            case SDLK_a:
            vel += FW / 4;
            break;

            default:
            {

            }
        }
    }
}

void sprite::jumps()
{

    Uint8* keystate = SDL_GetKeyState(NULL);

    if(keystate[SDLK_SPACE] && jump == false )
    {
        jump = true;
        jvel = -50;
    }
}

void sprite::move( tile * tiles[] )
{
    offs += vel;
    box.x = offs;

    if( ( offs < 0 ) || ( offs + FW > LW ) || col( box , tiles ) )
    {
        offs -= vel;
        box.x = offs;
    }

    y += jvel;
    box.y = y;

    if( col( box , tiles ) == false && jvel >= gravity )
    {
        jump = true;
    }

    if( col( box , tiles ) == true )
    {
        if( jump == true && jvel >= 0 )
        {
            jump = false;
        }

        if( jvel >= 0)
        {
            y -= jvel - (TH % jvel);
            jvel = 0;
        }
        else
        {
            y -= jvel;
            jvel = 0;
        }
        box.y = y;
    }

    jvel += gravity;

    if( jvel >= gravity * 5 )
    {
        jvel = gravity * 5;
    }

    if( y > LH + 50 )
    {
        y = 0;
        offs = 0;
        status = FR;
    }
}

void sprite::scamera()
{
    camera.x = ( offs + FW / 2 ) - SW / 2;
    camera.y = ( y + FH / 2 ) - SH / 2;

    if( camera.x < 0 )
    {
        camera.x = 0;
    }

    if( camera.x > LW - camera.w )
    {
        camera.x = LW - camera.w;
    }

    if( camera.y < 0 )
    {
        camera.y = 0;
    }

    if( camera.y > LH - camera.h )
    {
        camera.y = LH - camera.h;
    }
}

void sprite::show()
{
    if( vel < 0 )
    {
        status = FL;
        frame++;
    }
    else if( vel > 0 )
    {
        status = FR;
        frame++;
    }
    else
    {
        frame = 0;
    }

    if( frame >= 4 )
    {
        frame = 0;
    }

    if( status == FR )
    {
        apply( offs - camera.x , y - camera.y , face , screen , frame * FW , 0 , FW , FH );
    }
    else if( status == FL )
    {
        apply( offs - camera.x , y - camera.y , face , screen , frame * FW , 100 , FW , FH );
    }
}

…And the main loop!

int main( int argc , char * args[] )
{
    bool quit = false;

    if( init() == false )
    {
        return 1;
    }

    if( loadf() == false )
    {
        return 2;
    }

    timer fps;
    sprite s( 50 , 50 );
    tile * tiles[TT];

    if( settiles( tiles ) == false )
    {
        return 5;
    }

    while( quit == false )
    {
        fps.start();
        scrolling();
        if( edit == true ) edshow( current );

        while( SDL_PollEvent( &event ) )
        {
            if( event.type == SDL_KEYDOWN )
            {
                if( event.key.keysym.sym == SDLK_l )
                {
                    edit = !edit;
                }
            }

            if( event.type == SDL_QUIT )
            {
                quit = true;
            }

            if( edit == false )
            {
                s.events();
            }
            else
            {
                edevents( tiles );
            }
        }

        if( edit == false )
        {
            s.jumps();
            scrolling();

            s.move( tiles );
            s.scamera();
        }
        else
        {
            edcamera();
        }

        for( int t = 0 ; t < TT ; t++ )
        {
            tiles[t]->show();
        }

        if( edit == false )
        {
            s.show();
        }

        if( SDL_Flip( screen ) == -1 )
        {
            return 4;
        }

        if( fps.gett() < 1000 / FPS )
        {
            SDL_Delay( ( 1000 / FPS ) - fps.gett() );
        }
    }

    edsave( tiles );
    clean( tiles );

    return 0;
}
1 Like