【发布时间】: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 轴上的原始位置。也感谢您对我的代码的评论,尽管我认为我的碰撞响应代码非常混乱。不过现在更干净了,我会在我的主帖中发布更新的代码。