2
\$\begingroup\$

I have managed to create a collision detection system for my tile-based jump'n'run game (written in C++/SFML), where I check on each update what values the surrounding tiles of the player contain and then I let the player move accordingly (i. e. move left when there is an obstacle on the right side).

This works fine when the player sprite is not too big: Given a tile size of 5x5 pixels, my solution worked quite fine with a spritesize of 3x4 and 5x5 pixels.

My problem is that I actually need the player to be quite gigantic (34x70 pixels given the same tilesize). When I try this, there seems to be an invisible, notably smaller boundingbox where the player collides with obstacles, the player also seems to shake strongly. Here some images to explain what I mean:

  • Works: works

  • Doesn't work: doesn't

  • Another example of non-functioning: nonfunctioning (the player isn't falling, he stays there in the corner)

My code for getting the surrounding tiles looks like this (I removed some parts to make it better readable):

std::vector<std::map<std::string, int> > Game::getSurroundingTiles(sf::Vector2f position)
{
    // converting the pixel coordinates to tilemap coordinates
    sf::Vector2u pPos(static_cast<int>(position.x/tileSize.x), static_cast<int>(position.y/tileSize.y));
    std::vector<std::map<std::string, int> > surroundingTiles;

    for(int i = 0; i < 9; ++i)
    {
        // calculating the relative position of the surrounding tile(s)
        int c = i % 3;
        int r = static_cast<int>(i/3);
        // we subtract 1 to place the player in the middle of the 3x3 grid
        sf::Vector2u tilePos(pPos.x + (c - 1), pPos.y + (r - 1));

        // this tells us what kind of block this tile is
        int tGid = levelMap[tilePos.y][tilePos.x];

        // converts the coords from tile to world coords
        sf::Vector2u tileRect(tilePos.x*5, tilePos.y*5);

        // storing all the information
        std::map<std::string, int> tileDict;
        tileDict.insert(std::make_pair("gid", tGid));
        tileDict.insert(std::make_pair("x", tileRect.x));
        tileDict.insert(std::make_pair("y", tileRect.y));

        // adding the stored information to our vector
        surroundingTiles.push_back(tileDict);
    }

    // I organise the map so that it is arranged like the following:
    /*
     * 4 | 1 | 5
     * --  -- --
     * 2 | / | 3
     * --  -- --
     * 6 | 0 | 7
     *
     */
    return surroundingTiles;
}

I then check in a loop through the surrounding tiles, if there is a 1 as gid (indicates obstacle) and then check for intersections with that adjacent tile.

The problem I just can't overcome is that I think that I need to store the values of all the adjacent tiles and then check for them. How? And may there be a better solution? Any help is appreciated.

P.S.: My implementation derives from this blog entry, I mostly just translated it from Objective-C/Cocos2d.

\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

I believe that the cause of the problem, is that you are only checking for collision with the 9 surrounding tiles. This is OK, as long as the width and height of the entity that is being checked for collision is less than or equal to the width and height of the tiles. In order to fix this issue, you need to check for collision with the tiles from (x - 1, y - 1), to (x + width + 1, y + height + 1). The following image should demonstrate what I mean.

Tiles being checked

By doing this, you ensure that all of the tiles that the player can collide with (assuming that the player isn't going fast enough to move through a tile), will be checked for collision.

With this in mind, you should now have collision code that looks something like this:

xPos += velocityX;

// Note that xPos and yPos are the pixel x and y of the player, not the tile x and y of the player.

int left = xPos / TILE_WIDTH;
int right = (xPos + PLAYER_WIDTH) / TILE_WIDTH;
int top = yPos / TILE_HEIGHT;
int bottom = (yPos + PLAYER_HEIGHT) / TILE_HEIGHT;

for(int xt = left; xt <= right; xt++)
{
    for(int yt = top; yt <= bottom; yt++)
    {
        Tile t = world.getTile(xt, yt);

        if(collidesWith(t))
        {
            // Handle X collision
        }
    }
}

yPos += velocityY;

for(int xt = left; xt <= right; xt++)
{
    for(int yt = top; yt <= bottom; yt++)
    {
        Tile t = world.getTile(xt, yt);

        if(collidesWith(t))
        {
            // Handle Y collision
        }
    }
}

You may be wondering why I am moving and checking for collision on each axis separately. Unfortunately this would take too long to explain. However, I have already explained this in an answer to another question. I hope this helps.

\$\endgroup\$
1
  • \$\begingroup\$ I am not sure if I was clear enough. When I said "(x + width + 1, y + height + 1). I was referring to the player's width and height. \$\endgroup\$
    – Lysol
    Feb 12, 2014 at 17:20
3
\$\begingroup\$

I want to just comment, but alas I don't have 50 freaking points. :)

Your posted code makes no mention of the player size, all it seems to be doing is retrieving a set amount of the surrounding tiles. Which if they are as you say 5x5 pixels in size, and you are only getting the surrounding 3x3 tiles, than you are only checking the tiles within 15x15 pixels of your characters POSITION. Which makes sense for your smaller character of 3x4 or 5x5, but for a 34x70 you are not checking the tiles out far enough. So your character is clipping through the tiles.

I think you should expand your search of the surrounding tiles based upon the size of your character.

Like

int tilesToCheckWide=characterSize.w/tileSize.w;
int tilesToCheckHigh=characterSize.h/tileSize.h;

and then put that in your for loop to get the surrounding tiles. I think it may be better to change your whole system to a polygonal collision check instead of a pixel/tile check. But that has its own whole range of problems with platformers.

Good luck and I hope I made sense. -Mike

EDIT: LOL, I just saw aidan's answer and he has a picture :(

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .