2

I'm trying to make a area-of-effect feature. Until now, I've managed collision by checking if one rectangle intersects another rectangle.

That's easy, since I only create 2 Rectangle variables and check if they intersect. However, I can't seem to find an easy way to create a Circle variable.

How can I create a circle with radius r, and then check if any rectangle/circle intersects with it?

Val
  • 495
  • 1
  • 4
  • 24

3 Answers3

3

Collision detection and response can be a complicated topic. However, if you only need a few basic collision types the calculations are pretty easy.

Note, that once you start involving rotations and shapes more complex than circles and rectangles things can get a bit hairy. Also, it depends on what you want to do after the collision is detected. Moving shapes require more work than stationary ones.

If you do need any of these more complex things, I suggest taking a look at a physics engine like Farseer Physics or Box2D.XNA.

That said, let's breakdown the calculations.

As you said, collision detection between 2 rectangles is pretty straightforward:

var rectangle1 = new Rectangle(100, 200, 300, 400);
var rectangle2 = new Rectangle(150, 250, 350, 450);

if(rectangle1.Intersects(rectangle2))
{
    // do your thing
}

Now, let's say we created a Circle class that behaves in a similar way.

public struct Circle
{
    public Circle(int x, int y, int radius)
        : this()
    {
        X = x;
        Y = y;
        Radius = radius;
    }

    public int Radius { get; private set; }
    public int X { get; private set; }
    public int Y { get; private set; }

    public bool Intersects(Rectangle rectangle)
    {
        // the first thing we want to know is if any of the corners intersect
        var corners = new[]
        {
            new Point(rectangle.Top, rectangle.Left),
            new Point(rectangle.Top, rectangle.Right),
            new Point(rectangle.Bottom, rectangle.Right),
            new Point(rectangle.Bottom, rectangle.Left)
        };

        foreach (var corner in corners)
        {
            if (ContainsPoint(corner))
                return true;
        }

        // next we want to know if the left, top, right or bottom edges overlap
        if (X - Radius > rectangle.Right || X + Radius < rectangle.Left)
            return false;

        if (Y - Radius > rectangle.Bottom || Y + Radius < rectangle.Top)
            return false;

        return true;
    }

    public bool Intersects(Circle circle)
    {
        // put simply, if the distance between the circle centre's is less than
        // their combined radius
        var centre0 = new Vector2(circle.X, circle.Y);
        var centre1 = new Vector2(X, Y);
        return Vector2.Distance(centre0, centre1) < Radius + circle.Radius;
    }

    public bool ContainsPoint(Point point)
    {
        var vector2 = new Vector2(point.X - X, point.Y - Y);
        return vector2.Length() <= Radius;
    }
}

Now, if I've got my calculations correct (I wrote this off the top of my head) you should be able to use the new Circle class the same way you can use the XNA / MonoGame Rectangle class.

However, you may have also realised in the process that there's actually a few different combinations of collisions now. Circle-Circle, Rectangle-Rectangle, Rectangle-Circle and sometimes it's convenient to have a reversed Circle-Rectangle. This can get very tricky to manage (and feels wrong) if you put all these methods on the actual shape classes. Most physics engines I've seen usually have some collision helper classes that pull all these methods into one static class.

Good luck :) Let me know if I make any mistakes.

craftworkgames
  • 9,437
  • 4
  • 41
  • 52
  • Thank you very much. It worked pretty well, except that I was getting some errors. After trying some stuff, I saw that it wasn't creating a perfect circle. I fixed it by seeing this: http://stackoverflow.com/a/402010/2363810 – Val Jul 05 '14 at 21:01
  • I edited your answer to include the corrected Intersect method. Thanks – Val Jul 05 '14 at 21:02
2

I don't have the reputation to comment yet so I'll post this through an answer:

The accepted answer has an error in the corner generation:

 var corners = new[]
        {
            new Point(rectangle.Top, rectangle.Left),
            new Point(rectangle.Top, rectangle.Right),
            new Point(rectangle.Bottom, rectangle.Right),
            new Point(rectangle.Bottom, rectangle.Left)
        };

The coordinates of each point is inverted! This will cause a "specular" collision check, in other words if you have a bullet at x = 300 and y = 200, the check will return true at position x = 200 and y = 300. It will also return true in the correct position due to that return true at the end to manage unknown cases.

So, in other to fix that you need to have:

var corners = new[]
            {
            new Point(rectangle.Left, rectangle.Top),
            new Point(rectangle.Right, rectangle.Top),
            new Point(rectangle.Right, rectangle.Bottom),
            new Point(rectangle.Left, rectangle.Bottom)
            };
iLikeKFC
  • 169
  • 13
0

You probably need to create your own Circle class with position and radius.

Regarding collision between circle and rectangle the way I've done it is to break up rectangles into four segments. Then you check to see if the circle intersects with any of the four segments. This is done by checking the distance from the center of the circle to the segment, and seeing if the distance is less than the circle's radius.

You'll also need to check if the circle's center point is within the rectangle as that would be considered a collision even though the segments don't overlap the circle.

The math for segment vs. circle collision should be pretty easy to track down with a search.

Victor Chelaru
  • 4,491
  • 3
  • 35
  • 49