【问题标题】:Collision response for rectangles矩形的碰撞响应
【发布时间】:2016-02-19 15:18:32
【问题描述】:

我已经在物理引擎上工作了大约一周,被困了好几天,试图找出解决碰撞的方法。

我的问题是,如果有一个盒子卡在其他 2 个盒子的中间,或者一个盒子和一堵墙之间,我的应用程序将卡在一个 while 循环中。它不会解决冲突。

这是我的代码(注意:如果碰撞在右侧,则意味着对象 A 与对象 B 的右侧发生碰撞。距离是负数,因为对象在彼此内部,并且它在 x 或 y 轴上,具体取决于如果您需要更多代码,例如碰撞类,它只是两个对象的容器,我可以提供。):

edit:使用新的碰撞处理方式编辑代码:

//Move colliding objects so they don't collide anymore.
while (getCollidingAmount(objectVector)){
    for (int i = 0; i < objectVector.size(); i++){
        PhysicsObject* A = objectVector[i];
        if (objectVector[i]->getPhysicsType() != PhysicsType::staticT && A->_collision.size() > 0){
            Collision collision = A->_collision[A->getDeepestPenetrationCollisionIndex(A->_collision)];
            PhysicsObject* B = collision.getObject();


            switch (collision.getSide()){
            case SideOfCollision::left:
            case SideOfCollision::top:
                //Opposite velocity
                if (A->_saveVelocity.x < 0 && B->_saveVelocity.x > 0){
                    long double percentageOfVelocity = std::min(abs(B->_saveVelocity.x), abs(A->_saveVelocity.x)) /
                        std::max(abs(B->_saveVelocity.x), abs(A->_saveVelocity.x));
                    A->_position.x -= percentageOfVelocity*collision.getVectorPenetration().x;
                    A->_position.y -= percentageOfVelocity*collision.getVectorPenetration().y;
                }
                else{
                    A->_position.x -= collision.getVectorPenetration().x;
                    A->_position.y -= collision.getVectorPenetration().y;
                }                       
                break;

            case SideOfCollision::right:
            case SideOfCollision::bottom:
                //Opposite velocity
                if (A->_saveVelocity.x > 0 && B->_saveVelocity.x < 0){
                    long double percentageOfVelocity = 1 - std::min(abs(B->_saveVelocity.x), abs(A->_saveVelocity.x)) /
                        std::max(abs(B->_saveVelocity.x), abs(A->_saveVelocity.x));
                    A->_position.x -= percentageOfVelocity*collision.getVectorPenetration().x;
                    A->_position.y -= percentageOfVelocity*collision.getVectorPenetration().y;
                }
                else{
                    A->_position.x -= collision.getVectorPenetration().x;
                    A->_position.y -= collision.getVectorPenetration().y;
                }
                break;
            }
            updateCollisions(objectVector);
        }
    }
}

更新

我在底部和顶部碰撞中的三角函数有问题:

sf::Vector2<long double> Collision::getVectorPenetration() const{
long double x;
long double y;
long double velX = _object->getVelocity().x;
long double velY = _object->getVelocity().y;
long double angle = atan2(velY, velX);

if (_side == SideOfCollision::left || _side == SideOfCollision::right){
    x = getDistance();
    y = x * tan(angle);
    return sf::Vector2<long double>(x, y);
}
else if (_side == SideOfCollision::top || _side == SideOfCollision::bottom){
    y = getDistance();
    x = y / tan(angle);
    return sf::Vector2<long double>(x, y);
}

}

更新 2

感谢艾曼,我解决了我的问题。更新了我的碰撞响应代码以匹配我处理碰撞的新方法。我现在遇到了另一个问题,重力使我在触摸另一个物体时无法在 X 方向上移动。如果任何熟悉此问题的人想提供任何提示来解决它,我很感激:)。

更新 3

所以看起来重力实际上并不是问题,因为我可以将重力交换到 x 轴,然后能够沿着墙壁滑动盒子。三角函数似乎还是有问题。

【问题讨论】:

  • 这正是物理库允许您为位置和速度求解器设置最大迭代次数的原因。如果超过这个数字,你只需跳出循环。
  • 但是事情仍然会发生冲突,这就是为什么它没有打破循环。仍然有碰撞。但是我可能会尝试我发现的这种方法:stackoverflow.com/questions/18578656/… 不确定它是否对我有帮助。
  • 我已经按照我发布的链接的建议做了一些事情,并且我已经让它工作了!,对于 x 轴。当我在 y 轴(顶部或底部)上发生碰撞时,箱子拉到左边。我在 x 或 y 轴上做一些不同的事情的唯一地方就是这里的代码。你能告诉我在试图找到顶部和底部碰撞的穿透深度时我的三角函数是否错误吗?将在主帖中添加三角函数的代码。
  • 我真的很喜欢你的代码风格。 y = x / cos(angle);x = y / sin(angle); 应该分别是 y = x * tan(angle)x = y/tan(angle)。但是,是的,您确实应该为位置和速度求解器设置迭代限制,因为现在您的算法执行时间趋于无穷大。您当然不希望您的物理引擎将整个应用程序冻结在一帧,因为它必须经过 10000 次迭代才能解决棘手的 1 像素接触问题。
  • 感谢您的回复。我不知道为什么我没有意识到我应该使用棕褐色。我在一张纸上画了草图,现在可以看到了:)。非常感谢,您的代码更改使其工作!不幸的是,这种在 x 和 y 上移动的新方法使它无法沿着地面移动,因为重力不断将我带回到 x 轴上的原始位置。也感谢您对我的代码的评论,尽管我认为我的碰撞响应代码非常混乱。不过现在更干净了,我会在我的主帖中发布更新的代码。

标签: physics collision


【解决方案1】:

我可以想出很多方法来解决这个问题。

1-**更复杂的是**引入摩擦。这是我如何实现它,尽管这未经测试,并且我有可能在我的思路中错过了一些东西。

每个形状都有一个摩擦常数,根据这些你的物体在碰撞时滑动。

首先,您需要获得与您的表面垂直的角度。为此,您只需获得表面法线坡度的arctan。法线只是-1/m,其中m 是您的表面的斜率(您是表面在y 中延伸的程度与/在x 中延伸的程度的比率/商)。让我们将此角度称为“表面法线”sNormal。以后我们可能还需要sAngle-“表面角度”(您可以通过arctan(m) 找到)。与您是在谈论表面的“正面”还是“背面”有关的角度仍然存在一些歧义。您必须手动处理。

接下来,您需要对象飞行轨迹的角度,您已经知道如何找到 (atan2(y,x))。我们称这个角度为oAngle 表示“物体的表面角度”。接下来,计算deltaAngle = sNormal - oAngle。这个角度表示有多少动量没有被表面完全阻挡。 deltaAngle 为 0 表示所有动量都消失了,PI/290 的值表示两个表面平行地相互接触,根本没有阻挡任何动量。介于两者之间的任何内容,我们都会插入:

newSpeed = objectSpeed * deltaAngle/(PI/2);
newVelocity.x = cos(sAngle) * objectSpeed;
newVelocity.y = sin(sAngle) * objectSpeed;

现在假设摩擦力为 0。如果我们让 1 的摩擦力成为不允许对象“滑动”的最大摩擦力,我们在应用 newVelocity 值之前修改 newSpeed,如下所示:newSpeed *= (1-friction);

我们有了它!只要给你的平台一个小于 1 的摩擦值,你的盒子就可以滑动。如果你正在处理直立的盒子,那么表面角度是 PI 用于顶壁,0 用于底部,PI/2 用于右侧,-PI/2 用于左侧壁。

2-更简单的选择是在求解器的计算中从物体的 y 速度中减去重力。

【讨论】:

  • 感谢您的帖子。不幸的是,我认为重力不是问题。我的重力可以设置为 X 或 Y 轴。我现在已经测试将它设置为 X 轴,当它在 X 轴上时,我可以正确地沿着墙壁移动。所以我认为重力不是问题,还有其他问题。我已经用三角公式尝试了几乎所有的东西。如果您想查看完整代码,我已经上传了一个 Visual Studio 2013 项目。 dropbox.com/sh/zpl9t6arziebitv/AABUDdHYhiLN0gj8U0XehtHla?dl=0
  • 有些东西我想改变我设置加速度、速度、力等的方式,但它确实有效。你应该在physicsSystem类collisionResponse()函数(第一部分,不是处理速度处理的部分)和Collision类中寻找。
  • 此外,为了测试系统(如果您这样做),您可能想知道一些事情。你可以用空格跳跃和用a和d移动,你可以在main函数中设置物体的起始速度和位置。在顶部 PhysicsSystem 类 cpp 文件中,您可以更改重力,以查看 x 和 y 轴之间的差异。
  • 我忘了说你需要 SFML 库来构建它。也许这有点太问了,但我有点卡住了。如果你想下载,这里是 sfml:sfml-dev.org/download/sfml/2.3.2
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
  • 2014-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多