简体   繁体   中英

How to determine which side of a rectangle collides with a circle

Before you point out that there are other answers to this question, i have looked at if not all, most of the other answers to this question or a similar question and i haven't found the solution i require.

Basically all i want to be able to do is when the circle/ball collides with a rectangle, i want to determine which side of the rectangle this collision has occured at. I want to find this out so that i can enforce a bit more realistic physics, eg if the ball hits the top of the rectangle, inverse it's Y velocity only... instead of both.

I have tried comparing the X and Y positions of the ball and the rectangle and even the location of both of their bounding boxes... testing even if the bottom of the ball's box has intersected with the rectangles top... using 'if ball.boundingBox.Bottom >= rectangle.boundingBox.Top'.

I have attached a picture to this to show what i am trying to achieve... just in case it's a bit confusing, as it's not detailed... the red what look like v's is the path if the ball comes in from one side, i want the movement upon impact to travel in the opposite way but this depends on the side of the rectangle as to what component of the ball's velocity i will have to change...

FYI i have also looked at vector normalisation... i haven't used it before so forgive me if this could be solved using this...

Thanks v.much for reading

EDIT as i am in a rush, i have used an different image instead... this still shows the behaviour i am trying to achieve, as the physics shown on the diagram is how i want the ball to behave when it collides with the other sides... LINK TO IMAGE: http://codeincomplete.com/posts/2011/6/12/collision_detection_in_breakout/bounce2.v283.png

This code might be more comprehensive than you need and can be refactored to suit your needs but it is a complete answer and is flexible to use with moving bounding rectangles along with moving circles.

here is a graphic to give a visual aid to what the code is doing. the red circle is intersecting with the black rectangle. visualize two imaginary lines going through opposite corners. If you know which side of each of the 2 lines the circle is on, you can deduce the collided edge.

在此输入图像描述

first declare class scope private members

Rectangle CollisionBoxRect;
Rectangle circleRect;
Dictionary<string, Vector2> corners;

In your update after you've moved the circle and set its location and the potential intersected box's location it does a basic check to see if the circle's bounding rect is involved with the block's bounding rect. If so, it then alters the ball's velocity with the appropriate collision normal depending on which side of the rect the circle collided with.

if (CollisionBoxRect.Intersects(circleRect))
{
     ballVelocity = Vector2.Reflect(ballVelocity, GetCollisionNormal(CollisionBoxRect));
}

The following methods support getting the proper side (the normal actually). Some of these methods can be done once in the initialize phase if they never change (like the get corners method);

private Vector2 GetCollisionNormal(Rectangle boxBeingIntersected)
{
    getCorners(boxBeingIntersected);
    bool isAboveAC = isOnUpperSideOfLine(corners["bottomRight"], corners["topLeft"], getBallCenter());
    bool isAboveDB = isOnUpperSideOfLine( corners["topRight"], corners["bottomLeft"], getBallCenter());

    if (isAboveAC)
    {
        if (isAboveDB)
        {
            //top edge has intersected
            return -Vector2.UnitY;
        }
        else
        {
            //right edge intersected
            return Vector2.UnitX;
        }
    }
    else
    {
        if (isAboveDB)
        {
            //left edge has intersected
            return -Vector2.UnitX;
        }
        else
        {
            //bottom edge intersected
            return Vector2.UnitY;
        }
    }
}

public bool isOnUpperSideOfLine(Vector2 corner1, Vector2 oppositeCorner, Vector2 ballCenter)
{
    return ((oppositeCorner.X - corner1.X) * (ballCenter.Y - corner1.Y) - (oppositeCorner.Y - corner1.Y) * (ballCenter.X - corner1.X)) > 0;
}

private Vector2 getBallCenter()
{
    return new Vector2(circleRect.Location.X + circleRect.Width / 2, circleRect.Location.Y + circleRect.Height / 2);
}

private void getCorners(Rectangle boxToGetFrom)
{
    corners.Clear();
    Vector2 tl = new Vector2(boxToGetFrom.X, boxToGetFrom.Y);
    Vector2 tr = new Vector2(boxToGetFrom.X + boxToGetFrom.Width, boxToGetFrom.Y);
    Vector2 br = new Vector2(boxToGetFrom.X + boxToGetFrom.Width, boxToGetFrom.Y + boxToGetFrom.Height);
    Vector2 bl = new Vector2(boxToGetFrom.X, boxToGetFrom.Y + boxToGetFrom.Height);
    corners.Add("topLeft", tl);
    corners.Add("topRight", tr);
    corners.Add("bottomRight", br);
    corners.Add("bottomLeft", bl);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM