【问题标题】:C++ SFML collision is not accurateC++ SFML 冲突不准确
【发布时间】:2016-07-09 07:52:42
【问题描述】:

我正在使用 C++ 中的 SFML 制作 2D 游戏,但我遇到了碰撞问题。我有一个玩家和一张由瓷砖制成的地图。不起作用的是我的碰撞检测不准确。当我将玩家向上然后向下移动到瓷砖时,结果会有所不同。

我知道这个问题的根源可能是使用帧之间的增量时间来计算玩家移动 - 所以它不是恒定的。但是它可以平滑运动,所以我不知道如何以其他方式进行。我尝试使用恒定的速度值并使碰撞完全准确 - 速度必须非常低,我对此并不满意。

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

玩家位置更新中,我创建了未来的精灵对象,它是应用移动后的对象,以获取它的边界并将其传递给碰撞检查器。 collision checker 我还传递了玩家位置,因为我的地图存储在 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;
}

Full project on Github

我的解决方案

我已将更新函数中的 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 和偏移向量,这样它就可以自己计算边界。

【问题讨论】:

    标签: c++ collision sfml


    【解决方案1】:

    当玩家碰撞到一块瓷砖时,你应该计算穿透力,即“玩家进入瓷砖的多少”的值。当你有这个值时,把你的播放器推回来。

    【讨论】:

    • 我实际上是这样做的,但感谢您的想法。我没有想出如何获得 smfl 穿透值(这会很复杂,因为我需要专门检查每一面)。虽然我知道对偏移向量做了一些操作,但前一个会发生碰撞。
    【解决方案2】:

    这只是一个想法,但是当您将浮点 x 和 y 类型转换为整数然后将它们相除时,您的碰撞检测可能会有些不准确。这可能会导致问题,因为浮动中的某些数据可能会丢失。如果浮点数是 3.5 或 3.3 或 3.9,那么它将变为 3,这会影响您的碰撞计算。

    【讨论】:

    • 我只将 x, y 浮点数转换为 int 来计算我应该检查玩家周围的哪些图块。碰撞计算是使用浮点值进行的。
    • 你可能会检查错误的瓷砖,但因为这个原因
    猜你喜欢
    • 2015-06-11
    • 2017-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-14
    • 2012-11-20
    • 1970-01-01
    相关资源
    最近更新 更多