【问题标题】:XNA/Monogame Detecting collision between Circle and Rectangle not workingXNA/Monogame 检测圆形和矩形之间的碰撞不起作用
【发布时间】:2017-04-19 03:18:00
【问题描述】:

所以我有一个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 类:

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;
    }

}

我有两个继承自这个类的类。 CircleEntity 得到一个圆,RectangleEntity 得到一个矩形,但没有其他变化。

为了检查碰撞,我有一个辅助函数,它接收两个PhysicsEntity 对象,检查它们是什么类型(RectangleEntityCircleEntity),然后调用该特定碰撞类型的函数。如果发生碰撞,碰撞检测函数只会返回一个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;
}

Circle-Circle 碰撞和 Rectangle-Rectangle 碰撞可以正常工作,但 Circle-Rectangle 绝对不行。我什至无法让它正确检测碰撞,它总是返回错误。这是矩形-圆形碰撞检测:

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 碰撞检测白痴指南的方向,我将不胜感激。

【问题讨论】:

标签: c# xna collision-detection game-physics monogame


【解决方案1】:

圆形和矩形重叠。

下图显示了我们可以在其中找到圆形和矩形的所有情况。

深蓝色是要测试的矩形。由其中心及其宽度和高度定义

圈子

  • 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;
}

【讨论】:

  • 这真是太棒了,也是迄今为止我在 stackoverflow 上收到的最好的答案。非常感谢。我只是看不到它,您不仅完全解释了它,而且还真的帮助我自己想象了这个问题。老实说,太棒了。
  • 我希望我能再投票一百次。
猜你喜欢
  • 2023-01-26
  • 2014-05-05
  • 1970-01-01
  • 2016-08-31
  • 1970-01-01
  • 1970-01-01
  • 2023-04-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多