碰撞后释放卡住的物体
Freeing stuck objects after collision
我 运行 我的游戏引擎中的碰撞解决有一个小问题。如果两个物体碰撞并且碰撞导致速度变为零,则物体的边缘将相互重叠并且它们将被卡住。
有没有办法为这种情况实施一个包罗万象?也就是说,将物体沿正确的方向移动到足够的程度,这样它们就不会卡住。
以下是我检查碰撞和移动物体的方式。在实体上调用更新时,它会移动 (x,y)。
public static void Update()
{
for (var iterator = 0; iterator < PhysicsEntities.Count; iterator++)
{
for (var index = iterator + 1; index < PhysicsEntities.Count; index++)
{
if (!Collision.ResolveCollision(PhysicsEntities[iterator],
PhysicsEntities[index], Detection)) continue;
PhysicsEntities[iterator].Update();
PhysicsEntities[iterator].Collided = true;
PhysicsEntities[index].Update();
PhysicsEntities[index].Collided = true;
}
}
foreach (var entity in PhysicsEntities)
{
entity.Update(velocity: true);
entity.Collided = false;
}
}
}
这是实体的更新函数:
public void Update(bool velocity = false)
{
if(!Movable) return;
if (!Collided)
{
var moveX = Velocity.X / Universe.UpdateInterval;
var moveY = Velocity.Y / Universe.UpdateInterval;
Position.Move(moveX, moveY);
BoundingBox.Move(moveX, moveY);
}
if(velocity) UniversalForces();
}
private void UniversalForces()
{
Velocity.Scale(1 - Universe.Friction);
Velocity.Add(Universe.GravityMag, Universe.GravityDir);
}
最后,这是一张对象卡住的模拟图像。如您所见,只是边缘被卡住了:
快速解决方案是将两个对象移回前一个抽动点的位置,并将导致碰撞的任何其他对象也移回。它有效,但它看起来很乱,并导致一些看起来非常糟糕的行为——比如直接推墙会留下一个缝隙,但向墙倾斜会留下一个更小的缝隙。很乱。
更好的解决方案是将两个对象沿它们的负速度矢量向后移动足够远,以便它们不再接触。通常一些点积数学可以给你你需要的东西,虽然向后迭代可以工作(慢)。
长话短说,永远不要让对象重叠。在它发生之前处理它,避免卡住的紧张、无法移动东西等。
当一个物体与另一个物体碰撞时,让它回溯自己的运动矢量,使两个物体的质心之间的距离等于两个半径。如果它是一个圆,这会起作用 - 如果它是一个复杂的多边形,你真的需要进行边缘碰撞检测而不是边界球检测。如果边界球发生碰撞,那么您将转向复杂的边缘检测。最后的技巧是一样的;检查碰撞然后备份运动矢量,直到找到准确(或几乎准确)的碰撞点。
我能够根据人们提出的建议弄清楚。我所做的一些更改包括让每个对象每次更新仅与任何其他对象碰撞一次,并在碰撞后移动对象直到它不再碰撞。这是我用来执行此操作的代码,请随时在任何项目中使用它,如果您对此有任何疑问,请告诉我。
public static void Update()
{
foreach (var a in PhysicsEntities)
{
foreach (var b in PhysicsEntities)
{
if (a.Equals(b) ||
!Collision.ResolveCollision(a, b, Detection) || b.Collided) continue;
while (Detection == Detection.BoundingBox ?
Collision.BoundingBox(a, b) :
Collision.PixelPerfect(a, b))
{
const float moveBy = .5F;
var moveX = a.Position.X > b.Position.X ? moveBy : -moveBy;
var moveY = a.Position.Y > b.Position.Y ? moveBy : -moveBy;
if (a.Movable)
{
a.Move(moveX, moveY);
a.Velocity.Scale(-1);
}
else if (b.Movable)
{
b.Move(moveX * -1, moveY * -1);
b.Velocity.Scale(-1);
}
}
a.Update();
b.Update();
a.Collided = a.Movable;
}
}
foreach (var entity in PhysicsEntities)
{
entity.Update(velocity: true);
entity.Collided = false;
}
}
我 运行 我的游戏引擎中的碰撞解决有一个小问题。如果两个物体碰撞并且碰撞导致速度变为零,则物体的边缘将相互重叠并且它们将被卡住。
有没有办法为这种情况实施一个包罗万象?也就是说,将物体沿正确的方向移动到足够的程度,这样它们就不会卡住。
以下是我检查碰撞和移动物体的方式。在实体上调用更新时,它会移动 (x,y)。
public static void Update()
{
for (var iterator = 0; iterator < PhysicsEntities.Count; iterator++)
{
for (var index = iterator + 1; index < PhysicsEntities.Count; index++)
{
if (!Collision.ResolveCollision(PhysicsEntities[iterator],
PhysicsEntities[index], Detection)) continue;
PhysicsEntities[iterator].Update();
PhysicsEntities[iterator].Collided = true;
PhysicsEntities[index].Update();
PhysicsEntities[index].Collided = true;
}
}
foreach (var entity in PhysicsEntities)
{
entity.Update(velocity: true);
entity.Collided = false;
}
}
}
这是实体的更新函数:
public void Update(bool velocity = false)
{
if(!Movable) return;
if (!Collided)
{
var moveX = Velocity.X / Universe.UpdateInterval;
var moveY = Velocity.Y / Universe.UpdateInterval;
Position.Move(moveX, moveY);
BoundingBox.Move(moveX, moveY);
}
if(velocity) UniversalForces();
}
private void UniversalForces()
{
Velocity.Scale(1 - Universe.Friction);
Velocity.Add(Universe.GravityMag, Universe.GravityDir);
}
最后,这是一张对象卡住的模拟图像。如您所见,只是边缘被卡住了:
快速解决方案是将两个对象移回前一个抽动点的位置,并将导致碰撞的任何其他对象也移回。它有效,但它看起来很乱,并导致一些看起来非常糟糕的行为——比如直接推墙会留下一个缝隙,但向墙倾斜会留下一个更小的缝隙。很乱。
更好的解决方案是将两个对象沿它们的负速度矢量向后移动足够远,以便它们不再接触。通常一些点积数学可以给你你需要的东西,虽然向后迭代可以工作(慢)。
长话短说,永远不要让对象重叠。在它发生之前处理它,避免卡住的紧张、无法移动东西等。
当一个物体与另一个物体碰撞时,让它回溯自己的运动矢量,使两个物体的质心之间的距离等于两个半径。如果它是一个圆,这会起作用 - 如果它是一个复杂的多边形,你真的需要进行边缘碰撞检测而不是边界球检测。如果边界球发生碰撞,那么您将转向复杂的边缘检测。最后的技巧是一样的;检查碰撞然后备份运动矢量,直到找到准确(或几乎准确)的碰撞点。
我能够根据人们提出的建议弄清楚。我所做的一些更改包括让每个对象每次更新仅与任何其他对象碰撞一次,并在碰撞后移动对象直到它不再碰撞。这是我用来执行此操作的代码,请随时在任何项目中使用它,如果您对此有任何疑问,请告诉我。
public static void Update()
{
foreach (var a in PhysicsEntities)
{
foreach (var b in PhysicsEntities)
{
if (a.Equals(b) ||
!Collision.ResolveCollision(a, b, Detection) || b.Collided) continue;
while (Detection == Detection.BoundingBox ?
Collision.BoundingBox(a, b) :
Collision.PixelPerfect(a, b))
{
const float moveBy = .5F;
var moveX = a.Position.X > b.Position.X ? moveBy : -moveBy;
var moveY = a.Position.Y > b.Position.Y ? moveBy : -moveBy;
if (a.Movable)
{
a.Move(moveX, moveY);
a.Velocity.Scale(-1);
}
else if (b.Movable)
{
b.Move(moveX * -1, moveY * -1);
b.Velocity.Scale(-1);
}
}
a.Update();
b.Update();
a.Collided = a.Movable;
}
}
foreach (var entity in PhysicsEntities)
{
entity.Update(velocity: true);
entity.Collided = false;
}
}