Search code examples
c#xnamonogame

How to create a circle variable in Monogame and detect collision with other circles/rectangles


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?


Solution

  • 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.