Thread: SDL: Character collision with tiles.

  1. #1
    Registered User Kain's Avatar
    Join Date
    Nov 2012
    Posts
    17

    SDL: Character collision with tiles.

    Hello again.

    I'm trying to learn SDL for some time now using LazyFoo's tutorials.

    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.

    Code:
    const int gravity = 10;
    
    void sprite::events()
    {
        if( event.type == SDL_KEYDOWN )
        {
            switch( event.key.keysym.sym )
            {
                case SDLK_d:
                moving = true;
                vel += SPRITE_WIDTH / 4;
                break;
    
                case SDLK_a:
                moving = true;
                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:
                moving = false;
                vel -= SPRITE_WIDTH / 4;
                break;
    
                case SDLK_a:
                moving = false;
                vel += SPRITE_WIDTH / 4;
                break;
    
                default:
                {
    
                }
            }
        }
    }
    
    void sprite::move( tile * tiles[] )
    {
        if( moving == true )
        {
            x += vel;
            box.x = offs;
        }
    
        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 = false;
            y -= jvel;
            box.y = y;
            jvel = 0;
        }
    }
    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 stank 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 will be appreciated.

    Also, when the character bumps his head on the bottom of a platform, he will start ascending until he gets on top of it. This is probably related to my main problem. I want the vertical collision to behave quite like the horizontal, but I don't know how.

    Another minor problem is that when the character falls on a hole and re-enters the level, he falls through the tiles instead of landing of them. But if I press left or right while he's falling, he lands OK. I'm not sure what causes this, though.

    Thank you in advance.

  2. #2
    Your imaginary friend
    Join Date
    Jan 2010
    Location
    Canada
    Posts
    76
    Personally, I would check collision before updating the screen. So your game loop would check gravity, then check collision, and apply it after all that, which would put your movement back to 0, then apply the actual movement and update the screen, this way your character would not jump up and down when standing on a platform, hope it helps!

  3. #3
    Bored Programmer
    Join Date
    Jul 2009
    Location
    Tomball, TX
    Posts
    428
    Its ok halo 2 had this design flaw. Super Jump!

  4. #4
    Registered User Kain's Avatar
    Join Date
    Nov 2012
    Posts
    17
    Quote Originally Posted by jerimo View Post
    Personally, I would check collision before updating the screen. So your game loop would check gravity, then check collision, and apply it after all that, which would put your movement back to 0, then apply the actual movement and update the screen, this way your character would not jump up and down when standing on a platform, hope it helps!
    I knew the solution was something ridiculously simple! It did not even cross my mind just changing the order of the functions!
    I almost solved it, and now I'm confident I can make it work perfectly by tweaking it some more.
    Thank you so much for the help!

    Quote Originally Posted by Lesshardtofind View Post
    Its ok halo 2 had this design flaw. Super Jump!
    I LOL'd hard!

  5. #5
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    You may have already solved this issue but as a side note in 2D platformers you should check horizontal collisions first and deal with them. Then check the vertical collisions. This will allow your character to hit walls and slide up or down them.

  6. #6
    Registered User Kain's Avatar
    Join Date
    Nov 2012
    Posts
    17
    I almost solved it without having to divide the collision to horizontal and vertical.
    The only problem now is that sometimes when I jump or fall on tiles, it takes the player about 4 frames to slowly fall on the tile. Especially when I jump on tiles higher than the player.

    This is the code so far:

    Code:
    bool col( SDL_Rect a , SDL_Rect b )
    {
        int lefta , leftb;
        int righta , rightb;
        int topa , topb;
        int bottoma , bottomb;
    
        lefta = a.x;
        righta = a.x + a.w;
        topa = a.y;
        bottoma = a.y + a.h;
    
        leftb = b.x;
        rightb = b.x + b.w;
        topb = b.y;
        bottomb = b.y + b.h;
    
        if( righta <= leftb || lefta >= rightb || bottoma <= topb || topa >= bottomb )
        {
            return false;
        }
    
        return true;
    }
    
    bool col( SDL_Rect a , tile * tiles[] )
    {
        for( int t = 0 ; t < TT ; t++ )
        {
            if( tiles[t]->gettype() == PLATFORM )
            {
                if( col( a , tiles[t]->getbox() ) == true )
                {
                    return true;
                }
            }
        }
    
        return false;
    }
    
    void sprite::events()
    {
        if( event.type == SDL_KEYDOWN )
        {
            switch( event.key.keysym.sym )
            {
                case SDLK_d:
                vel += 15;
                break;
    
                case SDLK_a:
                vel -= 15;
                break;
    
                default:
                {
    
                }
            }
        }
    
        if( event.type == SDL_KEYUP )
        {
            switch( event.key.keysym.sym )
            {
                case SDLK_d:
                vel -= 15;
                break;
    
                case SDLK_a:
                vel += 15;
                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 )
        {
            y -= jvel;
            box.y = y;
    
            if( jump == true && jvel >= 0 )
            {
                jump = false;
            }
    
            jvel = 0;
        }
    
        jvel += gravity;
    
        if( jvel >= gravity * 5 )
        {
            jvel = gravity * 5;
        }
    
        if( y > LH + 50 )
        {
            y = 0;
            offs = 0;
            status = FR;
        }
    }
    ...And in the main loop:

    Code:
        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 )
                    {
                        if( edit == false )
                        {
                            edit = true;
                        }
                        else if( edit == true )
                        {
                            edit = false;
                        }
                    }
                }
    
                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() );
            }
        }
    Happy holidays! :-)
    Last edited by Kain; 12-29-2012 at 05:27 PM.

  7. #7
    Bored Programmer
    Join Date
    Jul 2009
    Location
    Tomball, TX
    Posts
    428
    Not quite seeing your collision glitch but I noticed

    Code:
    if( edit == false )
    {
      edit = true;
    }
    else if( edit == true )
    {
      edit = false;
    }
    could just be
    Code:
    edit = !edit;
    Virtual reality hello world http://www.rodneybrothers.com/vr/vrh...rld/index.html in html and javascript.
    Viewable with dodocase, google cardboard, OR, and other compatible VR gear.

  8. #8
    Bored Programmer
    Join Date
    Jul 2009
    Location
    Tomball, TX
    Posts
    428
    It seems after all your collisions you subtract the applied velocity back out. Meaning you would create extra space betwen the objects resting point and where the collision occured.

    Say an object had an acceleration of 5.0m/s but the collision occured at 4.8m from the starting location of the object on this turn. After the collision the object should be put at a resting point as close to the collision as possible (so at 4.7m) rather than being bumped back to its original position. Otherwise you will have to continually run extra loops until a value that is around 4.7 is actually passed to the function.

    Obviously if you were making a detailed physics engine you would have to worry about transfering momentum and energy into the collision object rather than just putting the object as close as possible, but for the sake of a 2d sidescroller platformer this kind of detail isn't needed (unless you want to add movable objects).
    Last edited by Lesshardtofind; 12-29-2012 at 07:15 PM.
    Virtual reality hello world http://www.rodneybrothers.com/vr/vrh...rld/index.html in html and javascript.
    Viewable with dodocase, google cardboard, OR, and other compatible VR gear.

  9. #9
    Registered User Kain's Avatar
    Join Date
    Nov 2012
    Posts
    17
    Thank you so much for the help!
    I changed...

    Code:
    if( col( box , tiles ) == true )
        {
            y -= jvel;
            box.y = y;
    ...to...

    Code:
    if( col( box , tiles ) == true )
        {
            if( jvel >= 0) y -= jvel - (TH % jvel);
            else y -= jvel;
            box.y = y;
    ...and now it seems to work almost perfectly!

    Thanks again!
    Last edited by Kain; 12-30-2012 at 07:31 AM.

  10. #10
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    You should still split the collisions into horizontal and vertical. It makes the whole process much simpler.

  11. #11
    Registered User
    Join Date
    Jan 2013
    Posts
    1

    SDL libraries instalation problem.

    Hello, I'have download the SDL libraries on official website and I try to instal it. Librarie are decompressed and I put SDL.dll with my program in C. She is called in my program with the command "#include <SDL/SDL.h>. I try to compile a simple program (open a window), the software indicates an error "SDL_Init has not found ..." and for all SDL functions.

    thank you for the gait to indicate that the software finds the good way.

  12. #12
    Bored Programmer
    Join Date
    Jul 2009
    Location
    Tomball, TX
    Posts
    428
    1. Please start a new thread when you have a question.
    2. Did you check your linker parameters?
    3. You said you included SDL/SDL.h but did you include it in all files that use SDL commands?
    4. Its nearly impossible to tell what you are doing wrong here without seeing source code or the exact error message.
    Virtual reality hello world http://www.rodneybrothers.com/vr/vrh...rld/index.html in html and javascript.
    Viewable with dodocase, google cardboard, OR, and other compatible VR gear.

  13. #13
    Registered User Kain's Avatar
    Join Date
    Nov 2012
    Posts
    17
    I've been trying all day without even a break to make different functions for horizontal and vertical collision detections, but I can't seem to get it right.

    The best I could come up with is this:

    Code:
    bool vertical collision( SDL_Rect a , SDL_Rect b )
    {
        int lefta , leftb;
        int righta , rightb;
        int topa , topb;
        int bottoma , bottomb;
    
        lefta = a.x;
        righta = a.x + a.w;
        topa = a.y;
        bottoma = a.y + a.h;
    
        leftb = b.x;
        rightb = b.x + b.w;
        topb = b.y;
        bottomb = b.y + b.h;
    
        if( bottoma >= topb && topa <= bottomb && righta > leftb && lefta < rightb ) return true;
    
        return false;
    }
    
    bool horizontal_collision( SDL_Rect a , SDL_Rect b )
    {
        int lefta , leftb;
        int righta , rightb;
        int topa , topb;
        int bottoma , bottomb;
    
        lefta = a.x;
        righta = a.x + a.w;
        topa = a.y;
        bottoma = a.y + a.h;
    
        leftb = b.x;
        rightb = b.x + b.w;
        topb = b.y;
        bottomb = b.y + b.h;
    
        if( righta >= leftb && lefta <= rightb && bottoma > topb && topa < bottomb ) return true;
    
        return false;
    }
    As you can see they are almost identical, and the sprite goes crazy.

    This is the moving function for the sprite:
    Code:
    void player::move( Uint32 delta_ticks , tile * tiles[] )
    {
        x += xvel * ( delta_ticks / 1000.f );
    
        if( x < 0 ) x = 0;
        if( x + PLAYER_WIDTH > LEVEL_WIDTH ) x = LEVEL_WIDTH - PLAYER_WIDTH;
    
        if( horizontal_collision( box , tiles ) )
        {
            if( xvel > 0 ) x -= 1;
            else if( xvel < 0 ) x += 1;
        }
    
        y += yvel * ( delta_ticks / 1000.f );
    
        if( vertical_collision( box , tiles ) )
        {
            if( yvel >= 0 )
            {
                jumping = false;
                yvel = 0;
                y -= 1;
            }
            else
            {
                jumping = true;
            }
        }
    
        yvel += gravity;
    
        if( y > LEVEL_HEIGHT + 50 )
        {
            y = 0;
            x = 0;
            status = FACING_RIGHT;
        }
    
        box.x = x;
        box.y = y;
    }
    ( I will change y -= 1 , x -= 1 and x += 1 once I figure out how to calculate the closest distance the sprite can go to the tile. )

    Once again I need the help of someone more experienced to proceed. Any help would be appreciated.
    Thanks in advance.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. 2d Tiles Tutorial?
    By Dark Dude in forum Game Programming
    Replies: 4
    Last Post: 10-17-2006, 11:52 PM
  2. tiles
    By pode in forum Game Programming
    Replies: 2
    Last Post: 01-01-2003, 09:01 PM
  3. Tiles
    By JoshG in forum Game Programming
    Replies: 18
    Last Post: 06-05-2002, 08:39 AM
  4. Array of Tiles
    By Okiesmokie in forum Game Programming
    Replies: 5
    Last Post: 05-29-2002, 10:14 PM

Tags for this Thread