Search code examples
javaandroidlibgdxcollision-detection

Tile collision detection in Android libgdx game


In this game I'm writing I'm so far just handling collision detection like this with tiles:

I scroll the map 8 units to the left. I then check if there's a collision on the right side of my character with a tile that has a "collision" property. If there is a collision, then I position my character so that its right-edge pixel value is equal to the left edge of the tile.

Now to check for vertical collision detection, I check both the bottom-right and bottom-left corners of my character. If either corner is in a "collision" tile, then I set character's y-value to be equal to the top of the tile (i.e. if it has collided with a solid tile then it will always appear on the top of that tile).

This vertical collision detection works until I run into a wall. Since the vertical-collision-check will always push the character to the top of the tile, if my character jumps and lands in the center of a wall, then he will be stuck at the top of one of the tiles in the wall until you press Jump, then he immediately moves up to the top of the next tile.

What would be a better way to address this vertical collision detection? I was thinking about never letting the character be totally hugging a "wall" tile, so that his right edge is always 1 pixel left from a wall tile, this way I can still do a check on the right-corner of the character and it would never really hit that wall (so the character could still be falling if next to a wall). This seems sloppy though. Is there a better way? I've been researching this all day and I can't seem to find a good solution to it.

Here is what the code looks like:

public void render() 
{       

    Gdx.gl.glClearColor(0, 0, 0, 1);
    Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

    gameTime += Gdx.graphics.getDeltaTime();

    characterDisplay = characterAnimation.getKeyFrame(gameTime, true);




    TiledMapTileLayer layer = (TiledMapTileLayer)tiledMap1.tiledMap.getLayers().get(0);

    float x;
    float x1; 
    float x2;
    x1 = ((characterPosition.x + characterWidth)/tileWidth);
    x2 = (-tileRenderer.tiledMap.x); 
    x = x1 + x2;


    if (isFalling)
        characterPosition.y -= FALLING_SPEED;

    float yBottom;
    float y1 = (int)((characterPosition.y)/tileWidth) + 1; 
    yBottom = y1;



    tileRenderer.tiledMap.x -= .2;


    if (layer.getCell((int)x, (int)yBottom).getTile().getProperties().containsKey("collision"))
    {
        characterPosition.x = ((x + tileRenderer.tiledMap.x )* tileWidth) - characterWidth;
    }


    y1 = (int)(characterPosition.y/tileWidth);
    yBottom = y1;

    x1 = ((characterPosition.x + characterWidth)/tileWidth);
    x2 = (-tileRenderer.tiledMap.x); 
    x = x1 + x2;    

    if ((layer.getCell((int)x, (int)yBottom).getTile().getProperties().containsKey("collision") || layer.getCell((int)x - 1, (int)yBottom).getTile().getProperties().containsKey("collision")))
    {

        characterPosition.y = (yBottom * tileHeight) + tileHeight;
        isFalling = false;
    }

    tileRenderer.render();      




    //draw textures
    batch.setProjectionMatrix(camera.combined);
    batch.begin();
    batch.draw(characterDisplay, characterPosition.x, characterPosition.y);
    batch.end();


    //handle user input
    handleUserInput();
}

Solution

  • Instead of simply setting the character "outside" of the tile that you impact with, you might want to consider "pushing" the character out of the tile. So, if you jump into a wall, the code will recognize your previous position and movement vector, and realize that you collided first in the x direction. Your actor will then get "pushed back" along its movement vector until it is touching the object that it collided with. Then check again to make sure that you haven't collided with anything and repeat. This should also help make sure you don't embed your character in a wall/floor.

    Effectively, try to do it iteratively rather than all at once.

    You could also do things preemptively - that is, check before you move. See here for an example.

    There's also box2d, which would be pretty quick to pick up for this level of complexity. It will do collision detection much more cleanly than a roll-your-own, and is GREAT for platformers.