扫描AABB碰撞检测和响应问题

时间:2016-02-09 16:30:44

标签: c++ collision-detection bounding-box aabb

我正在开发一个简单的2D游戏,我遇到了碰撞检测和响应问题。我使用的是从本文改编的代码:http://www.gamedev.net/page/resources/_/technical/game-programming/swept-aabb-collision-detection-and-response-r3084

我已经对代码做了很多改动,但我似乎无法让它按预期运行。我的算法尝试检测单个移动实体与一个或多个静态块之间的冲突。它使用以下逻辑:

  

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  |
|        ‖        |
|________‖________|

3 个答案:

答案 0 :(得分:0)

我猜这个问题是实体在每一帧中与盒子碰撞(因为触摸AABB)。尝试将实体移离块一小部分以查看错误是否仍然存在。

答案 1 :(得分:0)

resolve方法中,请勿按remainingTime缩放新的速度。当模拟继续时,它已经按帧时间缩放了速度。

答案 2 :(得分:0)

您需要为可能发生碰撞的所有entryTime计算Blockresolve仅计算与最小(正)entryTime的碰撞,并重复{{1} }}。重复一遍,我的意思是再次计算所有潜在碰撞的remainingTime > 0

没关系,这不会解决所有问题。您的entryTime可能已停止,因为它与右侧Entity的左侧发生碰撞。