我正在用C++中的SFML制作2D游戏,但碰到碰撞问题。我有一个玩家和一张由砖块制成的 map 。无效的是我的碰撞检测不准确。当我将播放器上移然后下移向图块时,其结局会有所不同。



我知道这个问题的根源可能在于使用帧之间的增量时间来计算玩家的移动-因此它不是恒定的。但是它可以平滑运动,所以我不知道该怎么做。我尝试以恒定的速度进行评估,并使碰撞完全准确-速度必须非常低,对此我并不满意。

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

玩家位置更新中,我创建了将来的Sprite对象,该对象是应用移动后的对象,以获取其边界并将其传递给碰撞检查器。对于冲突检查器,我还传递了玩家pos,因为我的 map 存储在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语句,这会减少我的偏移量 vector ,直到不存在冲突为止。我仍然需要进行一些调整,但总体思路是:
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和偏移 vector ,因此它可以自己计算边界。

最佳答案

当玩家与瓷砖碰撞时,您应该计算穿透力,即“玩家进入瓷砖多少”的值。当您拥有此值时,将您的播放器向后推。

关于c++ - C++ SFML冲突不准确,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36154187/

10-13 03:49