我正在使用SFML和C++制作2D游戏,但遇到了碰撞问题。我有一个玩家和一张由瓷砖组成的地图。问题在于我的碰撞检测不准确。当我将玩家向上移动,然后向下移动时,结果不同。
我知道这个问题的源头可能是使用帧之间的delta时间计算玩家移动 - 因此它不是恒定的。但它平滑了运动,所以我不知道其他方法该怎么做。我尝试过使用恒定速度值并使碰撞完全准确 - 速度必须非常低,我对此不满意。
在玩家位置更新时,我创建一个未来精灵对象,这是应用移动后的对象,以获得它的边界并将其传递给碰撞检查器。对于碰撞检查器,我还会传递玩家位置,因为我的地图存储在2D数组的平铺指针中,所以我只检查玩家范围内的这些内容。
我知道这个问题的源头可能是使用帧之间的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;
}
我的解决方案
我已将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和偏移向量,以便可以自行计算边界。