C++ SFML碰撞不准确

3
我正在使用SFML和C++制作2D游戏,但遇到了碰撞问题。我有一个玩家和一张由瓷砖组成的地图。问题在于我的碰撞检测不准确。当我将玩家向上移动,然后向下移动时,结果不同。Player very high above tile Player above tile Player standing on tile
我知道这个问题的源头可能是使用帧之间的delta时间计算玩家移动 - 因此它不是恒定的。但它平滑了运动,所以我不知道其他方法该怎么做。我尝试过使用恒定速度值并使碰撞完全准确 - 速度必须非常低,我对此不满意。
void Player::move() {
    sf::Vector2f offsetVec;

    if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
        offsetVec += sf::Vector2f(0, -10);

    if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
        offsetVec += sf::Vector2f(0, 10);

    if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
        offsetVec += sf::Vector2f(-10, 0);

    if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
        offsetVec += sf::Vector2f(10, 0);

    this->moveVec += offsetVec;
}

void Player::update(float dt, Map *map) {
    sf::Vector2f offset = sf::Vector2f(this->moveVec.x * this->playerSpeed * dt,
                                       this->moveVec.y * this->playerSpeed * dt);
    sf::Sprite futurePos = this->sprite;
    futurePos.move(offset);
    if (map->isCollideable(this->pos.x, this->pos.y, futurePos.getGlobalBounds())) {
        this->moveVec = sf::Vector2f(0, 0);
        return;
    }
    this->sprite.move(offset);
    this->pos += offset;
    this->moveVec = sf::Vector2f(0, 0);
    return;
}

在玩家位置更新时,我创建一个未来精灵对象,这是应用移动后的对象,以获得它的边界并将其传递给碰撞检查器。对于碰撞检查器,我还会传递玩家位置,因为我的地图存储在2D数组的平铺指针中,所以我只检查玩家范围内的这些内容。
bool Map::isCollideable(float x, float y, const sf::FloatRect &playerBounds) {
    int startX = int(x) / Storage::tileSize;
    int startY = int(y) / Storage::tileSize;
    Tile *tile;
    for (int i = startX - 10; i <= startX + 10; ++i) {
        for (int j = startY - 10; j <= startY + 10; ++j) {
            if (i >= 0 && j >= 0) {
                tile = getTile(i, j);
                if (tile != nullptr && playerBounds.intersects(tile->getGlobalBounds()))
                    return true;
            }
        }
    }
    return false;
}

在Github上查看完整项目

我的解决方案

我已将update函数中的if语句更改为while语句,这样可以减小偏移向量,直到没有碰撞为止。我仍需要进行一些调整,但总体思路是:

void Player::update(float dt, Map *map) {
    int repeats = 0;
    sf::Vector2f offset = sf::Vector2f(this->moveVec.x * this->playerSpeed * dt,
                                       this->moveVec.y * this->playerSpeed * dt);
    sf::Sprite futurePos = this->sprite;
    while (map->isCollideable(this->pos.x, this->pos.y, futurePos, offset)) {
        offset = 0.7f * offset;
        repeats++;
        if (repeats > 5) {
            this->moveVec = sf::Vector2f(0, 0);
            return;
        }
    }
    this->sprite.move(offset);
    this->pos += offset;
    this->moveVec = sf::Vector2f(0, 0);
    return;
}

我还需要重新设计isCollideable方法,使其可以接受sf::Sprite和偏移向量,以便可以自行计算边界。

2个回答

1
当玩家与图块碰撞时,您应该计算渗透,即“玩家进入图块的程度”。当您获得此值时,请将您的玩家向后推回相同的距离。

我其实用了别的方法,但还是谢谢你的建议。我没有想到如何获取SMFL渗透值(因为需要分别检查每一面,所以会变得很复杂)。虽然我对偏移向量进行了一些操作,但知道之前的操作会导致碰撞。 - Łukasz Szcześniak

0

这只是一个想法,但当您将浮点数x和y强制转换为整数并进行除法运算时,您的碰撞检测可能存在不准确性。这可能会导致问题,因为某些浮点数中的数据可能会丢失。 如果浮点数为3.5或3.3或3.9,则会变为3,这会影响您的碰撞计算。


我只将x,y浮点数强制转换为整数,以计算应检查哪些围绕玩家的瓷砖。碰撞计算使用浮点值进行。 - Łukasz Szcześniak
你可能会因此检查错误的瓷砖。 - Careful Now

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接