私は単純な 2D ゲームに取り組んでいますが、衝突の検出と応答に問題があります。この記事から改作したコードを使用しています: http://www.gamedev.net/page/resources/_/technical/game-programming/swept-aabb-collision-detection-and-response-r3084
コードに多くの変更を加えてみましたが、期待どおりに動作させることができないようです。私のアルゴリズムは、1 つの移動エンティティと 1 つ以上の静的ブロックの間の衝突を検出しようとします。次のロジックを使用します。
1. ブロードフェーズ衝突検出:
エンティティの動きをカプセル化する AABB を作成し、静的ブロックの AABB とのオーバーラップを検出します。それらの衝突イベントをリストに追加し、そのリストを衝突の時間で並べ替えます。
2. 狭位相衝突検出:
各衝突イベントの衝突時間と衝突法線を計算し、エンティティを各静的ブロックの外側の適切な位置に移動します。衝突法線を使用して、エンティティを適切な位置にスライドさせます。
衝突イベントの衝突時間を計算するために使用しているコードは次のとおりです。
float Collision::collisionTime(QVector2D &normal)
{
// _one is our moving entity, _two is a static block
// Calculate the distance to entry and distance to exit
QVector2D distToEntry, distToExit;
QVector2D velocity = _one->velocity();
if (velocity.x() > 0)
{
distToEntry.setX(_two->rect().left() - _one->rect().right());
distToExit.setX( _two->rect().right() - _one->rect().left());
}
else
{
distToEntry.setX(_two->rect().right() - _one->rect().left());
distToExit.setX( _two->rect().left() - _one->rect().right());
}
if (velocity.y() > 0)
{
distToEntry.setY(_two->rect().top() - _one->rect().bottom());
distToExit.setY( _two->rect().bottom() - _one->rect().top());
}
else
{
distToEntry.setY(_two->rect().bottom() - _one->rect().top());
distToExit.setY( _two->rect().top() - _one->rect().bottom());
}
// Calculate the entry and exit times.
QVector2D entry, exit;
if (velocity.x() == 0)
{
entry.setX(-std::numeric_limits<float>::infinity());
exit.setX(std::numeric_limits<float>::infinity());
}
else
{
entry.setX(distToEntry.x() / velocity.x());
exit.setX(distToExit.x() / velocity.x());
}
if (velocity.y() == 0)
{
entry.setY(-std::numeric_limits<float>::infinity());
exit.setY(std::numeric_limits<float>::infinity());
}
else
{
entry.setY(distToEntry.y() / velocity.y());
exit.setY(distToExit.y() / velocity.y());
}
if (entry.x() > 1.0)
{
entry.setX(-std::numeric_limits<float>::infinity());
}
if (entry.y() > 1.0)
{
entry.setY(-std::numeric_limits<float>::infinity());
}
// Find the earliest / latest times of collision.
float entryTime = std::max(entry.x(), entry.y());
float exitTime = std::min(exit.x(), exit.y());
// If there was no collision...
if ((entryTime > exitTime) ||
(entry.x() < 0 && entry.y() < 0))
{
normal = QVector2D(0, 0);
return 1.0;
}
// If there was a collision,
// set the proper normal and return.
if (entry.x() >= entry.y())
{
if (distToEntry.x() < 0)
{
normal = QVector2D(1.0, 0);
}
else
{
normal = QVector2D(-1.0, 0);
}
}
else
{
if (distToEntry.y() < 0)
{
normal = QVector2D(0, 1.0);
}
else
{
normal = QVector2D(0, -1.0);
}
}
return entryTime;
}
衝突応答を生成するコードは次のとおりです。
bool Collision::resolve()
{
QVector2D collisionNormal;
// Calculate the collision time and normal.
float time = collisionTime(collisionNormal);
// Move to the point of collision.
_one->setPos(_one->pos().x() + _one->velocity().x() * time,
_one->pos().y() + _one->velocity().y() * time);
float remainingTime = 1.0 - time;
// If remainingTime > 0, there was a collision.
// Apply a slide effect.
if (remainingTime > 0)
{
float dotProduct = (_one->velocity().x() * collisionNormal.y() + _one->velocity().y() * collisionNormal.x()) * remainingTime;
_one->setVelocity(QVector2D(dotProduct * collisionNormal.y(), dotProduct * collisionNormal.x()));
return true;
}
return false;
}
さて、問題点は・・・以下の場合、動いているエンティティが完全に停止してしまいます。どちらの場合も水平にスライドすると予想していました。衝突検出コードの問題なのか、衝突応答コードの問題なのかを判断できませんでした:
1.
|¯¯¯¯¯¯¯¯|
| Entity |
| |
|________|
|¯¯¯¯¯¯¯¯| ⇲ velocity
| Block |
| |
|________|
2.
|¯¯¯¯¯¯¯¯|
| Entity |
| | ⇲ velocity
|________|
|¯¯¯¯¯¯¯¯‖¯¯¯¯¯¯¯¯|
| Block ‖ Block |
| ‖ |
|________‖________|