SFML tilemap collision

|▌冷眼眸甩不掉的悲伤 提交于 2019-12-11 02:57:50

问题


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.

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;
        }
    }
}

回答1:


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.




回答2:


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.



来源:https://stackoverflow.com/questions/27790433/sfml-tilemap-collision

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!