XNA/Monogame 检测圆形和矩形之间的碰撞不起作用
XNA/Monogame Detecting collision between Circle and Rectangle not working
所以我有一个 Circle
结构,非常简单,看起来像这样:
public struct Circle
{
public Circle(int x, int y, int radius) : this()
{
Center = new Point(x, y);
Radius = radius;
}
public Point Center { get; private set; }
public int Radius { get; private set; }
}
我有一个 PhysicsEnity
class 看起来像这样:
public class PhysicsEntity
{
public int Width { get; protected set; }
public int Height { get; protected set; }
public Vector2 Position { get; set; }
public Vector2 Velocity { get; set; }
public float Restitution { get; protected set; }
public float Mass { get; protected set; }
public virtual void Update(GameTime gameTime)
{
float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
Velocity += ((Phys.Gravity * dt) * Mass);
Position += Velocity * dt;
}
public virtual void Draw(SpriteBatch spriteBatch) { }
public virtual void ApplyImpulse(Vector2 impulse)
{
Position += impulse;
}
}
我有两个 class 继承自此 class。 CircleEntity
得到一个圆,RectangleEntity
得到一个矩形,但其他没有变化。
为了检查碰撞,我有一个辅助函数,它接受两个 PhysicsEntity
对象,检查它们是什么类型(RectangleEntity
或 CircleEntity
),然后调用该函数对于特定的碰撞类型。如果它们发生碰撞,碰撞检测功能简单 return 和 boolean
。
然后我有一个名为 ResolveCollision
的函数,它有两个实体,看起来像这样:
public static void ResolveCollision(PhysicsEntity a, PhysicsEntity b)
{
if (a.Mass + b.Mass == 0)
{
a.Velocity = Vector2.Zero;
b.Velocity = Vector2.Zero;
return;
}
var invMassA = a.Mass > 0 ? 1 / a.Mass : 0;
var invMassB = b.Mass > 0 ? 1 / b.Mass : 0;
Vector2 rv = b.Velocity - a.Velocity;
Vector2 normal = Vector2.Normalize(b.Position - a.Position);
float velAlongNormal = Vector2.Dot(rv, normal);
if (velAlongNormal > 0) return;
float e = MathHelper.Min(a.Restitution, b.Restitution);
float j = (-(1 + e) * velAlongNormal) / (invMassA + invMassB);
Vector2 impulse = j * normal;
a.Velocity -= invMassA * impulse;
b.Velocity += invMassB * impulse;
}
圆-圆碰撞和矩形-矩形碰撞可以正常工作,但圆-矩形绝对不行。我什至不能让它正确检测碰撞,它总是 returns false。这是矩形-圆形碰撞检测:
public static bool RectangleCircleCollision(CircleEntity a, RectangleEntity b)
{
Circle c = a.Circle;
Rectangle r = b.Rectangle;
Vector2 v = new Vector2(MathHelper.Clamp(c.Center.X, r.Left, r.Right),
MathHelper.Clamp(c.Center.Y, r.Top, r.Bottom));
Vector2 direction = c.Center.ToVector2() - v;
float distSquare = direction.LengthSquared();
return ((distSquare > 0) && (distSquare < c.Radius * c.Radius));
}
此时我完全不知所措。我不知道出了什么问题。我已经研究了几乎所有关于碰撞检测的教程,但我只是不知道。
我做错了什么?
编辑:我错了,矩形-矩形也不起作用。如果有人能给我指点白痴的 2D 碰撞检测指南,我将不胜感激。
圆形和矩形重叠。
下图显示了我们可以找到圆形和矩形的所有情况。
其中深蓝色是要测试的矩形。由其中心及其宽度和高度定义
圈子
- A如果圆心距矩形中心(深蓝色部分)小于宽度和高度的一半,我们就知道它是接触的。
- B圆心的x或y位置距矩形中心的宽度或高度的一半以内,其他距离小于宽度或高度的一半加上圆半径。圆心在矩形外但仍相接
- C 圆在拐角附近,圆心在左上方,但与拐角的距离小于半径,所以它是接触的。
- D 圆在矩形的上边缘和右边缘的半径距离内,但比角的半径距离远。一点都不感人
- E 没有接触,因为圆心大于任何边缘的半径。
我们可以通过考虑对称性来简化解决方案。如果我们使圆与中心的 x 和 y 距离为正,那么我们只是在做一个角
private static bool DoRectangleCircleOverlap(Circle cir, Rectangle rect) {
// Get the rectangle half width and height
float rW = (rect.Width) / 2;
float rH = (rect.Height) / 2;
// Get the positive distance. This exploits the symmetry so that we now are
// just solving for one corner of the rectangle (memory tell me it fabs for
// floats but I could be wrong and its abs)
float distX = Math.Abs(cir.Center.X - (rect.Left + rW));
float distY = Math.Abs(cir.Center.Y - (rect.Top + rH));
if (distX >= cir.Radius + rW || distY >= cir.Radius + rH) {
// Outside see diagram circle E
return false;
}
if (distX < rW || distY < rH) {
// Inside see diagram circles A and B
return true; // touching
}
// Now only circles C and D left to test
// get the distance to the corner
distX -= rW;
distY -= rH;
// Find distance to corner and compare to circle radius
// (squared and the sqrt root is not needed
if (distX * distX + distY * distY < cir.Radius * cir.Radius) {
// Touching see diagram circle C
return true;
}
return false;
}
所以我有一个 Circle
结构,非常简单,看起来像这样:
public struct Circle
{
public Circle(int x, int y, int radius) : this()
{
Center = new Point(x, y);
Radius = radius;
}
public Point Center { get; private set; }
public int Radius { get; private set; }
}
我有一个 PhysicsEnity
class 看起来像这样:
public class PhysicsEntity
{
public int Width { get; protected set; }
public int Height { get; protected set; }
public Vector2 Position { get; set; }
public Vector2 Velocity { get; set; }
public float Restitution { get; protected set; }
public float Mass { get; protected set; }
public virtual void Update(GameTime gameTime)
{
float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
Velocity += ((Phys.Gravity * dt) * Mass);
Position += Velocity * dt;
}
public virtual void Draw(SpriteBatch spriteBatch) { }
public virtual void ApplyImpulse(Vector2 impulse)
{
Position += impulse;
}
}
我有两个 class 继承自此 class。 CircleEntity
得到一个圆,RectangleEntity
得到一个矩形,但其他没有变化。
为了检查碰撞,我有一个辅助函数,它接受两个 PhysicsEntity
对象,检查它们是什么类型(RectangleEntity
或 CircleEntity
),然后调用该函数对于特定的碰撞类型。如果它们发生碰撞,碰撞检测功能简单 return 和 boolean
。
然后我有一个名为 ResolveCollision
的函数,它有两个实体,看起来像这样:
public static void ResolveCollision(PhysicsEntity a, PhysicsEntity b)
{
if (a.Mass + b.Mass == 0)
{
a.Velocity = Vector2.Zero;
b.Velocity = Vector2.Zero;
return;
}
var invMassA = a.Mass > 0 ? 1 / a.Mass : 0;
var invMassB = b.Mass > 0 ? 1 / b.Mass : 0;
Vector2 rv = b.Velocity - a.Velocity;
Vector2 normal = Vector2.Normalize(b.Position - a.Position);
float velAlongNormal = Vector2.Dot(rv, normal);
if (velAlongNormal > 0) return;
float e = MathHelper.Min(a.Restitution, b.Restitution);
float j = (-(1 + e) * velAlongNormal) / (invMassA + invMassB);
Vector2 impulse = j * normal;
a.Velocity -= invMassA * impulse;
b.Velocity += invMassB * impulse;
}
圆-圆碰撞和矩形-矩形碰撞可以正常工作,但圆-矩形绝对不行。我什至不能让它正确检测碰撞,它总是 returns false。这是矩形-圆形碰撞检测:
public static bool RectangleCircleCollision(CircleEntity a, RectangleEntity b)
{
Circle c = a.Circle;
Rectangle r = b.Rectangle;
Vector2 v = new Vector2(MathHelper.Clamp(c.Center.X, r.Left, r.Right),
MathHelper.Clamp(c.Center.Y, r.Top, r.Bottom));
Vector2 direction = c.Center.ToVector2() - v;
float distSquare = direction.LengthSquared();
return ((distSquare > 0) && (distSquare < c.Radius * c.Radius));
}
此时我完全不知所措。我不知道出了什么问题。我已经研究了几乎所有关于碰撞检测的教程,但我只是不知道。
我做错了什么?
编辑:我错了,矩形-矩形也不起作用。如果有人能给我指点白痴的 2D 碰撞检测指南,我将不胜感激。
圆形和矩形重叠。
下图显示了我们可以找到圆形和矩形的所有情况。
其中深蓝色是要测试的矩形。由其中心及其宽度和高度定义
圈子
- A如果圆心距矩形中心(深蓝色部分)小于宽度和高度的一半,我们就知道它是接触的。
- B圆心的x或y位置距矩形中心的宽度或高度的一半以内,其他距离小于宽度或高度的一半加上圆半径。圆心在矩形外但仍相接
- C 圆在拐角附近,圆心在左上方,但与拐角的距离小于半径,所以它是接触的。
- D 圆在矩形的上边缘和右边缘的半径距离内,但比角的半径距离远。一点都不感人
- E 没有接触,因为圆心大于任何边缘的半径。
我们可以通过考虑对称性来简化解决方案。如果我们使圆与中心的 x 和 y 距离为正,那么我们只是在做一个角
private static bool DoRectangleCircleOverlap(Circle cir, Rectangle rect) {
// Get the rectangle half width and height
float rW = (rect.Width) / 2;
float rH = (rect.Height) / 2;
// Get the positive distance. This exploits the symmetry so that we now are
// just solving for one corner of the rectangle (memory tell me it fabs for
// floats but I could be wrong and its abs)
float distX = Math.Abs(cir.Center.X - (rect.Left + rW));
float distY = Math.Abs(cir.Center.Y - (rect.Top + rH));
if (distX >= cir.Radius + rW || distY >= cir.Radius + rH) {
// Outside see diagram circle E
return false;
}
if (distX < rW || distY < rH) {
// Inside see diagram circles A and B
return true; // touching
}
// Now only circles C and D left to test
// get the distance to the corner
distX -= rW;
distY -= rH;
// Find distance to corner and compare to circle radius
// (squared and the sqrt root is not needed
if (distX * distX + distY * distY < cir.Radius * cir.Radius) {
// Touching see diagram circle C
return true;
}
return false;
}