You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

带旋转角度的2D圆形与矩形双向碰撞检测实现求助

圆形与旋转矩形的碰撞检测问题

我正尝试检测圆形与矩形这两类物体的碰撞情况,但始终无法实现该功能。网上多数示例或教程要么数学性过强,要么未考虑矩形的旋转角度。以下是我目前编写的代码及单元测试:

当前实现代码

public abstract record Location
{
    public abstract double DistanceTo(Location location);
    public abstract double DirectionTo(Location location);
}

public sealed record CircleLocation(double X, double Y, int Radius) : Location
{
    public override double DistanceTo(Location location)
    {
       return location switch
       {
           CircleLocation circleLocation => Math.Sqrt(Math.Pow(X - circleLocation.X, 2) + Math.Pow(Y - circleLocation.Y, 2)) - circleLocation.Radius,
           RectangleLocation rectangleLocation => Math.Max(rectangleLocation.Left - X, X - rectangleLocation.Right) + Math.Max(rectangleLocation.Top - Y, Y - rectangleLocation.Bottom),
           _ => throw new Exception("Invalid location type")
       };
    }

    public override double DirectionTo(Location location)
    {
        return location switch
        {
            CircleLocation circleLocation => Math.Atan2(circleLocation.Y - Y, circleLocation.X - X),
            RectangleLocation rectangleLocation => Math.Atan2(rectangleLocation.CenterY - Y, rectangleLocation.CenterX - X),
            _ => throw new Exception("Invalid location type")
        };
    }
}

// 角度单位为弧度
public sealed record RectangleLocation(double X, double Y, double Width, double Height, double Angle) : Location
{
    public double Left => X - Math.Cos(Angle) * Width / 2;
    public double Top => Y - Math.Sin(Angle) * Height / 2;
    public double Right => Left + (double)Math.Cos(Angle) * Width;
    public double Bottom => Top + Math.Sin(Angle) * Height;
    public double CenterX => X - Width / 2;
    public double CenterY => Y - Height / 2;

    public override double DistanceTo(Location location)
    {
        return location switch
        {
            CircleLocation circleLocation => Math.Sqrt(Math.Pow(X - circleLocation.X, 2) + Math.Pow(Y - circleLocation.Y, 2)) - circleLocation.Radius,
            RectangleLocation rectangleLocation => Math.Max(rectangleLocation.Left - X, X - rectangleLocation.Right) + Math.Max(rectangleLocation.Top - Y, Y - rectangleLocation.Bottom),
            _ => throw new Exception("Invalid location type")
        };
    }

    public override double DirectionTo(Location location)
    {
        return location switch
        {
            CircleLocation circleLocation => Math.Atan2(circleLocation.Y - Y, circleLocation.X - X),
            RectangleLocation rectangleLocation => Math.Atan2(rectangleLocation.CenterY - CenterY, rectangleLocation.CenterX - CenterX),
            _ => throw new Exception("Invalid location type")
        };
    }
}

单元测试代码

public class UnitTest1
{
    [Fact]
    public void CircleToCircleTouches()
    {
        var circle1 = new CircleLocation(0, 0, 1);
        var circle2 = new CircleLocation(2, 0, 1);

        var distance = circle1.DistanceTo(circle2);

        Assert.Equal(0, distance);
    }

    [Fact]
    public void CircleToCircleDoesNotTouch()
    {
        var circle1 = new CircleLocation(0, 0, 1);
        var circle2 = new CircleLocation(4, 0, 1);

        var distance = circle1.DistanceTo(circle2);

        Assert.Equal(2, distance);
    }

    [Fact]
    public void RectangleToRectangleTouches()
    {
        var rectangle1 = new RectangleLocation(0, 0, 2, 2, 0);
        var rectangle2 = new RectangleLocation(2, 2, 2, 2, 0);

        var distance = rectangle1.DistanceTo(rectangle2);

        Assert.Equal(0, distance);
    }

    [Fact]
    public void RectangleToRectangleDoesNotTouch()
    {
        var rectangle1 = new RectangleLocation(0, 0, 2, 2, 0);
        var rectangle2 = new RectangleLocation(3, 3, 2, 2, 0);

        var distance = rectangle1.DistanceTo(rectangle2);

        Assert.Equal(1, distance);
    }

    [Fact]
    public void TestCircleToRectangleDistance()
    {
        var circle = new CircleLocation(0, 0, 1);
        var rectangle = new RectangleLocation(3, 4, 2, 2, 0);

        var distance = circle.DistanceTo(rectangle);

        Assert.Equal(5, distance);
    }

    [Fact]
    public void TestRectangleToCircleDistance()
    {
        var rectangle = new RectangleLocation(0, 0, 2, 2, 0);
        var circle = new CircleLocation(3, 4, 1);

        var distance = rectangle.DistanceTo(circle);

        Assert.Equal(5, distance, 1);
    }
}

内容的提问来源于stack exchange,提问作者Marcel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 02:14:58