5
\$\begingroup\$

I cribbed some code from another web tutorial (SuperKoalio - https://github.com/libgdx/libgdx/blob/master/tests/gdx-tests/src/com/badlogic/gdx/tests/superkoalio/SuperKoalio.java) about detecting a collision between my player, and tiles within a tilemap layer called "walls". The game is an RPG (Gauntlet) style game. The collision detection generally works but occasionally, the player will pass through a wall and appear much further away on the map, or suddenly outside the bounds of the map. It's almost like there are gaps.

The code I have used appears to check the tiles immediately to the left and right of the player, and then performs another check on the tiles above and below the player. I think the idea is fundamentally okay, except that this doesnt take into consideration diagonal movement, which I believe might be causing the player to pass through walls.

The code inside my detection routine is as follows:

public void checkCollisions(float delta) {
    ////////////////////////////////////////////////////////////////
    //collision detection - X
    int startX, startY, endX, endY;
    startX  = (int) (position.x);
    endX    = (int) (position.x + 1);
    startY  = (int) (position.y);
    endY    = (int) (position.y);

    playerRect = rectPool.obtain();
    playerRect.set(position.x, position.y, 1.0f, 1.0f);

    getWalls(startX, startY, endX, endY, walls);

    for (Rectangle wall : walls)
    {
        //Rectangle rectangle = rectangleObject.getRectangle();
        if (playerRect.overlaps(wall) && velocity.x != 0) {
            velocity.x = 0;
            if(facingDir == 3) {
                position.x = wall.x+1.1f;
            } else if(facingDir == 4) {
                position.x = wall.x-1.1f;
            }

            break;

        }
        // collision happened
        //break;
    }


    playerRect.x = position.x;


    // if the player is moving upwards, check the tiles to the top of it's
    // top bounding box edge, otherwise check the ones to the bottom
    if (velocity.y > 0)
    {
        startY = (int) (position.y);
        endY = (int) (position.y + 1.1f);
    }
    else
    {
        startY = endY = (int) (position.y);
    }

    startX = (int) (position.x);
    endX = (int) (position.x + 1);

    getWalls(startX, startY, endX, endY, walls);

    //playerRect.y = velocity.y;

    for (Rectangle wall : walls)
    {
        if (playerRect.overlaps(wall) && velocity.y != 0)
        {
            velocity.y = 0;
            //velocity.x = 0;
            if(facingDir == 1) {
                position.y = wall.y - 1.1f;
            } else if(facingDir == 2) {
                position.y = wall.y + 1.1f;
            }
            break;
        }
        break;
    }

    playerRect.y = position.y;

    rectPool.free(playerRect);

    velocity.x = 0;
    velocity.y = 0;

}

And the "getWalls()" method which loads the walls layer is as follows:

/**
 * Get the walls of the map for collision detection between the player and the walls, so that
 * the player cannot pass through.
 * @param startX
 * @param startY
 * @param endX
 * @param endY
 * @param walls
 */
private void getWalls(int startX, int startY, int endX, int endY, Array<Rectangle> walls)
{
    TiledMapTileLayer layer = (TiledMapTileLayer) MapRenderer.map.getLayers().get("walls");
    layer.setVisible(false);
    rectPool.freeAll(walls);
    walls.clear();

    for (int y = startY; y <= endY; y++)
    {
        for (int x = startX; x <= endX; x++)
        {
            TiledMapTileLayer.Cell cell = layer.getCell(x, y);
            if (cell != null)
            {
                Rectangle rect = rectPool.obtain();
                rect.set(x, y, 1, 1);
                walls.add(rect);
            }
        }
    }
}

Does anyone have any ideas as to why this routine is not very robust?

I really want to be able to achieve this as simply and effectively as possible. The same technique would also need to be applied to enemies, to stop them from also passing through walls.

Surely there must be a better and more reliable way of doing this, or is it just that there are faults/gaps in my code - for example if I it a wall in a diagonal direction?

Regards

James

\$\endgroup\$
0
\$\begingroup\$

In your update loop:

  • You first handle the input from the user and move him accordingly.

  • Then, you check for collisions with the walls and identify which wall/walls collide with the player. To improve the efficiency of that, instead of checking every single wall in your game, you can check only the walls close to your player and check if there is a collision with those walls. To do that, you will also have to consider how fast the player is moving.

  • Lastly, if there is a collision, your player should now be inside 1 or more walls. So, using the position and size of the wall/walls that the user collided with (you calculated those walls in the previous step), you need to move your player right on the edges of those walls so that the player, just barely, no longer collides with them. To do that, you will also have to consider the player's position before and after the input was handled and move the player towards the opposite direction.

\$\endgroup\$
0
\$\begingroup\$

I think that you have error in your collision testing function. Maybe it has to be like this:

foreach (Rectangle wall in walls)
    {

   /* your code here */

    }

In your original code you are not testing all the walls from the list generated by getWalls() procedure.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.