SFML tilemap collision

2019-07-17 02:45发布

I'm making a 2D game in SFML and I'm trying to figure out how to collide with tiles in a tilemap. I've written some code but the problem is that I get stuck in the tile I'm colliding with as you can see in the picture below. I cannot move in any direction and around 5 pixels of the player is in the wall. enter image description here

This is my collision / movement code

void Player::update(float delta, std::vector<Tile>& tiles) {
    canMove = true;

    for (int i = 0; i < tiles.size(); i++) {
        if (Collision::PixelPerfectTest(sprite, tiles[i].sprite) && tiles[i].collision) {
            canMove = false;
        }
    }

    if (canMove) {
        move(delta);
    }
}

void Player::move(float delta) {
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::W) || sf::Keyboard::isKeyPressed(sf::Keyboard::Up) || sf::Joystick::getAxisPosition(0, sf::Joystick::Y) < -20) {
        movement.y -= speed * delta;
    }
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::A) || sf::Keyboard::isKeyPressed(sf::Keyboard::Left) || sf::Joystick::getAxisPosition(0, sf::Joystick::X) < -20) {
        movement.x -= speed * delta;
    }
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::S) || sf::Keyboard::isKeyPressed(sf::Keyboard::Down) || sf::Joystick::getAxisPosition(0, sf::Joystick::Y) > 20) {
        movement.y += speed * delta;
    }
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::D) || sf::Keyboard::isKeyPressed(sf::Keyboard::Right) || sf::Joystick::getAxisPosition(0, sf::Joystick::X) > 20) {
        movement.x += speed * delta;
    }

    sprite.setPosition(movement);
}

This is the PixelPerfectTest function (it's not mine)

bool PixelPerfectTest(const sf::Sprite& Object1, const sf::Sprite& Object2, sf::Uint8 AlphaLimit) {
        sf::FloatRect Intersection;
        if (Object1.getGlobalBounds().intersects(Object2.getGlobalBounds(), Intersection)) {
            sf::IntRect O1SubRect = Object1.getTextureRect();
            sf::IntRect O2SubRect = Object2.getTextureRect();

            sf::Uint8* mask1 = Bitmasks.GetMask(Object1.getTexture());
            sf::Uint8* mask2 = Bitmasks.GetMask(Object2.getTexture());

            // Loop through our pixels
            for (int i = Intersection.left; i < Intersection.left + Intersection.width; i++) {
                for (int j = Intersection.top; j < Intersection.top + Intersection.height; j++) {

                    sf::Vector2f o1v = Object1.getInverseTransform().transformPoint(i, j);
                    sf::Vector2f o2v = Object2.getInverseTransform().transformPoint(i, j);

                    // Make sure pixels fall within the sprite's subrect
                    if (o1v.x > 0 && o1v.y > 0 && o2v.x > 0 && o2v.y > 0 &&
                        o1v.x < O1SubRect.width && o1v.y < O1SubRect.height &&
                        o2v.x < O2SubRect.width && o2v.y < O2SubRect.height) {

                        if (Bitmasks.GetPixel(mask1, Object1.getTexture(), (int)(o1v.x) + O1SubRect.left, (int)(o1v.y) + O1SubRect.top) > AlphaLimit &&
                            Bitmasks.GetPixel(mask2, Object2.getTexture(), (int)(o2v.x) + O2SubRect.left, (int)(o2v.y) + O2SubRect.top) > AlphaLimit)
                            return true;

                    }
                }
            }
        }
        return false;
    }

EDIT: This is how I got it working in case anyone from the future needs help

void Player::update(float delta, std::vector<Tile>& tiles) {
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::W) || sf::Keyboard::isKeyPressed(sf::Keyboard::Up) || sf::Joystick::getAxisPosition(0, sf::Joystick::Y) < -20) {
        newPos.y -= speed * delta;
    }
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::A) || sf::Keyboard::isKeyPressed(sf::Keyboard::Left) || sf::Joystick::getAxisPosition(0, sf::Joystick::X) < -20) {
        newPos.x -= speed * delta;
    }
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::S) || sf::Keyboard::isKeyPressed(sf::Keyboard::Down) || sf::Joystick::getAxisPosition(0, sf::Joystick::Y) > 20) {
        newPos.y += speed * delta;
    }
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::D) || sf::Keyboard::isKeyPressed(sf::Keyboard::Right) || sf::Joystick::getAxisPosition(0, sf::Joystick::X) > 20) {
        newPos.x += speed * delta;
    }

    sf::Vector2f oldPos = sprite.getPosition();
    sprite.setPosition(newPos);

    for (int i = 0; i < tiles.size(); i++) {
        if (Collision::PixelPerfectTest(sprite, tiles[i].sprite) && tiles[i].collision) {
            sprite.setPosition(oldPos);
            newPos = oldPos;
        }
    }
}

2条回答
闹够了就滚
2楼-- · 2019-07-17 03:04

EDIT: When collision detecting returns false save previous position , when your character collise with an object all u have to do is to restore previous non-collision position.

void Player::update(float delta, std::vector<Tile>& tiles) {
for (int i = 0; i < tiles.size(); i++)
    Collision::PixelPerfectTest(sprite, tiles[i].sprite) && tiles[i].collision) ? canMove = false : prevpos = sprite.getPosition();

if (canMove) {
    move(delta);
}
else
{
 sprite.setPosition(prevpos);
 canMove = true;
}
}

Something like that.

查看更多
狗以群分
3楼-- · 2019-07-17 03:07

Your problem is essentially this:

If your player is not in a wall, he can move in any direction. This includes movements which would put him in a wall. If your player is in a wall, he cannot move at all. So if he moves into a wall (because you allowed him to do so), he can never get out of it.

So, the most obvious fix is to not let the player move into a wall. One simple way to do that is to attempt to move the player, then check for a collision with the wall. If there is a collision, move the player back to its original position.

That should fix your basic problem of getting stuck in walls. Though it may leave you with other problems, such as not being able to get right up next to the wall. For that, what you can do is check exactly where the wall is when a collision happens, and move the player right to it.

查看更多
登录 后发表回答