Столкновение c++ SFML не является точным



Я делаю 2D-игру с SFML в C++, и у меня есть проблема с коллизией. У меня есть плеер и карта, сделанная из плиток. Что не работает, так это то, что мое обнаружение столкновений не является точным. Когда я перемещаю игрока вверх, а затем вниз к плиткам, все заканчивается по-другому.



Игрок очень высоко над плиткойИгрок над плиткойИгрок, стоящий на плитке



Я знаю, что источником этой проблемы может быть вычисление движения игрока с использованием дельта-времени между кадрами-поэтому оно не является постоянным. Но это сглаживает движения, поэтому я не знаю, как это сделать. сделай это по-другому. Я старался с постоянными скоростными значениями и сделать столкновение полностью точным - скорость должна была быть очень низкой, и меня это не устраивает.



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. В collision checker я также передаю POS игрока, потому что моя карта хранится в 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



Мое решение



Я изменил оператор if в функции update на оператор 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 и вектор смещения, чтобы он мог вычислять границы самостоятельно.

708   2  

2 ответов:

Когда игрок сталкивается с плиткой, вы должны вычислить проникновение, то есть значение "сколько игрок вошел в плитку". Когда у вас есть это значение, подтолкните вашего игрока назад так сильно.

Это просто мысль, но у вас могут быть некоторые неточности в обнаружении столкновений, когда вы набираете float x и y в целые числа, а затем делите их. Это может вызвать проблемы, так как некоторые данные в float могут быть потеряны. Если бы поплавок был 3,5, 3,3 или 3,9, то он стал бы 3, что отбрасывает ваши расчеты столкновения.

Comments

    Ничего не найдено.